processor_driver.c 8.1 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * processor_driver.c - ACPI Processor Driver
L
Linus Torvalds 已提交
3 4 5 6 7 8
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
 *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
 *  			- Added processor hotplug support
9 10
 *  Copyright (C) 2013, Intel Corporation
 *                      Rafael J. Wysocki <rafael.j.wysocki@intel.com>
L
Linus Torvalds 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/cpu.h>
36
#include <linux/cpuidle.h>
37
#include <linux/slab.h>
38
#include <linux/acpi.h>
39

L
Linus Torvalds 已提交
40 41
#include <acpi/processor.h>

42 43
#include "internal.h"

44 45
#define PREFIX "ACPI: "

L
Linus Torvalds 已提交
46 47
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
#define ACPI_PROCESSOR_NOTIFY_POWER	0x81
48
#define ACPI_PROCESSOR_NOTIFY_THROTTLING	0x82
L
Linus Torvalds 已提交
49 50

#define _COMPONENT		ACPI_PROCESSOR_COMPONENT
51
ACPI_MODULE_NAME("processor_driver");
L
Linus Torvalds 已提交
52

53
MODULE_AUTHOR("Paul Diefenbaugh");
54
MODULE_DESCRIPTION("ACPI Processor Driver");
L
Linus Torvalds 已提交
55 56
MODULE_LICENSE("GPL");

57 58
static int acpi_processor_start(struct device *dev);
static int acpi_processor_stop(struct device *dev);
L
Linus Torvalds 已提交
59

60
static const struct acpi_device_id processor_device_ids[] = {
61
	{ACPI_PROCESSOR_OBJECT_HID, 0},
62
	{ACPI_PROCESSOR_DEVICE_HID, 0},
63 64 65 66
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, processor_device_ids);

67
static struct device_driver acpi_processor_driver = {
L
Len Brown 已提交
68
	.name = "processor",
69 70 71 72
	.bus = &cpu_subsys,
	.acpi_match_table = processor_device_ids,
	.probe = acpi_processor_start,
	.remove = acpi_processor_stop,
L
Linus Torvalds 已提交
73 74
};

75
static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
76
{
77
	struct acpi_device *device = data;
78
	struct acpi_processor *pr;
79
	int saved;
L
Linus Torvalds 已提交
80

81 82 83 84
	if (device->handle != handle)
		return;

	pr = acpi_driver_data(device);
L
Linus Torvalds 已提交
85
	if (!pr)
86
		return;
L
Linus Torvalds 已提交
87 88 89

	switch (event) {
	case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
90
		saved = pr->performance_platform_limit;
91
		acpi_processor_ppc_has_changed(pr, 1);
92 93
		if (saved == pr->performance_platform_limit)
			break;
94
		acpi_bus_generate_netlink_event(device->pnp.device_class,
95
						  dev_name(&device->dev), event,
96
						  pr->performance_platform_limit);
L
Linus Torvalds 已提交
97 98 99
		break;
	case ACPI_PROCESSOR_NOTIFY_POWER:
		acpi_processor_cst_has_changed(pr);
100
		acpi_bus_generate_netlink_event(device->pnp.device_class,
101
						  dev_name(&device->dev), event, 0);
L
Linus Torvalds 已提交
102
		break;
103 104
	case ACPI_PROCESSOR_NOTIFY_THROTTLING:
		acpi_processor_tstate_has_changed(pr);
105
		acpi_bus_generate_netlink_event(device->pnp.device_class,
106
						  dev_name(&device->dev), event, 0);
A
Alan Cox 已提交
107
		break;
L
Linus Torvalds 已提交
108 109
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
110
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
111 112 113
		break;
	}

114
	return;
L
Linus Torvalds 已提交
115 116
}

117 118 119 120
static __cpuinit int __acpi_processor_start(struct acpi_device *device);

static int __cpuinit acpi_cpu_soft_notify(struct notifier_block *nfb,
					  unsigned long action, void *hcpu)
121 122
{
	unsigned int cpu = (unsigned long)hcpu;
123
	struct acpi_processor *pr = per_cpu(processors, cpu);
124
	struct acpi_device *device;
125

126 127 128 129 130 131 132
	if (!pr || acpi_bus_get_device(pr->handle, &device))
		return NOTIFY_DONE;

	if (action == CPU_ONLINE) {
		/*
		 * CPU got physically hotplugged and onlined for the first time:
		 * Initialize missing things.
133 134
		 */
		if (pr->flags.need_hotplug_init) {
135 136
			int ret;

137 138
			pr_info("Will online and init hotplugged CPU: %d\n",
				pr->id);
139
			pr->flags.need_hotplug_init = 0;
140 141
			ret = __acpi_processor_start(device);
			WARN(ret, "Failed to start CPU: %d\n", pr->id);
142
		} else {
143
			/* Normal CPU soft online event. */
144
			acpi_processor_ppc_has_changed(pr, 0);
145
			acpi_processor_hotplug(pr);
146 147 148
			acpi_processor_reevaluate_tstate(pr, action);
			acpi_processor_tstate_has_changed(pr);
		}
149 150
	} else if (action == CPU_DEAD) {
		/* Invalidate flag.throttling after the CPU is offline. */
151 152
		acpi_processor_reevaluate_tstate(pr, action);
	}
153 154 155
	return NOTIFY_OK;
}

156
static struct notifier_block __refdata acpi_cpu_notifier =
157 158 159 160
{
	    .notifier_call = acpi_cpu_soft_notify,
};

161
static __cpuinit int __acpi_processor_start(struct acpi_device *device)
162
{
163 164
	struct acpi_processor *pr = acpi_driver_data(device);
	acpi_status status;
165 166
	int result = 0;

167 168 169 170 171 172
	if (!pr)
		return -ENODEV;

	if (pr->flags.need_hotplug_init)
		return 0;

173 174
#ifdef CONFIG_CPU_FREQ
	acpi_processor_ppc_has_changed(pr, 0);
175
	acpi_processor_load_module(pr);
176 177 178 179 180
#endif
	acpi_processor_get_throttling_info(pr);
	acpi_processor_get_limit_info(pr);

	if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
181
		acpi_processor_power_init(pr);
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

	pr->cdev = thermal_cooling_device_register("Processor", device,
						   &processor_cooling_ops);
	if (IS_ERR(pr->cdev)) {
		result = PTR_ERR(pr->cdev);
		goto err_power_exit;
	}

	dev_dbg(&device->dev, "registered as cooling_device%d\n",
		pr->cdev->id);

	result = sysfs_create_link(&device->dev.kobj,
				   &pr->cdev->device.kobj,
				   "thermal_cooling");
	if (result) {
197 198
		dev_err(&device->dev,
			"Failed to create sysfs link 'thermal_cooling'\n");
199 200 201 202 203 204
		goto err_thermal_unregister;
	}
	result = sysfs_create_link(&pr->cdev->device.kobj,
				   &device->dev.kobj,
				   "device");
	if (result) {
205 206
		dev_err(&pr->cdev->device,
			"Failed to create sysfs link 'device'\n");
207 208 209
		goto err_remove_sysfs_thermal;
	}

210 211 212 213
	status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
					     acpi_processor_notify, device);
	if (ACPI_SUCCESS(status))
		return 0;
214

215 216
	sysfs_remove_link(&pr->cdev->device.kobj, "device");
 err_remove_sysfs_thermal:
217
	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
218
 err_thermal_unregister:
219
	thermal_cooling_device_unregister(pr->cdev);
220
 err_power_exit:
221
	acpi_processor_power_exit(pr);
222 223 224
	return result;
}

225
static int __cpuinit acpi_processor_start(struct device *dev)
L
Linus Torvalds 已提交
226
{
227
	struct acpi_device *device;
L
Linus Torvalds 已提交
228

229 230
	if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
		return -ENODEV;
L
Linus Torvalds 已提交
231

232
	return __acpi_processor_start(device);
L
Linus Torvalds 已提交
233 234
}

235
static int acpi_processor_stop(struct device *dev)
L
Linus Torvalds 已提交
236
{
237 238
	struct acpi_device *device;
	struct acpi_processor *pr;
L
Linus Torvalds 已提交
239

240 241
	if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
		return 0;
L
Linus Torvalds 已提交
242

243 244
	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
				   acpi_processor_notify);
L
Linus Torvalds 已提交
245

246
	pr = acpi_driver_data(device);
247 248
	if (!pr)
		return 0;
L
Linus Torvalds 已提交
249

250
	acpi_processor_power_exit(pr);
L
Linus Torvalds 已提交
251

252 253 254 255 256 257
	if (pr->cdev) {
		sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
		sysfs_remove_link(&pr->cdev->device.kobj, "device");
		thermal_cooling_device_unregister(pr->cdev);
		pr->cdev = NULL;
	}
258
	return 0;
L
Linus Torvalds 已提交
259 260 261 262 263 264 265 266
}

/*
 * We keep the driver loaded even when ACPI is not running.
 * This is needed for the powernow-k8 driver, that works even without
 * ACPI, but needs symbols from this driver
 */

267
static int __init acpi_processor_driver_init(void)
L
Linus Torvalds 已提交
268
{
L
Len Brown 已提交
269
	int result = 0;
L
Linus Torvalds 已提交
270

271 272 273
	if (acpi_disabled)
		return 0;

274
	result = driver_register(&acpi_processor_driver);
275
	if (result < 0)
276
		return result;
L
Linus Torvalds 已提交
277

278
	acpi_processor_syscore_init();
279
	register_hotcpu_notifier(&acpi_cpu_notifier);
L
Linus Torvalds 已提交
280 281
	acpi_thermal_cpufreq_init();
	acpi_processor_ppc_init();
282
	acpi_processor_throttling_init();
283
	return 0;
L
Linus Torvalds 已提交
284 285
}

286
static void __exit acpi_processor_driver_exit(void)
L
Linus Torvalds 已提交
287
{
288 289 290
	if (acpi_disabled)
		return;

L
Linus Torvalds 已提交
291 292
	acpi_processor_ppc_exit();
	acpi_thermal_cpufreq_exit();
293
	unregister_hotcpu_notifier(&acpi_cpu_notifier);
294
	acpi_processor_syscore_exit();
295
	driver_unregister(&acpi_processor_driver);
L
Linus Torvalds 已提交
296 297
}

298 299
module_init(acpi_processor_driver_init);
module_exit(acpi_processor_driver_exit);
L
Linus Torvalds 已提交
300 301

MODULE_ALIAS("processor");