diff options
author | Deepthi Dharwar <deepthi@linux.vnet.ibm.com> | 2014-01-14 05:56:02 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-01-29 01:02:22 -0500 |
commit | 962e7bd4976516c34fc9ef51d536aab801980767 (patch) | |
tree | b1ddb6a14ffd3d83567d16b2265a02ba16331658 /arch/powerpc | |
parent | d765ff23e3181413fb1bed090c8d702165448a84 (diff) |
powerpc/pseries/cpuidle: Move processor_idle.c to drivers/cpuidle.
Move the file from arch specific pseries/processor_idle.c
to drivers/cpuidle/cpuidle-pseries.c
Make the relevant Makefile and Kconfig changes.
Also, introduce Kconfig.powerpc in drivers/cpuidle
for all powerpc cpuidle drivers.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/processor.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Kconfig | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/processor_idle.c | 308 |
4 files changed, 1 insertions, 319 deletions
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 8ca20ac28dc2..c2c0f4478be3 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h | |||
@@ -451,7 +451,7 @@ enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF}; | |||
451 | extern int powersave_nap; /* set if nap mode can be used in idle loop */ | 451 | extern int powersave_nap; /* set if nap mode can be used in idle loop */ |
452 | extern void power7_nap(void); | 452 | extern void power7_nap(void); |
453 | 453 | ||
454 | #ifdef CONFIG_PSERIES_IDLE | 454 | #ifdef CONFIG_PSERIES_CPUIDLE |
455 | extern void update_smt_snooze_delay(int cpu, int residency); | 455 | extern void update_smt_snooze_delay(int cpu, int residency); |
456 | #else | 456 | #else |
457 | static inline void update_smt_snooze_delay(int cpu, int residency) {} | 457 | static inline void update_smt_snooze_delay(int cpu, int residency) {} |
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index e66643250fee..37300f6ee244 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig | |||
@@ -119,12 +119,3 @@ config DTL | |||
119 | which are accessible through a debugfs file. | 119 | which are accessible through a debugfs file. |
120 | 120 | ||
121 | Say N if you are unsure. | 121 | Say N if you are unsure. |
122 | |||
123 | config PSERIES_IDLE | ||
124 | bool "Cpuidle driver for pSeries platforms" | ||
125 | depends on CPU_IDLE | ||
126 | depends on PPC_PSERIES | ||
127 | default y | ||
128 | help | ||
129 | Select this option to enable processor idle state management | ||
130 | through cpuidle subsystem. | ||
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index fbccac9cd2dc..03480796af9a 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -21,7 +21,6 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o | |||
21 | obj-$(CONFIG_CMM) += cmm.o | 21 | obj-$(CONFIG_CMM) += cmm.o |
22 | obj-$(CONFIG_DTL) += dtl.o | 22 | obj-$(CONFIG_DTL) += dtl.o |
23 | obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o | 23 | obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o |
24 | obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o | ||
25 | obj-$(CONFIG_LPARCFG) += lparcfg.o | 24 | obj-$(CONFIG_LPARCFG) += lparcfg.o |
26 | 25 | ||
27 | ifeq ($(CONFIG_PPC_PSERIES),y) | 26 | ifeq ($(CONFIG_PPC_PSERIES),y) |
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c deleted file mode 100644 index 002d5b4112f2..000000000000 --- a/arch/powerpc/platforms/pseries/processor_idle.c +++ /dev/null | |||
@@ -1,308 +0,0 @@ | |||
1 | /* | ||
2 | * processor_idle - idle state cpuidle driver. | ||
3 | * Adapted from drivers/idle/intel_idle.c and | ||
4 | * drivers/acpi/processor_idle.c | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/moduleparam.h> | ||
12 | #include <linux/cpuidle.h> | ||
13 | #include <linux/cpu.h> | ||
14 | #include <linux/notifier.h> | ||
15 | |||
16 | #include <asm/paca.h> | ||
17 | #include <asm/reg.h> | ||
18 | #include <asm/machdep.h> | ||
19 | #include <asm/firmware.h> | ||
20 | #include <asm/plpar_wrappers.h> | ||
21 | |||
22 | struct cpuidle_driver pseries_idle_driver = { | ||
23 | .name = "pseries_idle", | ||
24 | .owner = THIS_MODULE, | ||
25 | }; | ||
26 | |||
27 | #define MAX_IDLE_STATE_COUNT 2 | ||
28 | |||
29 | static int max_idle_state = MAX_IDLE_STATE_COUNT - 1; | ||
30 | static struct cpuidle_state *cpuidle_state_table; | ||
31 | |||
32 | static inline void idle_loop_prolog(unsigned long *in_purr) | ||
33 | { | ||
34 | *in_purr = mfspr(SPRN_PURR); | ||
35 | /* | ||
36 | * Indicate to the HV that we are idle. Now would be | ||
37 | * a good time to find other work to dispatch. | ||
38 | */ | ||
39 | get_lppaca()->idle = 1; | ||
40 | } | ||
41 | |||
42 | static inline void idle_loop_epilog(unsigned long in_purr) | ||
43 | { | ||
44 | u64 wait_cycles; | ||
45 | |||
46 | wait_cycles = be64_to_cpu(get_lppaca()->wait_state_cycles); | ||
47 | wait_cycles += mfspr(SPRN_PURR) - in_purr; | ||
48 | get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles); | ||
49 | get_lppaca()->idle = 0; | ||
50 | } | ||
51 | |||
52 | static int snooze_loop(struct cpuidle_device *dev, | ||
53 | struct cpuidle_driver *drv, | ||
54 | int index) | ||
55 | { | ||
56 | unsigned long in_purr; | ||
57 | int cpu = dev->cpu; | ||
58 | |||
59 | idle_loop_prolog(&in_purr); | ||
60 | local_irq_enable(); | ||
61 | set_thread_flag(TIF_POLLING_NRFLAG); | ||
62 | |||
63 | while ((!need_resched()) && cpu_online(cpu)) { | ||
64 | HMT_low(); | ||
65 | HMT_very_low(); | ||
66 | } | ||
67 | |||
68 | HMT_medium(); | ||
69 | clear_thread_flag(TIF_POLLING_NRFLAG); | ||
70 | smp_mb(); | ||
71 | |||
72 | idle_loop_epilog(in_purr); | ||
73 | |||
74 | return index; | ||
75 | } | ||
76 | |||
77 | static void check_and_cede_processor(void) | ||
78 | { | ||
79 | /* | ||
80 | * Ensure our interrupt state is properly tracked, | ||
81 | * also checks if no interrupt has occurred while we | ||
82 | * were soft-disabled | ||
83 | */ | ||
84 | if (prep_irq_for_idle()) { | ||
85 | cede_processor(); | ||
86 | #ifdef CONFIG_TRACE_IRQFLAGS | ||
87 | /* Ensure that H_CEDE returns with IRQs on */ | ||
88 | if (WARN_ON(!(mfmsr() & MSR_EE))) | ||
89 | __hard_irq_enable(); | ||
90 | #endif | ||
91 | } | ||
92 | } | ||
93 | |||
94 | static int dedicated_cede_loop(struct cpuidle_device *dev, | ||
95 | struct cpuidle_driver *drv, | ||
96 | int index) | ||
97 | { | ||
98 | unsigned long in_purr; | ||
99 | |||
100 | idle_loop_prolog(&in_purr); | ||
101 | get_lppaca()->donate_dedicated_cpu = 1; | ||
102 | |||
103 | HMT_medium(); | ||
104 | check_and_cede_processor(); | ||
105 | |||
106 | get_lppaca()->donate_dedicated_cpu = 0; | ||
107 | |||
108 | idle_loop_epilog(in_purr); | ||
109 | |||
110 | return index; | ||
111 | } | ||
112 | |||
113 | static int shared_cede_loop(struct cpuidle_device *dev, | ||
114 | struct cpuidle_driver *drv, | ||
115 | int index) | ||
116 | { | ||
117 | unsigned long in_purr; | ||
118 | |||
119 | idle_loop_prolog(&in_purr); | ||
120 | |||
121 | /* | ||
122 | * Yield the processor to the hypervisor. We return if | ||
123 | * an external interrupt occurs (which are driven prior | ||
124 | * to returning here) or if a prod occurs from another | ||
125 | * processor. When returning here, external interrupts | ||
126 | * are enabled. | ||
127 | */ | ||
128 | check_and_cede_processor(); | ||
129 | |||
130 | idle_loop_epilog(in_purr); | ||
131 | |||
132 | return index; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * States for dedicated partition case. | ||
137 | */ | ||
138 | static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = { | ||
139 | { /* Snooze */ | ||
140 | .name = "snooze", | ||
141 | .desc = "snooze", | ||
142 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
143 | .exit_latency = 0, | ||
144 | .target_residency = 0, | ||
145 | .enter = &snooze_loop }, | ||
146 | { /* CEDE */ | ||
147 | .name = "CEDE", | ||
148 | .desc = "CEDE", | ||
149 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
150 | .exit_latency = 10, | ||
151 | .target_residency = 100, | ||
152 | .enter = &dedicated_cede_loop }, | ||
153 | }; | ||
154 | |||
155 | /* | ||
156 | * States for shared partition case. | ||
157 | */ | ||
158 | static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = { | ||
159 | { /* Shared Cede */ | ||
160 | .name = "Shared Cede", | ||
161 | .desc = "Shared Cede", | ||
162 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
163 | .exit_latency = 0, | ||
164 | .target_residency = 0, | ||
165 | .enter = &shared_cede_loop }, | ||
166 | }; | ||
167 | |||
168 | void update_smt_snooze_delay(int cpu, int residency) | ||
169 | { | ||
170 | struct cpuidle_driver *drv = cpuidle_get_driver(); | ||
171 | struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); | ||
172 | |||
173 | if (cpuidle_state_table != dedicated_states) | ||
174 | return; | ||
175 | |||
176 | if (residency < 0) { | ||
177 | /* Disable the Nap state on that cpu */ | ||
178 | if (dev) | ||
179 | dev->states_usage[1].disable = 1; | ||
180 | } else | ||
181 | if (drv) | ||
182 | drv->states[1].target_residency = residency; | ||
183 | } | ||
184 | |||
185 | static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n, | ||
186 | unsigned long action, void *hcpu) | ||
187 | { | ||
188 | int hotcpu = (unsigned long)hcpu; | ||
189 | struct cpuidle_device *dev = | ||
190 | per_cpu_ptr(cpuidle_devices, hotcpu); | ||
191 | |||
192 | if (dev && cpuidle_get_driver()) { | ||
193 | switch (action) { | ||
194 | case CPU_ONLINE: | ||
195 | case CPU_ONLINE_FROZEN: | ||
196 | cpuidle_pause_and_lock(); | ||
197 | cpuidle_enable_device(dev); | ||
198 | cpuidle_resume_and_unlock(); | ||
199 | break; | ||
200 | |||
201 | case CPU_DEAD: | ||
202 | case CPU_DEAD_FROZEN: | ||
203 | cpuidle_pause_and_lock(); | ||
204 | cpuidle_disable_device(dev); | ||
205 | cpuidle_resume_and_unlock(); | ||
206 | break; | ||
207 | |||
208 | default: | ||
209 | return NOTIFY_DONE; | ||
210 | } | ||
211 | } | ||
212 | return NOTIFY_OK; | ||
213 | } | ||
214 | |||
215 | static struct notifier_block setup_hotplug_notifier = { | ||
216 | .notifier_call = pseries_cpuidle_add_cpu_notifier, | ||
217 | }; | ||
218 | |||
219 | /* | ||
220 | * pseries_cpuidle_driver_init() | ||
221 | */ | ||
222 | static int pseries_cpuidle_driver_init(void) | ||
223 | { | ||
224 | int idle_state; | ||
225 | struct cpuidle_driver *drv = &pseries_idle_driver; | ||
226 | |||
227 | drv->state_count = 0; | ||
228 | |||
229 | for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) { | ||
230 | |||
231 | if (idle_state > max_idle_state) | ||
232 | break; | ||
233 | |||
234 | /* is the state not enabled? */ | ||
235 | if (cpuidle_state_table[idle_state].enter == NULL) | ||
236 | continue; | ||
237 | |||
238 | drv->states[drv->state_count] = /* structure copy */ | ||
239 | cpuidle_state_table[idle_state]; | ||
240 | |||
241 | drv->state_count += 1; | ||
242 | } | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * pseries_idle_probe() | ||
249 | * Choose state table for shared versus dedicated partition | ||
250 | */ | ||
251 | static int pseries_idle_probe(void) | ||
252 | { | ||
253 | |||
254 | if (!firmware_has_feature(FW_FEATURE_SPLPAR)) | ||
255 | return -ENODEV; | ||
256 | |||
257 | if (cpuidle_disable != IDLE_NO_OVERRIDE) | ||
258 | return -ENODEV; | ||
259 | |||
260 | if (max_idle_state == 0) { | ||
261 | printk(KERN_DEBUG "pseries processor idle disabled.\n"); | ||
262 | return -EPERM; | ||
263 | } | ||
264 | |||
265 | if (lppaca_shared_proc(get_lppaca())) | ||
266 | cpuidle_state_table = shared_states; | ||
267 | else | ||
268 | cpuidle_state_table = dedicated_states; | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int __init pseries_processor_idle_init(void) | ||
274 | { | ||
275 | int retval; | ||
276 | |||
277 | retval = pseries_idle_probe(); | ||
278 | if (retval) | ||
279 | return retval; | ||
280 | |||
281 | pseries_cpuidle_driver_init(); | ||
282 | retval = cpuidle_register(&pseries_idle_driver, NULL); | ||
283 | if (retval) { | ||
284 | printk(KERN_DEBUG "Registration of pseries driver failed.\n"); | ||
285 | return retval; | ||
286 | } | ||
287 | |||
288 | register_cpu_notifier(&setup_hotplug_notifier); | ||
289 | printk(KERN_DEBUG "pseries_idle_driver registered\n"); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static void __exit pseries_processor_idle_exit(void) | ||
295 | { | ||
296 | |||
297 | unregister_cpu_notifier(&setup_hotplug_notifier); | ||
298 | cpuidle_unregister(&pseries_idle_driver); | ||
299 | |||
300 | return; | ||
301 | } | ||
302 | |||
303 | module_init(pseries_processor_idle_init); | ||
304 | module_exit(pseries_processor_idle_exit); | ||
305 | |||
306 | MODULE_AUTHOR("Deepthi Dharwar <deepthi@linux.vnet.ibm.com>"); | ||
307 | MODULE_DESCRIPTION("Cpuidle driver for POWER"); | ||
308 | MODULE_LICENSE("GPL"); | ||