processor_driver.c 8.2 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_proc_event(device, event,
L
Len Brown 已提交
95
					pr->performance_platform_limit);
96
		acpi_bus_generate_netlink_event(device->pnp.device_class,
97
						  dev_name(&device->dev), event,
98
						  pr->performance_platform_limit);
L
Linus Torvalds 已提交
99 100 101
		break;
	case ACPI_PROCESSOR_NOTIFY_POWER:
		acpi_processor_cst_has_changed(pr);
102
		acpi_bus_generate_proc_event(device, event, 0);
103
		acpi_bus_generate_netlink_event(device->pnp.device_class,
104
						  dev_name(&device->dev), event, 0);
L
Linus Torvalds 已提交
105
		break;
106 107
	case ACPI_PROCESSOR_NOTIFY_THROTTLING:
		acpi_processor_tstate_has_changed(pr);
108
		acpi_bus_generate_proc_event(device, event, 0);
109
		acpi_bus_generate_netlink_event(device->pnp.device_class,
110
						  dev_name(&device->dev), event, 0);
A
Alan Cox 已提交
111
		break;
L
Linus Torvalds 已提交
112 113
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
114
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
115 116 117
		break;
	}

118
	return;
L
Linus Torvalds 已提交
119 120
}

121
static int __acpi_processor_start(struct acpi_device *device);
122

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

130 131 132 133 134 135 136
	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.
137 138
		 */
		if (pr->flags.need_hotplug_init) {
139 140
			int ret;

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

160
static struct notifier_block __refdata acpi_cpu_notifier =
161 162 163 164
{
	    .notifier_call = acpi_cpu_soft_notify,
};

165
static int __acpi_processor_start(struct acpi_device *device)
166
{
167 168
	struct acpi_processor *pr = acpi_driver_data(device);
	acpi_status status;
169 170
	int result = 0;

171 172 173 174 175 176
	if (!pr)
		return -ENODEV;

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

177 178
#ifdef CONFIG_CPU_FREQ
	acpi_processor_ppc_has_changed(pr, 0);
179
	acpi_processor_load_module(pr);
180 181 182 183 184
#endif
	acpi_processor_get_throttling_info(pr);
	acpi_processor_get_limit_info(pr);

	if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
185
		acpi_processor_power_init(pr);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

	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) {
201 202
		dev_err(&device->dev,
			"Failed to create sysfs link 'thermal_cooling'\n");
203 204 205 206 207 208
		goto err_thermal_unregister;
	}
	result = sysfs_create_link(&pr->cdev->device.kobj,
				   &device->dev.kobj,
				   "device");
	if (result) {
209 210
		dev_err(&pr->cdev->device,
			"Failed to create sysfs link 'device'\n");
211 212 213
		goto err_remove_sysfs_thermal;
	}

214 215 216 217
	status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
					     acpi_processor_notify, device);
	if (ACPI_SUCCESS(status))
		return 0;
218

219 220
	sysfs_remove_link(&pr->cdev->device.kobj, "device");
 err_remove_sysfs_thermal:
221
	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
222
 err_thermal_unregister:
223
	thermal_cooling_device_unregister(pr->cdev);
224
 err_power_exit:
225
	acpi_processor_power_exit(pr);
226 227 228
	return result;
}

229
static int acpi_processor_start(struct device *dev)
L
Linus Torvalds 已提交
230
{
231
	struct acpi_device *device;
L
Linus Torvalds 已提交
232

233 234
	if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
		return -ENODEV;
L
Linus Torvalds 已提交
235

236
	return __acpi_processor_start(device);
L
Linus Torvalds 已提交
237 238
}

239
static int acpi_processor_stop(struct device *dev)
L
Linus Torvalds 已提交
240
{
241 242
	struct acpi_device *device;
	struct acpi_processor *pr;
L
Linus Torvalds 已提交
243

244 245
	if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
		return 0;
L
Linus Torvalds 已提交
246

247 248
	acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
				   acpi_processor_notify);
L
Linus Torvalds 已提交
249

250
	pr = acpi_driver_data(device);
251 252
	if (!pr)
		return 0;
L
Linus Torvalds 已提交
253

254
	acpi_processor_power_exit(pr);
L
Linus Torvalds 已提交
255

256 257 258 259 260 261
	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;
	}
262
	return 0;
L
Linus Torvalds 已提交
263 264 265 266 267 268 269 270
}

/*
 * 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
 */

271
static int __init acpi_processor_driver_init(void)
L
Linus Torvalds 已提交
272
{
L
Len Brown 已提交
273
	int result = 0;
L
Linus Torvalds 已提交
274

275 276 277
	if (acpi_disabled)
		return 0;

278
	result = driver_register(&acpi_processor_driver);
279
	if (result < 0)
280
		return result;
L
Linus Torvalds 已提交
281

282
	acpi_processor_syscore_init();
283
	register_hotcpu_notifier(&acpi_cpu_notifier);
L
Linus Torvalds 已提交
284 285
	acpi_thermal_cpufreq_init();
	acpi_processor_ppc_init();
286
	acpi_processor_throttling_init();
287
	return 0;
L
Linus Torvalds 已提交
288 289
}

290
static void __exit acpi_processor_driver_exit(void)
L
Linus Torvalds 已提交
291
{
292 293 294
	if (acpi_disabled)
		return;

L
Linus Torvalds 已提交
295 296
	acpi_processor_ppc_exit();
	acpi_thermal_cpufreq_exit();
297
	unregister_hotcpu_notifier(&acpi_cpu_notifier);
298
	acpi_processor_syscore_exit();
299
	driver_unregister(&acpi_processor_driver);
L
Linus Torvalds 已提交
300 301
}

302 303
module_init(acpi_processor_driver_init);
module_exit(acpi_processor_driver_exit);
L
Linus Torvalds 已提交
304 305

MODULE_ALIAS("processor");