Skip to content

Commit 997e93d

Browse files
paulburtonamalon
authored andcommitted
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>
1 parent 4d73b73 commit 997e93d

File tree

1 file changed

+62
-6
lines changed

1 file changed

+62
-6
lines changed

arch/mips/kernel/reset.c

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <linux/reboot.h>
1414
#include <linux/delay.h>
1515

16+
#include <asm/compiler.h>
17+
#include <asm/idle.h>
18+
#include <asm/mipsregs.h>
1619
#include <asm/reboot.h>
1720

1821
/*
@@ -26,6 +29,62 @@ void (*pm_power_off)(void);
2629

2730
EXPORT_SYMBOL(pm_power_off);
2831

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+
2988
void machine_restart(char *command)
3089
{
3190
if (_machine_restart)
@@ -38,8 +97,7 @@ void machine_restart(char *command)
3897
do_kernel_restart(command);
3998
mdelay(1000);
4099
pr_emerg("Reboot failed -- System halted\n");
41-
local_irq_disable();
42-
while (1);
100+
machine_hang();
43101
}
44102

45103
void machine_halt(void)
@@ -51,8 +109,7 @@ void machine_halt(void)
51109
preempt_disable();
52110
smp_send_stop();
53111
#endif
54-
local_irq_disable();
55-
while (1);
112+
machine_hang();
56113
}
57114

58115
void machine_power_off(void)
@@ -64,6 +121,5 @@ void machine_power_off(void)
64121
preempt_disable();
65122
smp_send_stop();
66123
#endif
67-
local_irq_disable();
68-
while (1);
124+
machine_hang();
69125
}

0 commit comments

Comments
 (0)