xref: /linux/drivers/cpuidle/cpuidle.c (revision 4202735e8ab6ecfb0381631a0d0b58fefe0bd4e2)
14f86d3a8SLen Brown /*
24f86d3a8SLen Brown  * cpuidle.c - core cpuidle infrastructure
34f86d3a8SLen Brown  *
44f86d3a8SLen Brown  * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
54f86d3a8SLen Brown  *               Shaohua Li <shaohua.li@intel.com>
64f86d3a8SLen Brown  *               Adam Belay <abelay@novell.com>
74f86d3a8SLen Brown  *
84f86d3a8SLen Brown  * This code is licenced under the GPL.
94f86d3a8SLen Brown  */
104f86d3a8SLen Brown 
114f86d3a8SLen Brown #include <linux/kernel.h>
124f86d3a8SLen Brown #include <linux/mutex.h>
134f86d3a8SLen Brown #include <linux/sched.h>
144f86d3a8SLen Brown #include <linux/notifier.h>
15d82b3518SMark Gross #include <linux/pm_qos_params.h>
164f86d3a8SLen Brown #include <linux/cpu.h>
174f86d3a8SLen Brown #include <linux/cpuidle.h>
189a0b8415Svenkatesh.pallipadi@intel.com #include <linux/ktime.h>
192e94d1f7SArjan van de Ven #include <linux/hrtimer.h>
20288f023eSArjan van de Ven #include <trace/events/power.h>
214f86d3a8SLen Brown 
224f86d3a8SLen Brown #include "cpuidle.h"
234f86d3a8SLen Brown 
244f86d3a8SLen Brown DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
254f86d3a8SLen Brown 
264f86d3a8SLen Brown DEFINE_MUTEX(cpuidle_lock);
274f86d3a8SLen Brown LIST_HEAD(cpuidle_detected_devices);
284f86d3a8SLen Brown 
294f86d3a8SLen Brown static int enabled_devices;
3062027aeaSLen Brown static int off __read_mostly;
31a0bfa137SLen Brown static int initialized __read_mostly;
3262027aeaSLen Brown 
3362027aeaSLen Brown int cpuidle_disabled(void)
3462027aeaSLen Brown {
3562027aeaSLen Brown 	return off;
3662027aeaSLen Brown }
37d91ee586SLen Brown void disable_cpuidle(void)
38d91ee586SLen Brown {
39d91ee586SLen Brown 	off = 1;
40d91ee586SLen Brown }
414f86d3a8SLen Brown 
42a6869cc4SVenki Pallipadi #if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT)
43a6869cc4SVenki Pallipadi static void cpuidle_kick_cpus(void)
44a6869cc4SVenki Pallipadi {
45a6869cc4SVenki Pallipadi 	cpu_idle_wait();
46a6869cc4SVenki Pallipadi }
47a6869cc4SVenki Pallipadi #elif defined(CONFIG_SMP)
48a6869cc4SVenki Pallipadi # error "Arch needs cpu_idle_wait() equivalent here"
49a6869cc4SVenki Pallipadi #else /* !CONFIG_ARCH_HAS_CPU_IDLE_WAIT && !CONFIG_SMP */
50a6869cc4SVenki Pallipadi static void cpuidle_kick_cpus(void) {}
51a6869cc4SVenki Pallipadi #endif
52a6869cc4SVenki Pallipadi 
53dcb84f33SVenkatesh Pallipadi static int __cpuidle_register_device(struct cpuidle_device *dev);
54dcb84f33SVenkatesh Pallipadi 
554f86d3a8SLen Brown /**
564f86d3a8SLen Brown  * cpuidle_idle_call - the main idle loop
574f86d3a8SLen Brown  *
584f86d3a8SLen Brown  * NOTE: no locks or semaphores should be used here
59a0bfa137SLen Brown  * return non-zero on failure
604f86d3a8SLen Brown  */
61a0bfa137SLen Brown int cpuidle_idle_call(void)
624f86d3a8SLen Brown {
634a6f4fe8SChristoph Lameter 	struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
644f86d3a8SLen Brown 	struct cpuidle_state *target_state;
65e978aa7dSDeepthi Dharwar 	int next_state, entered_state;
664f86d3a8SLen Brown 
67a0bfa137SLen Brown 	if (off)
68a0bfa137SLen Brown 		return -ENODEV;
69a0bfa137SLen Brown 
70a0bfa137SLen Brown 	if (!initialized)
71a0bfa137SLen Brown 		return -ENODEV;
72a0bfa137SLen Brown 
734f86d3a8SLen Brown 	/* check if the device is ready */
74a0bfa137SLen Brown 	if (!dev || !dev->enabled)
75a0bfa137SLen Brown 		return -EBUSY;
764f86d3a8SLen Brown 
779a655837SArjan van de Ven #if 0
789a655837SArjan van de Ven 	/* shows regressions, re-enable for 2.6.29 */
792e94d1f7SArjan van de Ven 	/*
802e94d1f7SArjan van de Ven 	 * run any timers that can be run now, at this point
812e94d1f7SArjan van de Ven 	 * before calculating the idle duration etc.
822e94d1f7SArjan van de Ven 	 */
832e94d1f7SArjan van de Ven 	hrtimer_peek_ahead_timers();
849a655837SArjan van de Ven #endif
8571abbbf8SAi Li 
864f86d3a8SLen Brown 	/* ask the governor for the next state */
874f86d3a8SLen Brown 	next_state = cpuidle_curr_governor->select(dev);
88246eb7f0SKevin Hilman 	if (need_resched()) {
89246eb7f0SKevin Hilman 		local_irq_enable();
90a0bfa137SLen Brown 		return 0;
91246eb7f0SKevin Hilman 	}
92246eb7f0SKevin Hilman 
934f86d3a8SLen Brown 	target_state = &dev->states[next_state];
944f86d3a8SLen Brown 
95f77cfe4eSThomas Renninger 	trace_power_start(POWER_CSTATE, next_state, dev->cpu);
96f77cfe4eSThomas Renninger 	trace_cpu_idle(next_state, dev->cpu);
97f77cfe4eSThomas Renninger 
98e978aa7dSDeepthi Dharwar 	entered_state = target_state->enter(dev, next_state);
99f77cfe4eSThomas Renninger 
100f77cfe4eSThomas Renninger 	trace_power_end(dev->cpu);
101f77cfe4eSThomas Renninger 	trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu);
102f77cfe4eSThomas Renninger 
103e978aa7dSDeepthi Dharwar 	if (entered_state >= 0) {
104e978aa7dSDeepthi Dharwar 		/* Update cpuidle counters */
105e978aa7dSDeepthi Dharwar 		/* This can be moved to within driver enter routine
106e978aa7dSDeepthi Dharwar 		 * but that results in multiple copies of same code.
107e978aa7dSDeepthi Dharwar 		 */
108*4202735eSDeepthi Dharwar 		dev->states_usage[entered_state].time +=
109e978aa7dSDeepthi Dharwar 				(unsigned long long)dev->last_residency;
110*4202735eSDeepthi Dharwar 		dev->states_usage[entered_state].usage++;
111e978aa7dSDeepthi Dharwar 	}
1124f86d3a8SLen Brown 
1134f86d3a8SLen Brown 	/* give the governor an opportunity to reflect on the outcome */
1144f86d3a8SLen Brown 	if (cpuidle_curr_governor->reflect)
115e978aa7dSDeepthi Dharwar 		cpuidle_curr_governor->reflect(dev, entered_state);
116a0bfa137SLen Brown 
117a0bfa137SLen Brown 	return 0;
1184f86d3a8SLen Brown }
1194f86d3a8SLen Brown 
1204f86d3a8SLen Brown /**
1214f86d3a8SLen Brown  * cpuidle_install_idle_handler - installs the cpuidle idle loop handler
1224f86d3a8SLen Brown  */
1234f86d3a8SLen Brown void cpuidle_install_idle_handler(void)
1244f86d3a8SLen Brown {
125a0bfa137SLen Brown 	if (enabled_devices) {
1264f86d3a8SLen Brown 		/* Make sure all changes finished before we switch to new idle */
1274f86d3a8SLen Brown 		smp_wmb();
128a0bfa137SLen Brown 		initialized = 1;
1294f86d3a8SLen Brown 	}
1304f86d3a8SLen Brown }
1314f86d3a8SLen Brown 
1324f86d3a8SLen Brown /**
1334f86d3a8SLen Brown  * cpuidle_uninstall_idle_handler - uninstalls the cpuidle idle loop handler
1344f86d3a8SLen Brown  */
1354f86d3a8SLen Brown void cpuidle_uninstall_idle_handler(void)
1364f86d3a8SLen Brown {
137a0bfa137SLen Brown 	if (enabled_devices) {
138a0bfa137SLen Brown 		initialized = 0;
139a6869cc4SVenki Pallipadi 		cpuidle_kick_cpus();
1404f86d3a8SLen Brown 	}
1414f86d3a8SLen Brown }
1424f86d3a8SLen Brown 
1434f86d3a8SLen Brown /**
1444f86d3a8SLen Brown  * cpuidle_pause_and_lock - temporarily disables CPUIDLE
1454f86d3a8SLen Brown  */
1464f86d3a8SLen Brown void cpuidle_pause_and_lock(void)
1474f86d3a8SLen Brown {
1484f86d3a8SLen Brown 	mutex_lock(&cpuidle_lock);
1494f86d3a8SLen Brown 	cpuidle_uninstall_idle_handler();
1504f86d3a8SLen Brown }
1514f86d3a8SLen Brown 
1524f86d3a8SLen Brown EXPORT_SYMBOL_GPL(cpuidle_pause_and_lock);
1534f86d3a8SLen Brown 
1544f86d3a8SLen Brown /**
1554f86d3a8SLen Brown  * cpuidle_resume_and_unlock - resumes CPUIDLE operation
1564f86d3a8SLen Brown  */
1574f86d3a8SLen Brown void cpuidle_resume_and_unlock(void)
1584f86d3a8SLen Brown {
1594f86d3a8SLen Brown 	cpuidle_install_idle_handler();
1604f86d3a8SLen Brown 	mutex_unlock(&cpuidle_lock);
1614f86d3a8SLen Brown }
1624f86d3a8SLen Brown 
1634f86d3a8SLen Brown EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
1644f86d3a8SLen Brown 
165d8c216cfSRafael J. Wysocki #ifdef CONFIG_ARCH_HAS_CPU_RELAX
166e978aa7dSDeepthi Dharwar static int poll_idle(struct cpuidle_device *dev, int index)
167d8c216cfSRafael J. Wysocki {
168d8c216cfSRafael J. Wysocki 	ktime_t	t1, t2;
169d8c216cfSRafael J. Wysocki 	s64 diff;
170d8c216cfSRafael J. Wysocki 
171d8c216cfSRafael J. Wysocki 	t1 = ktime_get();
172d8c216cfSRafael J. Wysocki 	local_irq_enable();
173d8c216cfSRafael J. Wysocki 	while (!need_resched())
174d8c216cfSRafael J. Wysocki 		cpu_relax();
175d8c216cfSRafael J. Wysocki 
176d8c216cfSRafael J. Wysocki 	t2 = ktime_get();
177d8c216cfSRafael J. Wysocki 	diff = ktime_to_us(ktime_sub(t2, t1));
178d8c216cfSRafael J. Wysocki 	if (diff > INT_MAX)
179d8c216cfSRafael J. Wysocki 		diff = INT_MAX;
180d8c216cfSRafael J. Wysocki 
181e978aa7dSDeepthi Dharwar 	dev->last_residency = (int) diff;
182e978aa7dSDeepthi Dharwar 
183e978aa7dSDeepthi Dharwar 	return index;
184d8c216cfSRafael J. Wysocki }
185d8c216cfSRafael J. Wysocki 
186d8c216cfSRafael J. Wysocki static void poll_idle_init(struct cpuidle_device *dev)
187d8c216cfSRafael J. Wysocki {
188d8c216cfSRafael J. Wysocki 	struct cpuidle_state *state = &dev->states[0];
189*4202735eSDeepthi Dharwar 	struct cpuidle_state_usage *state_usage = &dev->states_usage[0];
190d8c216cfSRafael J. Wysocki 
191*4202735eSDeepthi Dharwar 	cpuidle_set_statedata(state_usage, NULL);
192d8c216cfSRafael J. Wysocki 
193720f1c30SThomas Renninger 	snprintf(state->name, CPUIDLE_NAME_LEN, "POLL");
194d8c216cfSRafael J. Wysocki 	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
195d8c216cfSRafael J. Wysocki 	state->exit_latency = 0;
196d8c216cfSRafael J. Wysocki 	state->target_residency = 0;
197d8c216cfSRafael J. Wysocki 	state->power_usage = -1;
198d247632cSLen Brown 	state->flags = 0;
199d8c216cfSRafael J. Wysocki 	state->enter = poll_idle;
200d8c216cfSRafael J. Wysocki }
201d8c216cfSRafael J. Wysocki #else
202d8c216cfSRafael J. Wysocki static void poll_idle_init(struct cpuidle_device *dev) {}
203d8c216cfSRafael J. Wysocki #endif /* CONFIG_ARCH_HAS_CPU_RELAX */
204d8c216cfSRafael J. Wysocki 
2054f86d3a8SLen Brown /**
2064f86d3a8SLen Brown  * cpuidle_enable_device - enables idle PM for a CPU
2074f86d3a8SLen Brown  * @dev: the CPU
2084f86d3a8SLen Brown  *
2094f86d3a8SLen Brown  * This function must be called between cpuidle_pause_and_lock and
2104f86d3a8SLen Brown  * cpuidle_resume_and_unlock when used externally.
2114f86d3a8SLen Brown  */
2124f86d3a8SLen Brown int cpuidle_enable_device(struct cpuidle_device *dev)
2134f86d3a8SLen Brown {
2144f86d3a8SLen Brown 	int ret, i;
2154f86d3a8SLen Brown 
2164f86d3a8SLen Brown 	if (dev->enabled)
2174f86d3a8SLen Brown 		return 0;
218752138dfSLen Brown 	if (!cpuidle_get_driver() || !cpuidle_curr_governor)
2194f86d3a8SLen Brown 		return -EIO;
2204f86d3a8SLen Brown 	if (!dev->state_count)
2214f86d3a8SLen Brown 		return -EINVAL;
2224f86d3a8SLen Brown 
223dcb84f33SVenkatesh Pallipadi 	if (dev->registered == 0) {
224dcb84f33SVenkatesh Pallipadi 		ret = __cpuidle_register_device(dev);
225dcb84f33SVenkatesh Pallipadi 		if (ret)
226dcb84f33SVenkatesh Pallipadi 			return ret;
227dcb84f33SVenkatesh Pallipadi 	}
228dcb84f33SVenkatesh Pallipadi 
229d8c216cfSRafael J. Wysocki 	poll_idle_init(dev);
230d8c216cfSRafael J. Wysocki 
2314f86d3a8SLen Brown 	if ((ret = cpuidle_add_state_sysfs(dev)))
2324f86d3a8SLen Brown 		return ret;
2334f86d3a8SLen Brown 
2344f86d3a8SLen Brown 	if (cpuidle_curr_governor->enable &&
2354f86d3a8SLen Brown 	    (ret = cpuidle_curr_governor->enable(dev)))
2364f86d3a8SLen Brown 		goto fail_sysfs;
2374f86d3a8SLen Brown 
2384f86d3a8SLen Brown 	for (i = 0; i < dev->state_count; i++) {
239*4202735eSDeepthi Dharwar 		dev->states_usage[i].usage = 0;
240*4202735eSDeepthi Dharwar 		dev->states_usage[i].time = 0;
2414f86d3a8SLen Brown 	}
2424f86d3a8SLen Brown 	dev->last_residency = 0;
2434f86d3a8SLen Brown 
2444f86d3a8SLen Brown 	smp_wmb();
2454f86d3a8SLen Brown 
2464f86d3a8SLen Brown 	dev->enabled = 1;
2474f86d3a8SLen Brown 
2484f86d3a8SLen Brown 	enabled_devices++;
2494f86d3a8SLen Brown 	return 0;
2504f86d3a8SLen Brown 
2514f86d3a8SLen Brown fail_sysfs:
2524f86d3a8SLen Brown 	cpuidle_remove_state_sysfs(dev);
2534f86d3a8SLen Brown 
2544f86d3a8SLen Brown 	return ret;
2554f86d3a8SLen Brown }
2564f86d3a8SLen Brown 
2574f86d3a8SLen Brown EXPORT_SYMBOL_GPL(cpuidle_enable_device);
2584f86d3a8SLen Brown 
2594f86d3a8SLen Brown /**
2604f86d3a8SLen Brown  * cpuidle_disable_device - disables idle PM for a CPU
2614f86d3a8SLen Brown  * @dev: the CPU
2624f86d3a8SLen Brown  *
2634f86d3a8SLen Brown  * This function must be called between cpuidle_pause_and_lock and
2644f86d3a8SLen Brown  * cpuidle_resume_and_unlock when used externally.
2654f86d3a8SLen Brown  */
2664f86d3a8SLen Brown void cpuidle_disable_device(struct cpuidle_device *dev)
2674f86d3a8SLen Brown {
2684f86d3a8SLen Brown 	if (!dev->enabled)
2694f86d3a8SLen Brown 		return;
270752138dfSLen Brown 	if (!cpuidle_get_driver() || !cpuidle_curr_governor)
2714f86d3a8SLen Brown 		return;
2724f86d3a8SLen Brown 
2734f86d3a8SLen Brown 	dev->enabled = 0;
2744f86d3a8SLen Brown 
2754f86d3a8SLen Brown 	if (cpuidle_curr_governor->disable)
2764f86d3a8SLen Brown 		cpuidle_curr_governor->disable(dev);
2774f86d3a8SLen Brown 
2784f86d3a8SLen Brown 	cpuidle_remove_state_sysfs(dev);
2794f86d3a8SLen Brown 	enabled_devices--;
2804f86d3a8SLen Brown }
2814f86d3a8SLen Brown 
2824f86d3a8SLen Brown EXPORT_SYMBOL_GPL(cpuidle_disable_device);
2834f86d3a8SLen Brown 
2844f86d3a8SLen Brown /**
285dcb84f33SVenkatesh Pallipadi  * __cpuidle_register_device - internal register function called before register
286dcb84f33SVenkatesh Pallipadi  * and enable routines
2874f86d3a8SLen Brown  * @dev: the cpu
288dcb84f33SVenkatesh Pallipadi  *
289dcb84f33SVenkatesh Pallipadi  * cpuidle_lock mutex must be held before this is called
2904f86d3a8SLen Brown  */
291dcb84f33SVenkatesh Pallipadi static int __cpuidle_register_device(struct cpuidle_device *dev)
2924f86d3a8SLen Brown {
2934f86d3a8SLen Brown 	int ret;
2944f86d3a8SLen Brown 	struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
295752138dfSLen Brown 	struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
2964f86d3a8SLen Brown 
2974f86d3a8SLen Brown 	if (!sys_dev)
2984f86d3a8SLen Brown 		return -EINVAL;
299752138dfSLen Brown 	if (!try_module_get(cpuidle_driver->owner))
3004f86d3a8SLen Brown 		return -EINVAL;
3014f86d3a8SLen Brown 
3024f86d3a8SLen Brown 	init_completion(&dev->kobj_unregister);
3034f86d3a8SLen Brown 
30471abbbf8SAi Li 	/*
30571abbbf8SAi Li 	 * cpuidle driver should set the dev->power_specified bit
30671abbbf8SAi Li 	 * before registering the device if the driver provides
30771abbbf8SAi Li 	 * power_usage numbers.
30871abbbf8SAi Li 	 *
30971abbbf8SAi Li 	 * For those devices whose ->power_specified is not set,
31071abbbf8SAi Li 	 * we fill in power_usage with decreasing values as the
31171abbbf8SAi Li 	 * cpuidle code has an implicit assumption that state Cn
31271abbbf8SAi Li 	 * uses less power than C(n-1).
31371abbbf8SAi Li 	 *
31471abbbf8SAi Li 	 * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
31571abbbf8SAi Li 	 * an power value of -1.  So we use -2, -3, etc, for other
31671abbbf8SAi Li 	 * c-states.
31771abbbf8SAi Li 	 */
31871abbbf8SAi Li 	if (!dev->power_specified) {
31971abbbf8SAi Li 		int i;
32071abbbf8SAi Li 		for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++)
32171abbbf8SAi Li 			dev->states[i].power_usage = -1 - i;
32271abbbf8SAi Li 	}
32371abbbf8SAi Li 
3244f86d3a8SLen Brown 	per_cpu(cpuidle_devices, dev->cpu) = dev;
3254f86d3a8SLen Brown 	list_add(&dev->device_list, &cpuidle_detected_devices);
3264f86d3a8SLen Brown 	if ((ret = cpuidle_add_sysfs(sys_dev))) {
327752138dfSLen Brown 		module_put(cpuidle_driver->owner);
3284f86d3a8SLen Brown 		return ret;
3294f86d3a8SLen Brown 	}
3304f86d3a8SLen Brown 
331dcb84f33SVenkatesh Pallipadi 	dev->registered = 1;
332dcb84f33SVenkatesh Pallipadi 	return 0;
333dcb84f33SVenkatesh Pallipadi }
334dcb84f33SVenkatesh Pallipadi 
335dcb84f33SVenkatesh Pallipadi /**
336dcb84f33SVenkatesh Pallipadi  * cpuidle_register_device - registers a CPU's idle PM feature
337dcb84f33SVenkatesh Pallipadi  * @dev: the cpu
338dcb84f33SVenkatesh Pallipadi  */
339dcb84f33SVenkatesh Pallipadi int cpuidle_register_device(struct cpuidle_device *dev)
340dcb84f33SVenkatesh Pallipadi {
341dcb84f33SVenkatesh Pallipadi 	int ret;
342dcb84f33SVenkatesh Pallipadi 
343dcb84f33SVenkatesh Pallipadi 	mutex_lock(&cpuidle_lock);
344dcb84f33SVenkatesh Pallipadi 
345dcb84f33SVenkatesh Pallipadi 	if ((ret = __cpuidle_register_device(dev))) {
346dcb84f33SVenkatesh Pallipadi 		mutex_unlock(&cpuidle_lock);
347dcb84f33SVenkatesh Pallipadi 		return ret;
348dcb84f33SVenkatesh Pallipadi 	}
349dcb84f33SVenkatesh Pallipadi 
3504f86d3a8SLen Brown 	cpuidle_enable_device(dev);
3514f86d3a8SLen Brown 	cpuidle_install_idle_handler();
3524f86d3a8SLen Brown 
3534f86d3a8SLen Brown 	mutex_unlock(&cpuidle_lock);
3544f86d3a8SLen Brown 
3554f86d3a8SLen Brown 	return 0;
3564f86d3a8SLen Brown 
3574f86d3a8SLen Brown }
3584f86d3a8SLen Brown 
3594f86d3a8SLen Brown EXPORT_SYMBOL_GPL(cpuidle_register_device);
3604f86d3a8SLen Brown 
3614f86d3a8SLen Brown /**
3624f86d3a8SLen Brown  * cpuidle_unregister_device - unregisters a CPU's idle PM feature
3634f86d3a8SLen Brown  * @dev: the cpu
3644f86d3a8SLen Brown  */
3654f86d3a8SLen Brown void cpuidle_unregister_device(struct cpuidle_device *dev)
3664f86d3a8SLen Brown {
3674f86d3a8SLen Brown 	struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
368752138dfSLen Brown 	struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
3694f86d3a8SLen Brown 
370dcb84f33SVenkatesh Pallipadi 	if (dev->registered == 0)
371dcb84f33SVenkatesh Pallipadi 		return;
372dcb84f33SVenkatesh Pallipadi 
3734f86d3a8SLen Brown 	cpuidle_pause_and_lock();
3744f86d3a8SLen Brown 
3754f86d3a8SLen Brown 	cpuidle_disable_device(dev);
3764f86d3a8SLen Brown 
3774f86d3a8SLen Brown 	cpuidle_remove_sysfs(sys_dev);
3784f86d3a8SLen Brown 	list_del(&dev->device_list);
3794f86d3a8SLen Brown 	wait_for_completion(&dev->kobj_unregister);
3804f86d3a8SLen Brown 	per_cpu(cpuidle_devices, dev->cpu) = NULL;
3814f86d3a8SLen Brown 
3824f86d3a8SLen Brown 	cpuidle_resume_and_unlock();
3834f86d3a8SLen Brown 
384752138dfSLen Brown 	module_put(cpuidle_driver->owner);
3854f86d3a8SLen Brown }
3864f86d3a8SLen Brown 
3874f86d3a8SLen Brown EXPORT_SYMBOL_GPL(cpuidle_unregister_device);
3884f86d3a8SLen Brown 
3894f86d3a8SLen Brown #ifdef CONFIG_SMP
3904f86d3a8SLen Brown 
3914f86d3a8SLen Brown static void smp_callback(void *v)
3924f86d3a8SLen Brown {
3934f86d3a8SLen Brown 	/* we already woke the CPU up, nothing more to do */
3944f86d3a8SLen Brown }
3954f86d3a8SLen Brown 
3964f86d3a8SLen Brown /*
3974f86d3a8SLen Brown  * This function gets called when a part of the kernel has a new latency
3984f86d3a8SLen Brown  * requirement.  This means we need to get all processors out of their C-state,
3994f86d3a8SLen Brown  * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that
4004f86d3a8SLen Brown  * wakes them all right up.
4014f86d3a8SLen Brown  */
4024f86d3a8SLen Brown static int cpuidle_latency_notify(struct notifier_block *b,
4034f86d3a8SLen Brown 		unsigned long l, void *v)
4044f86d3a8SLen Brown {
4058691e5a8SJens Axboe 	smp_call_function(smp_callback, NULL, 1);
4064f86d3a8SLen Brown 	return NOTIFY_OK;
4074f86d3a8SLen Brown }
4084f86d3a8SLen Brown 
4094f86d3a8SLen Brown static struct notifier_block cpuidle_latency_notifier = {
4104f86d3a8SLen Brown 	.notifier_call = cpuidle_latency_notify,
4114f86d3a8SLen Brown };
4124f86d3a8SLen Brown 
413d82b3518SMark Gross static inline void latency_notifier_init(struct notifier_block *n)
414d82b3518SMark Gross {
415d82b3518SMark Gross 	pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, n);
416d82b3518SMark Gross }
4174f86d3a8SLen Brown 
4184f86d3a8SLen Brown #else /* CONFIG_SMP */
4194f86d3a8SLen Brown 
4204f86d3a8SLen Brown #define latency_notifier_init(x) do { } while (0)
4214f86d3a8SLen Brown 
4224f86d3a8SLen Brown #endif /* CONFIG_SMP */
4234f86d3a8SLen Brown 
4244f86d3a8SLen Brown /**
4254f86d3a8SLen Brown  * cpuidle_init - core initializer
4264f86d3a8SLen Brown  */
4274f86d3a8SLen Brown static int __init cpuidle_init(void)
4284f86d3a8SLen Brown {
4294f86d3a8SLen Brown 	int ret;
4304f86d3a8SLen Brown 
43162027aeaSLen Brown 	if (cpuidle_disabled())
43262027aeaSLen Brown 		return -ENODEV;
43362027aeaSLen Brown 
4344f86d3a8SLen Brown 	ret = cpuidle_add_class_sysfs(&cpu_sysdev_class);
4354f86d3a8SLen Brown 	if (ret)
4364f86d3a8SLen Brown 		return ret;
4374f86d3a8SLen Brown 
4384f86d3a8SLen Brown 	latency_notifier_init(&cpuidle_latency_notifier);
4394f86d3a8SLen Brown 
4404f86d3a8SLen Brown 	return 0;
4414f86d3a8SLen Brown }
4424f86d3a8SLen Brown 
44362027aeaSLen Brown module_param(off, int, 0444);
4444f86d3a8SLen Brown core_initcall(cpuidle_init);
445