aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@mips.com>2017-08-23 16:53:16 -0400
committerJames Hogan <jhogan@kernel.org>2018-03-09 06:22:43 -0500
commit997e93d4df165202f2ee36a48f7aca172ff8b48a (patch)
treee8b4a559c3db1f3069a16c48bf025e8f41e71eeb
parent4d73b73ff7943c92e8bcb65fece8caa70c24db87 (diff)
MIPS: Hang more efficiently on halt/powerdown/restart
The generic MIPS implementations of halting, powering down or restarting the system all hang using a busy loop as a last resort. We have many platforms which avoid this loop by implementing their own, many using some variation upon executing a wait instruction to lower CPU power usage if we reach this point. In order to prepare for cleaning up these various custom implementations of the same thing, this patch makes the generic machine_halt(), machine_power_off() & machine_restart() functions each make use of the wait instruction to lower CPU power usage in cases where we know that the wait instruction is available. If wait isn't known to be supported then we fall back to calling cpu_wait(), and if we don't have a cpu_wait() callback then we effectively continue using a busy loop. In effect the new machine_hang() function provides a superset of the functionality that the various platforms currently provide differing subsets of. Signed-off-by: Paul Burton <paul.burton@mips.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/17178/ Signed-off-by: James Hogan <jhogan@kernel.org>
-rw-r--r--arch/mips/kernel/reset.c68
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
27EXPORT_SYMBOL(pm_power_off); 30EXPORT_SYMBOL(pm_power_off);
28 31
32static 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
29void machine_restart(char *command) 88void 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
45void machine_halt(void) 103void 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
58void machine_power_off(void) 115void 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}