more changes on original files
[linux-2.4.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 #include <linux/config.h>
18 #include <linux/init.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/ptrace.h>
28 #include <linux/slab.h>
29
30 #include <asm/pgtable.h>
31 #include <asm/uaccess.h>
32 #include <asm/system.h>
33 #include <asm/io.h>
34 #include <asm/processor.h>
35 #include <asm/mmu.h>
36 #include <asm/cache.h>
37 #include <asm/cputable.h>
38
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 int (*idle_loop)(void);
45
46 #ifdef CONFIG_PPC_ISERIES
47 static void yield_shared_processor(void)
48 {
49         struct paca_struct *lpaca = get_paca();
50
51         HvCall_setEnabledInterrupts( HvCall_MaskIPI |
52                                      HvCall_MaskLpEvent |
53                                      HvCall_MaskLpProd |
54                                      HvCall_MaskTimeout );
55
56         if ( ! ItLpQueue_isLpIntPending( paca->lpQueuePtr ) ) {
57                 /*
58                  * Compute future tb value when yield should expire.
59                  * We want to be woken up when the next decrementer is
60                  * to fire.
61                  */
62           
63                 __cli();
64                 lpaca->yielded = 1;        /* Indicate a prod is desired */
65                 lpaca->xLpPaca.xIdle = 1;  /* Inform the HV we are idle  */
66
67                 HvCall_yieldProcessor(HvCall_YieldTimed,
68                                       lpaca->next_jiffy_update_tb);
69
70                 lpaca->yielded = 0;        /* Back to IPI's */
71                 __sti();
72
73                 /*
74                  * The decrementer stops during the yield.  Force a fake
75                  * decrementer here and let the timer_interrupt code sort
76                  * out the actual time.
77            */
78           lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 1;
79         }
80           
81         process_iSeries_events();
82 }
83
84 int idle_iSeries(void)
85 {
86         struct paca_struct *lpaca;
87         long oldval;
88         unsigned long CTRL;
89
90         /* endless loop with no priority at all */
91         current->nice = 20;
92         current->counter = -100;
93
94         /* ensure iSeries run light will be out when idle */
95         current->thread.flags &= ~PPC_FLAG_RUN_LIGHT;
96         CTRL = mfspr(CTRLF);
97         CTRL &= ~RUNLATCH;
98         mtspr(CTRLT, CTRL);
99         init_idle();    
100
101         lpaca = get_paca();
102
103         for (;;) {
104                 if ( lpaca->xLpPaca.xSharedProc ) {
105                         if ( ItLpQueue_isLpIntPending( lpaca->lpQueuePtr ) )
106                                 process_iSeries_events();
107                         if ( !current->need_resched )
108                                 yield_shared_processor();
109                 } else {
110                         /* Avoid an IPI by setting need_resched */
111                         oldval = xchg(&current->need_resched, -1);
112                         if (!oldval) {
113                                 while(current->need_resched == -1) {
114                                         HMT_medium();
115                                         if ( ItLpQueue_isLpIntPending( lpaca->lpQueuePtr ) )
116                                                 process_iSeries_events();
117                                         HMT_low();
118                                 }
119                         }
120                 }
121                 HMT_medium();
122                 if (current->need_resched) {
123                         lpaca->xLpPaca.xIdle = 0;
124                         schedule();
125                         check_pgt_cache();
126                 }
127         }
128         return 0;
129 }
130 #endif
131
132 int idle_default(void)
133 {
134         long oldval;
135
136         current->nice = 20;
137         current->counter = -100;
138         init_idle();
139
140         for (;;) {
141                 /* Avoid an IPI by setting need_resched */
142                 oldval = xchg(&current->need_resched, -1);
143                 if (!oldval) {
144                         while(current->need_resched == -1) {
145                                         HMT_low();
146                         }
147                 }
148                 HMT_medium();
149                 if (current->need_resched) {
150                         schedule();
151                         check_pgt_cache();
152                 }
153         }
154         return 0;
155 }
156
157 int idle_dedicated(void)
158 {
159         long oldval;
160         struct paca_struct *lpaca = get_paca(), *ppaca;;
161         unsigned long start_snooze;
162
163         ppaca = &paca[(lpaca->xPacaIndex) ^ 1];
164         current->nice = 20;
165         current->counter = -100;
166         init_idle();
167
168         for (;;) {
169                 /* Indicate to the HV that we are idle.  Now would be
170                  * a good time to find other work to dispatch. */
171                 lpaca->xLpPaca.xIdle = 1;
172
173                 /* Avoid an IPI by setting need_resched */
174                 oldval = xchg(&current->need_resched, -1);
175                 if (!oldval) {
176                         start_snooze = __get_tb();
177                         while(current->need_resched == -1) {
178                                 if (__get_tb() <
179                                     (start_snooze +
180                                      naca->smt_snooze_delay*tb_ticks_per_usec)) {
181                                         HMT_low(); /* Low thread priority */
182                                         continue;
183                                 }
184
185                                 HMT_very_low(); /* Low power mode */
186
187                                 /* If the SMT mode is system controlled & the
188                                  * partner thread is doing work, switch into
189                                  * ST mode.
190                                  */
191                                 if((naca->smt_state == SMT_DYNAMIC) &&
192                                    (!(ppaca->xLpPaca.xIdle))) {
193                                         /* need_resched could be 1 or -1 at this
194                                          * point.  If it is -1, set it to 0, so
195                                          * an IPI/Prod is sent.  If it is 1, keep
196                                          * it that way & schedule work.
197                                          */
198                                         oldval = xchg(&current->need_resched, 0);
199                                         if(oldval == 1) {
200                                                 current->need_resched = oldval;
201                                                 break;
202                                         }
203
204                                         /* DRENG: Go HMT_medium here ? */
205                                         __cli();
206                                         lpaca->yielded = 1;
207
208                                         /* SMT dynamic mode.  Cede will result
209                                          * in this thread going dormant, if the
210                                          * partner thread is still doing work.
211                                          * Thread wakes up if partner goes idle,
212                                          * an interrupt is presented, or a prod
213                                          * occurs.  Returning from the cede
214                                          * enables external interrupts.
215                                          */
216                                         cede_processor();
217
218                                         lpaca->yielded = 0;
219                                 } else {
220                                         /* Give the HV an opportunity at the
221                                          * processor, since we are not doing
222                                          * any work.
223                                          */
224                                         poll_pending();
225                                 }
226                         }
227                 }
228                 HMT_medium();
229                 if (current->need_resched) {
230                         lpaca->xLpPaca.xIdle = 0;
231                         schedule();
232                         check_pgt_cache();
233                 }
234         }
235         return 0;
236 }
237
238 int idle_shared(void)
239 {
240         struct paca_struct *lpaca = get_paca();
241
242         /* endless loop with no priority at all */
243         current->nice = 20;
244         current->counter = -100;
245
246         init_idle();
247
248         for (;;) {
249                 /* Indicate to the HV that we are idle.  Now would be
250                  * a good time to find other work to dispatch. */
251                 lpaca->xLpPaca.xIdle = 1;
252
253                 if (!current->need_resched) {
254                         __cli();
255                         lpaca->yielded = 1;
256
257 /*
258                          * Yield the processor to the hypervisor.  We return if
259                          * an external interrupt occurs (which are driven prior
260                          * to returning here) or if a prod occurs from another
261                          * processor.  When returning here, external interrupts
262                          * are enabled.
263  */
264                         cede_processor();
265
266                         lpaca->yielded = 0;
267                 }
268
269                 HMT_medium();
270                 if (current->need_resched) {
271                         lpaca->xLpPaca.xIdle = 0;
272                         schedule();
273                         check_pgt_cache();
274                 }
275         }
276
277         return 0;
278 }
279
280 int cpu_idle(void)
281 {
282         idle_loop();
283         return 0; 
284 }
285
286 int idle_setup(void)
287 {
288 #ifdef CONFIG_PPC_ISERIES
289         idle_loop = idle_iSeries;
290 #else
291         if (systemcfg->platform & PLATFORM_PSERIES) {
292                 if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) {
293                         if(get_paca()->xLpPaca.xSharedProc) {
294                                 printk("idle = idle_shared\n");
295                                 idle_loop = idle_shared;
296                         } else {
297                                 printk("idle = idle_dedicated\n");
298                                 idle_loop = idle_dedicated;
299                         }
300                 } else {
301                         printk("idle = idle_default\n");
302                         idle_loop = idle_default;
303                 }
304         } else {
305                 printk("idle_setup: unknown platform, use idle_default\n");
306                 idle_loop = idle_default;
307         }
308 #endif
309
310         return 1;
311 }
312