ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc64 / kernel / idle.c
1 /*
2  * Idle daemon for PowerPC.  Idle daemon will handle any action
3  * that needs to be taken when the system becomes idle.
4  *
5  * Originally Written by Cort Dougan (cort@cs.nmt.edu)
6  *
7  * iSeries supported added by Mike Corrigan <mikejc@us.ibm.com>
8  *
9  * Additional shared processor, SMT, and firmware support
10  *    Copyright (c) 2003 Dave Engebretsen <engebret@us.ibm.com>
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version
15  * 2 of the License, or (at your option) any later version.
16  */
17
18 #include <linux/config.h>
19 #include <linux/errno.h>
20 #include <linux/sched.h>
21 #include <linux/kernel.h>
22 #include <linux/mm.h>
23 #include <linux/smp.h>
24 #include <linux/smp_lock.h>
25 #include <linux/stddef.h>
26 #include <linux/unistd.h>
27 #include <linux/slab.h>
28 #include <linux/interrupt.h>
29 #include <linux/cpu.h>
30
31 #include <asm/pgtable.h>
32 #include <asm/uaccess.h>
33 #include <asm/system.h>
34 #include <asm/io.h>
35 #include <asm/processor.h>
36 #include <asm/mmu.h>
37 #include <asm/cache.h>
38 #include <asm/cputable.h>
39 #include <asm/time.h>
40 #include <asm/iSeries/LparData.h>
41 #include <asm/iSeries/HvCall.h>
42 #include <asm/iSeries/ItLpQueue.h>
43
44 extern long cede_processor(void);
45 extern long poll_pending(void);
46 extern void power4_idle(void);
47
48 int (*idle_loop)(void);
49
50 #ifdef CONFIG_PPC_ISERIES
51 unsigned long maxYieldTime = 0;
52 unsigned long minYieldTime = 0xffffffffffffffffUL;
53
54 static void yield_shared_processor(void)
55 {
56         unsigned long tb;
57         unsigned long yieldTime;
58
59         HvCall_setEnabledInterrupts(HvCall_MaskIPI |
60                                     HvCall_MaskLpEvent |
61                                     HvCall_MaskLpProd |
62                                     HvCall_MaskTimeout);
63
64         tb = get_tb();
65         /* Compute future tb value when yield should expire */
66         HvCall_yieldProcessor(HvCall_YieldTimed, tb+tb_ticks_per_jiffy);
67
68         yieldTime = get_tb() - tb;
69         if (yieldTime > maxYieldTime)
70                 maxYieldTime = yieldTime;
71
72         if (yieldTime < minYieldTime)
73                 minYieldTime = yieldTime;
74         
75         /*
76          * The decrementer stops during the yield.  Force a fake decrementer
77          * here and let the timer_interrupt code sort out the actual time.
78          */
79         get_paca()->xLpPaca.xIntDword.xFields.xDecrInt = 1;
80         process_iSeries_events();
81 }
82
83 int iSeries_idle(void)
84 {
85         struct paca_struct *lpaca;
86         long oldval;
87         unsigned long CTRL;
88
89         /* ensure iSeries run light will be out when idle */
90         clear_thread_flag(TIF_RUN_LIGHT);
91         CTRL = mfspr(CTRLF);
92         CTRL &= ~RUNLATCH;
93         mtspr(CTRLT, CTRL);
94 #if 0
95         init_idle();    
96 #endif
97
98         lpaca = get_paca();
99
100         for (;;) {
101                 if (lpaca->xLpPaca.xSharedProc) {
102                         if (ItLpQueue_isLpIntPending(lpaca->lpQueuePtr))
103                                 process_iSeries_events();
104                         if (!need_resched())
105                                 yield_shared_processor();
106                 } else {
107                         oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED);
108
109                         if (!oldval) {
110                                 set_thread_flag(TIF_POLLING_NRFLAG);
111
112                                 while (!need_resched()) {
113                                         HMT_medium();
114                                         if (ItLpQueue_isLpIntPending(lpaca->lpQueuePtr))
115                                                 process_iSeries_events();
116                                         HMT_low();
117                                 }
118
119                                 HMT_medium();
120                                 clear_thread_flag(TIF_POLLING_NRFLAG);
121                         } else {
122                                 set_need_resched();
123                         }
124                 }
125
126                 schedule();
127         }
128         return 0;
129 }
130 #endif
131
132 int default_idle(void)
133 {
134         long oldval;
135
136         while (1) {
137                 oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED);
138
139                 if (!oldval) {
140                         set_thread_flag(TIF_POLLING_NRFLAG);
141
142                         while (!need_resched()) {
143                                 barrier();
144                                 HMT_low();
145                         }
146
147                         HMT_medium();
148                         clear_thread_flag(TIF_POLLING_NRFLAG);
149                 } else {
150                         set_need_resched();
151                 }
152
153                 schedule();
154                 if (cpu_is_offline(smp_processor_id()) &&
155                                 system_state == SYSTEM_RUNNING)
156                         cpu_die();
157         }
158
159         return 0;
160 }
161
162 #ifdef CONFIG_PPC_PSERIES
163
164 DECLARE_PER_CPU(unsigned long, smt_snooze_delay);
165
166 int dedicated_idle(void)
167 {
168         long oldval;
169         struct paca_struct *lpaca = get_paca(), *ppaca;
170         unsigned long start_snooze;
171         unsigned long *smt_snooze_delay = &__get_cpu_var(smt_snooze_delay);
172
173         ppaca = &paca[smp_processor_id() ^ 1];
174
175         while (1) {
176                 /* Indicate to the HV that we are idle.  Now would be
177                  * a good time to find other work to dispatch. */
178                 lpaca->xLpPaca.xIdle = 1;
179
180                 oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED);
181                 if (!oldval) {
182                         set_thread_flag(TIF_POLLING_NRFLAG);
183                         start_snooze = __get_tb() +
184                                 *smt_snooze_delay * tb_ticks_per_usec;
185                         while (!need_resched()) {
186                                 /* need_resched could be 1 or 0 at this 
187                                  * point.  If it is 0, set it to 0, so
188                                  * an IPI/Prod is sent.  If it is 1, keep
189                                  * it that way & schedule work.
190                                  */
191                                 if (*smt_snooze_delay == 0 ||
192                                     __get_tb() < start_snooze) {
193                                         HMT_low(); /* Low thread priority */
194                                         continue;
195                                 }
196
197                                 HMT_very_low(); /* Low power mode */
198
199                                 /* If the SMT mode is system controlled & the 
200                                  * partner thread is doing work, switch into
201                                  * ST mode.
202                                  */
203                                 if((naca->smt_state == SMT_DYNAMIC) &&
204                                    (!(ppaca->xLpPaca.xIdle))) {
205                                         /* Indicate we are no longer polling for
206                                          * work, and then clear need_resched.  If
207                                          * need_resched was 1, set it back to 1
208                                          * and schedule work
209                                          */
210                                         clear_thread_flag(TIF_POLLING_NRFLAG);
211                                         oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED);
212                                         if(oldval == 1) {
213                                                 set_need_resched();
214                                                 break;
215                                         }
216
217                                         /* DRENG: Go HMT_medium here ? */
218                                         local_irq_disable(); 
219                                         lpaca->yielded = 1;
220
221                                         /* SMT dynamic mode.  Cede will result 
222                                          * in this thread going dormant, if the
223                                          * partner thread is still doing work.
224                                          * Thread wakes up if partner goes idle,
225                                          * an interrupt is presented, or a prod
226                                          * occurs.  Returning from the cede
227                                          * enables external interrupts.
228                                          */
229                                         cede_processor();
230
231                                         lpaca->yielded = 0;
232                                 } else {
233                                         /* Give the HV an opportunity at the
234                                          * processor, since we are not doing
235                                          * any work.
236                                          */
237                                         poll_pending();
238                                 }
239                         }
240                 } else {
241                         set_need_resched();
242                 }
243
244                 HMT_medium();
245                 lpaca->xLpPaca.xIdle = 0;
246                 schedule();
247                 if (cpu_is_offline(smp_processor_id()) &&
248                                 system_state == SYSTEM_RUNNING)
249                         cpu_die();
250         }
251         return 0;
252 }
253
254 int shared_idle(void)
255 {
256         struct paca_struct *lpaca = get_paca();
257
258         while (1) {
259                 if (cpu_is_offline(smp_processor_id()) &&
260                                 system_state == SYSTEM_RUNNING)
261                         cpu_die();
262
263                 /* Indicate to the HV that we are idle.  Now would be
264                  * a good time to find other work to dispatch. */
265                 lpaca->xLpPaca.xIdle = 1;
266
267                 if (!need_resched()) {
268                         local_irq_disable(); 
269                         lpaca->yielded = 1;
270                         
271                         /* 
272                          * Yield the processor to the hypervisor.  We return if
273                          * an external interrupt occurs (which are driven prior
274                          * to returning here) or if a prod occurs from another 
275                          * processor.  When returning here, external interrupts 
276                          * are enabled.
277                          */
278                         cede_processor();
279                         
280                         lpaca->yielded = 0;
281                 }
282
283                 HMT_medium();
284                 lpaca->xLpPaca.xIdle = 0;
285                 schedule();
286         }
287
288         return 0;
289 }
290 #endif
291
292 int cpu_idle(void)
293 {
294         idle_loop();
295         return 0; 
296 }
297
298 int native_idle(void)
299 {
300         while(1) {
301                 if (!need_resched())
302                         power4_idle();
303                 if (need_resched())
304                         schedule();
305         }
306         return 0;
307 }
308
309 int idle_setup(void)
310 {
311 #ifdef CONFIG_PPC_ISERIES
312         idle_loop = iSeries_idle;
313 #else
314         if (systemcfg->platform & PLATFORM_PSERIES) {
315                 if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) {
316                         if(get_paca()->xLpPaca.xSharedProc) {
317                                 printk("idle = shared_idle\n");
318                                 idle_loop = shared_idle;
319                         } else {
320                                 printk("idle = dedicated_idle\n");
321                                 idle_loop = dedicated_idle;
322                         }
323                 } else {
324                         printk("idle = default_idle\n");
325                         idle_loop = default_idle;
326                 }
327         } else if (systemcfg->platform == PLATFORM_POWERMAC) {
328                 printk("idle = native_idle\n");
329                 idle_loop = native_idle;
330         } else {
331                 printk("idle_setup: unknown platform, use default_idle\n");
332                 idle_loop = default_idle;
333         }
334 #endif
335
336         return 1;
337 }
338