| /* |
| * The idle loop for all SuperH platforms. |
| * |
| * Copyright (C) 2002 - 2009 Paul Mundt |
| * |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| */ |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/mm.h> |
| #include <linux/pm.h> |
| #include <linux/tick.h> |
| #include <linux/preempt.h> |
| #include <linux/thread_info.h> |
| #include <linux/irqflags.h> |
| #include <linux/smp.h> |
| #include <asm/pgalloc.h> |
| #include <asm/system.h> |
| #include <asm/atomic.h> |
| |
| static int hlt_counter; |
| void (*pm_idle)(void); |
| void (*pm_power_off)(void); |
| EXPORT_SYMBOL(pm_power_off); |
| |
| static int __init nohlt_setup(char *__unused) |
| { |
| hlt_counter = 1; |
| return 1; |
| } |
| __setup("nohlt", nohlt_setup); |
| |
| static int __init hlt_setup(char *__unused) |
| { |
| hlt_counter = 0; |
| return 1; |
| } |
| __setup("hlt", hlt_setup); |
| |
| void default_idle(void) |
| { |
| if (!hlt_counter) { |
| clear_thread_flag(TIF_POLLING_NRFLAG); |
| smp_mb__after_clear_bit(); |
| set_bl_bit(); |
| stop_critical_timings(); |
| |
| while (!need_resched()) |
| cpu_sleep(); |
| |
| start_critical_timings(); |
| clear_bl_bit(); |
| set_thread_flag(TIF_POLLING_NRFLAG); |
| } else |
| while (!need_resched()) |
| cpu_relax(); |
| } |
| |
| void cpu_idle(void) |
| { |
| set_thread_flag(TIF_POLLING_NRFLAG); |
| |
| /* endless idle loop with no priority at all */ |
| while (1) { |
| void (*idle)(void) = pm_idle; |
| |
| if (!idle) |
| idle = default_idle; |
| |
| tick_nohz_stop_sched_tick(1); |
| while (!need_resched()) |
| idle(); |
| tick_nohz_restart_sched_tick(); |
| |
| preempt_enable_no_resched(); |
| schedule(); |
| preempt_disable(); |
| check_pgt_cache(); |
| } |
| } |
| |
| static void do_nothing(void *unused) |
| { |
| } |
| |
| /* |
| * cpu_idle_wait - Used to ensure that all the CPUs discard old value of |
| * pm_idle and update to new pm_idle value. Required while changing pm_idle |
| * handler on SMP systems. |
| * |
| * Caller must have changed pm_idle to the new value before the call. Old |
| * pm_idle value will not be used by any CPU after the return of this function. |
| */ |
| void cpu_idle_wait(void) |
| { |
| smp_mb(); |
| /* kick all the CPUs so that they exit out of pm_idle */ |
| smp_call_function(do_nothing, NULL, 1); |
| } |
| EXPORT_SYMBOL_GPL(cpu_idle_wait); |