| 1 | |
| 2 | * This file is subject to the terms and conditions of the GNU General Public
|
| 3 | * License. See the file "COPYING" in the main directory of this archive
|
| 4 | * for more details.
|
| 5 | *
|
| 6 | * Copyright (C) 2001, 06 by Ralf Baechle ([email protected])
|
| 7 | * Copyright (C) 2001 MIPS Technologies, Inc.
|
| 8 |
|
| 9 | #include <linux/kernel.h>
|
| 10 | #include <linux/export.h>
|
| 11 | #include <linux/pm.h>
|
| 12 | #include <linux/types.h>
|
| 13 | #include <linux/reboot.h>
|
| 14 | #include <linux/delay.h>
|
| 15 |
|
| 16 | #include <asm/compiler.h>
|
| 17 | #include <asm/idle.h>
|
| 18 | #include <asm/mipsregs.h>
|
| 19 | #include <asm/reboot.h>
|
| 20 |
|
| 21 | |
| 22 | * Urgs ... Too many MIPS machines to handle this in a generic way.
|
| 23 | * So handle all using function pointers to machine specific
|
| 24 | * functions.
|
| 25 |
|
| 26 | void (*_machine_restart)(char *command);
|
| 27 | void (*_machine_halt)(void);
|
| 28 | void (*pm_power_off)(void);
|
| 29 |
|
| 30 | EXPORT_SYMBOL(pm_power_off);
|
| 31 |
|
| 32 | static void machine_hang(void)
|
| 33 | {
|
| 34 | |
| 35 | * We're hanging the system so we don't want to be interrupted anymore.
|
| 36 | * Any interrupt handlers that ran would at best be useless & at worst
|
| 37 | * go awry because the system isn't in a functional state.
|
| 38 |
|
| 39 | local_irq_disable();
|
| 40 |
|
| 41 | |
| 42 | * Mask all interrupts, giving us a better chance of remaining in the
|
| 43 | * low power wait state.
|
| 44 |
|
| 45 | clear_c0_status(ST0_IM);
|
| 46 |
|
| 47 | while (true) {
|
| 48 | if (cpu_has_mips_r) {
|
| 49 | |
| 50 | * We know that the wait instruction is supported so
|
| 51 | * make use of it directly, leaving interrupts
|
| 52 | * disabled.
|
| 53 |
|
| 54 | asm volatile(
|
| 55 | ".set push\n\t"
|
| 56 | ".set " MIPS_ISA_ARCH_LEVEL "\n\t"
|
| 57 | "wait\n\t"
|
| 58 | ".set pop");
|
| 59 | } else if (cpu_wait) {
|
| 60 | |
| 61 | * Try the cpu_wait() callback. This isn't ideal since
|
| 62 | * it'll re-enable interrupts, but that ought to be
|
| 63 | * harmless given that they're all masked.
|
| 64 |
|
| 65 | cpu_wait();
|
| 66 | local_irq_disable();
|
| 67 | } else {
|
| 68 | |
| 69 | * We're going to burn some power running round the
|
| 70 | * loop, but we don't really have a choice. This isn't
|
| 71 | * a path we should expect to run for long during
|
| 72 | * typical use anyway.
|
| 73 |
|
| 74 | }
|
| 75 |
|
| 76 | |
| 77 | * In most modern MIPS CPUs interrupts will cause the wait
|
| 78 | * instruction to graduate even when disabled, and in some
|
| 79 | * cases even when masked. In order to prevent a timer
|
| 80 | * interrupt from continuously taking us out of the low power
|
| 81 | * wait state, we clear any pending timer interrupt here.
|
| 82 |
|
| 83 | if (cpu_has_counter)
|
| 84 | write_c0_compare(0);
|
| 85 | }
|
| 86 | }
|
| 87 |
|
| 88 | void machine_restart(char *command)
|
| 89 | {
|
| 90 | if (_machine_restart)
|
| 91 | _machine_restart(command);
|
| 92 |
|
| 93 | #ifdef CONFIG_SMP
|
| 94 | preempt_disable();
|
| 95 | smp_send_stop();
|
| 96 | #endif
|
| 97 | do_kernel_restart(command);
|
| 98 | mdelay(1000);
|
| 99 | pr_emerg("Reboot failed -- System halted\n");
|
| 100 | machine_hang();
|
| 101 | }
|
| 102 |
|
| 103 | void machine_halt(void)
|
| 104 | {
|
| 105 | if (_machine_halt)
|
| 106 | _machine_halt();
|
| 107 |
|
| 108 | #ifdef CONFIG_SMP
|
| 109 | preempt_disable();
|
| 110 | smp_send_stop();
|
| 111 | #endif
|
| 112 | machine_hang();
|
| 113 | }
|
| 114 |
|
| 115 | void machine_power_off(void)
|
| 116 | {
|
| 117 | do_kernel_power_off();
|
| 118 |
|
| 119 | #ifdef CONFIG_SMP
|
| 120 | preempt_disable();
|
| 121 | smp_send_stop();
|
| 122 | #endif
|
| 123 | machine_hang();
|
| 124 | }
|