diff options
Diffstat (limited to 'arch/mips/kernel/reset.c')
-rw-r--r-- | arch/mips/kernel/reset.c | 68 |
1 files changed, 62 insertions, 6 deletions
diff --git a/arch/mips/kernel/reset.c b/arch/mips/kernel/reset.c index 7c746d3458e7..6288780b779e 100644 --- a/arch/mips/kernel/reset.c +++ b/arch/mips/kernel/reset.c | |||
@@ -13,6 +13,9 @@ | |||
13 | #include <linux/reboot.h> | 13 | #include <linux/reboot.h> |
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | 15 | ||
16 | #include <asm/compiler.h> | ||
17 | #include <asm/idle.h> | ||
18 | #include <asm/mipsregs.h> | ||
16 | #include <asm/reboot.h> | 19 | #include <asm/reboot.h> |
17 | 20 | ||
18 | /* | 21 | /* |
@@ -26,6 +29,62 @@ void (*pm_power_off)(void); | |||
26 | 29 | ||
27 | EXPORT_SYMBOL(pm_power_off); | 30 | EXPORT_SYMBOL(pm_power_off); |
28 | 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 | |||
29 | void machine_restart(char *command) | 88 | void machine_restart(char *command) |
30 | { | 89 | { |
31 | if (_machine_restart) | 90 | if (_machine_restart) |
@@ -38,8 +97,7 @@ void machine_restart(char *command) | |||
38 | do_kernel_restart(command); | 97 | do_kernel_restart(command); |
39 | mdelay(1000); | 98 | mdelay(1000); |
40 | pr_emerg("Reboot failed -- System halted\n"); | 99 | pr_emerg("Reboot failed -- System halted\n"); |
41 | local_irq_disable(); | 100 | machine_hang(); |
42 | while (1); | ||
43 | } | 101 | } |
44 | 102 | ||
45 | void machine_halt(void) | 103 | void machine_halt(void) |
@@ -51,8 +109,7 @@ void machine_halt(void) | |||
51 | preempt_disable(); | 109 | preempt_disable(); |
52 | smp_send_stop(); | 110 | smp_send_stop(); |
53 | #endif | 111 | #endif |
54 | local_irq_disable(); | 112 | machine_hang(); |
55 | while (1); | ||
56 | } | 113 | } |
57 | 114 | ||
58 | void machine_power_off(void) | 115 | void machine_power_off(void) |
@@ -64,6 +121,5 @@ void machine_power_off(void) | |||
64 | preempt_disable(); | 121 | preempt_disable(); |
65 | smp_send_stop(); | 122 | smp_send_stop(); |
66 | #endif | 123 | #endif |
67 | local_irq_disable(); | 124 | machine_hang(); |
68 | while (1); | ||
69 | } | 125 | } |