2 * Idle daemon for PowerPC. Idle daemon will handle any action
3 * that needs to be taken when the system becomes idle.
5 * Originally Written by Cort Dougan (cort@cs.nmt.edu)
7 * iSeries supported added by Mike Corrigan <mikejc@us.ibm.com>
9 * Additional shared processor, SMT, and firmware support
10 * Copyright (c) 2003 Dave Engebretsen <engebret@us.ibm.com>
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.
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>
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>
30 #include <asm/pgtable.h>
31 #include <asm/uaccess.h>
32 #include <asm/system.h>
34 #include <asm/processor.h>
36 #include <asm/cache.h>
37 #include <asm/cputable.h>
40 #include <asm/iSeries/LparData.h>
41 #include <asm/iSeries/HvCall.h>
42 #include <asm/iSeries/ItLpQueue.h>
44 int (*idle_loop)(void);
46 #ifdef CONFIG_PPC_ISERIES
47 static void yield_shared_processor(void)
49 struct paca_struct *lpaca = get_paca();
51 HvCall_setEnabledInterrupts( HvCall_MaskIPI |
56 if ( ! ItLpQueue_isLpIntPending( paca->lpQueuePtr ) ) {
58 * Compute future tb value when yield should expire.
59 * We want to be woken up when the next decrementer is
64 lpaca->yielded = 1; /* Indicate a prod is desired */
65 lpaca->xLpPaca.xIdle = 1; /* Inform the HV we are idle */
67 HvCall_yieldProcessor(HvCall_YieldTimed,
68 lpaca->next_jiffy_update_tb);
70 lpaca->yielded = 0; /* Back to IPI's */
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.
78 lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 1;
81 process_iSeries_events();
84 int idle_iSeries(void)
86 struct paca_struct *lpaca;
90 /* endless loop with no priority at all */
92 current->counter = -100;
94 /* ensure iSeries run light will be out when idle */
95 current->thread.flags &= ~PPC_FLAG_RUN_LIGHT;
104 if ( lpaca->xLpPaca.xSharedProc ) {
105 if ( ItLpQueue_isLpIntPending( lpaca->lpQueuePtr ) )
106 process_iSeries_events();
107 if ( !current->need_resched )
108 yield_shared_processor();
110 /* Avoid an IPI by setting need_resched */
111 oldval = xchg(¤t->need_resched, -1);
113 while(current->need_resched == -1) {
115 if ( ItLpQueue_isLpIntPending( lpaca->lpQueuePtr ) )
116 process_iSeries_events();
122 if (current->need_resched) {
123 lpaca->xLpPaca.xIdle = 0;
132 int idle_default(void)
137 current->counter = -100;
141 /* Avoid an IPI by setting need_resched */
142 oldval = xchg(¤t->need_resched, -1);
144 while(current->need_resched == -1) {
149 if (current->need_resched) {
157 int idle_dedicated(void)
160 struct paca_struct *lpaca = get_paca(), *ppaca;;
161 unsigned long start_snooze;
163 ppaca = &paca[(lpaca->xPacaIndex) ^ 1];
165 current->counter = -100;
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;
173 /* Avoid an IPI by setting need_resched */
174 oldval = xchg(¤t->need_resched, -1);
176 start_snooze = __get_tb();
177 while(current->need_resched == -1) {
180 naca->smt_snooze_delay*tb_ticks_per_usec)) {
181 HMT_low(); /* Low thread priority */
185 HMT_very_low(); /* Low power mode */
187 /* If the SMT mode is system controlled & the
188 * partner thread is doing work, switch into
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.
198 oldval = xchg(¤t->need_resched, 0);
200 current->need_resched = oldval;
204 /* DRENG: Go HMT_medium here ? */
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.
220 /* Give the HV an opportunity at the
221 * processor, since we are not doing
229 if (current->need_resched) {
230 lpaca->xLpPaca.xIdle = 0;
238 int idle_shared(void)
240 struct paca_struct *lpaca = get_paca();
242 /* endless loop with no priority at all */
244 current->counter = -100;
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;
253 if (!current->need_resched) {
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
270 if (current->need_resched) {
271 lpaca->xLpPaca.xIdle = 0;
288 #ifdef CONFIG_PPC_ISERIES
289 idle_loop = idle_iSeries;
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;
297 printk("idle = idle_dedicated\n");
298 idle_loop = idle_dedicated;
301 printk("idle = idle_default\n");
302 idle_loop = idle_default;
305 printk("idle_setup: unknown platform, use idle_default\n");
306 idle_loop = idle_default;