| /* | |
| * 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. | |
| * | |
| * Copyright (C) 2001, 06 by Ralf Baechle (ralf@linux-mips.org) | |
| * Copyright (C) 2001 MIPS Technologies, Inc. | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/export.h> | |
| #include <linux/pm.h> | |
| #include <linux/types.h> | |
| #include <linux/reboot.h> | |
| #include <linux/delay.h> | |
| #include <asm/compiler.h> | |
| #include <asm/idle.h> | |
| #include <asm/mipsregs.h> | |
| #include <asm/reboot.h> | |
| /* | |
| * Urgs ... Too many MIPS machines to handle this in a generic way. | |
| * So handle all using function pointers to machine specific | |
| * functions. | |
| */ | |
| void (*_machine_restart)(char *command); | |
| void (*_machine_halt)(void); | |
| void (*pm_power_off)(void); | |
| EXPORT_SYMBOL(pm_power_off); | |
| static void machine_hang(void) | |
| { | |
| /* | |
| * We're hanging the system so we don't want to be interrupted anymore. | |
| * Any interrupt handlers that ran would at best be useless & at worst | |
| * go awry because the system isn't in a functional state. | |
| */ | |
| local_irq_disable(); | |
| /* | |
| * Mask all interrupts, giving us a better chance of remaining in the | |
| * low power wait state. | |
| */ | |
| clear_c0_status(ST0_IM); | |
| while (true) { | |
| if (cpu_has_mips_r) { | |
| /* | |
| * We know that the wait instruction is supported so | |
| * make use of it directly, leaving interrupts | |
| * disabled. | |
| */ | |
| asm volatile( | |
| ".set push\n\t" | |
| ".set " MIPS_ISA_ARCH_LEVEL "\n\t" | |
| "wait\n\t" | |
| ".set pop"); | |
| } else if (cpu_wait) { | |
| /* | |
| * Try the cpu_wait() callback. This isn't ideal since | |
| * it'll re-enable interrupts, but that ought to be | |
| * harmless given that they're all masked. | |
| */ | |
| cpu_wait(); | |
| local_irq_disable(); | |
| } else { | |
| /* | |
| * We're going to burn some power running round the | |
| * loop, but we don't really have a choice. This isn't | |
| * a path we should expect to run for long during | |
| * typical use anyway. | |
| */ | |
| } | |
| /* | |
| * In most modern MIPS CPUs interrupts will cause the wait | |
| * instruction to graduate even when disabled, and in some | |
| * cases even when masked. In order to prevent a timer | |
| * interrupt from continuously taking us out of the low power | |
| * wait state, we clear any pending timer interrupt here. | |
| */ | |
| if (cpu_has_counter) | |
| write_c0_compare(0); | |
| } | |
| } | |
| void machine_restart(char *command) | |
| { | |
| if (_machine_restart) | |
| _machine_restart(command); | |
| #ifdef CONFIG_SMP | |
| preempt_disable(); | |
| smp_send_stop(); | |
| #endif | |
| do_kernel_restart(command); | |
| mdelay(1000); | |
| pr_emerg("Reboot failed -- System halted\n"); | |
| machine_hang(); | |
| } | |
| void machine_halt(void) | |
| { | |
| if (_machine_halt) | |
| _machine_halt(); | |
| #ifdef CONFIG_SMP | |
| preempt_disable(); | |
| smp_send_stop(); | |
| #endif | |
| machine_hang(); | |
| } | |
| void machine_power_off(void) | |
| { | |
| if (pm_power_off) | |
| pm_power_off(); | |
| #ifdef CONFIG_SMP | |
| preempt_disable(); | |
| smp_send_stop(); | |
| #endif | |
| machine_hang(); | |
| } |