MIPS: SGI-IP30: power off through the RTC
Subsystems (2)
v2 cover MIPS: SGI-IP30: power off through the RTC
The Octane's power supply is switched by its DS1687 RTC. Patch 1 uses it for power-off and halt. Patch 2 turns a power button press into an orderly power-off, and adds the hook to the IOC3 RTC platform data in drivers/mfd/ioc3.c.
Both take effect only with CONFIG_RTC_DRV_DS1685_FAMILY=y, which ip30_defconfig sets with "MIPS: SGI-IP30: fix the defconfig defaults for the Bridge PROM and RTC" from the "MIPS: SGI fixes" series. They build without it. Patch 2 drives the RTC with interrupts disabled, so at run time it needs "rtc: ds1685: do not sleep in the power-off handler" from the rtc-ds1685 v2 series sent to the RTC tree.
v1 was tested on an Octane with poweroff, one and two power button presses, and a stalled shutdown.
Changes in v2:
- Patch 1: drop the device reference when the RTC has no driver data.
- Patch 2: only the first power-off drives the RTC and later callers wait for the power to drop; the 120 second timeout holds the device lock against an unbind of rtc-ds1685. The RTC's power-off message is printed from the power-off context, as the console thread cannot run once interrupts are off.
v1: https://lore.kernel.org/all/[email protected]/
Imre Kaloz (2):
MIPS: SGI-IP30: power off through the DS1687 RTC
MIPS: SGI-IP30: shut down cleanly on a power button press
arch/mips/include/asm/sgi/heart.h | 2 + arch/mips/sgi-ip30/ip30-power.c | 94 +++++++++++++++++++++++++++++++ drivers/mfd/ioc3.c | 6 ++ 3 files changed, 102 insertions(+)
base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907
Signature · 1 line
2.47.3
PATCH v2 1/2 MIPS: SGI-IP30: power off through the DS1687 RTC
The Octane's power supply is switched by its DS1687 RTC. Use ds1685_rtc_poweroff() for power-off and halt, with all HEART interrupts masked first, instead of leaving the machine halted.
--- arch/mips/sgi-ip30/ip30-power.c | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
arch/mips/sgi-ip30/ip30-power.c +34 -0
@@ -15,6 +15,7 @@#include <linux/delay.h> #include <linux/rtc/ds1685.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> #include <linux/pm.h> #include <asm/reboot.h>
@@ -31,10 +32,43 @@ static void __noreturn ip30_machine_restart(char *cmd)unreachable (); } +static void ip30_machine_power_off (void ) +{ +struct device *dev; +int cpu; + +if (!IS_REACHABLE (CONFIG_RTC_DRV_DS1685_FAMILY )) +return ; + + dev =bus_find_device_by_name (&platform_bus_type,NULL ,"rtc-ds1685" ); +if (!dev) +return ; +if (!dev_get_drvdata (dev)) { +put_device (dev); +return ; + } + +/* Devices losing power may still interrupt; keep their handlers out. */ +for (cpu =0 ; cpu <HEART_MAX_CPUS ; cpu++) +heart_write (HEART_CLR_ALL_MASK , &heart_regs->imr[cpu]); +heart_write (HEART_ACK_ALL_MASK , &heart_regs->clear_isr); + +ds1685_rtc_poweroff (to_platform_device (dev)); +} +static int __init ip30_reboot_setup (void ) { _machine_restart = ip30_machine_restart; +/* +* The DS1687 RTC switches the power supply off. Halt powers off +* too, as the power button needs a running kernel. +*/ +if (IS_REACHABLE (CONFIG_RTC_DRV_DS1685_FAMILY )) { + _machine_halt = ip30_machine_power_off; + pm_power_off = ip30_machine_power_off; + } +return 0 ; }
Signature · 1 line
2.47.3
Re: [PATCH v2 1/2] MIPS: SGI-IP30: power off through the DS1687 RTC
arch/mips/sgi-ip30/ip30-power.c static void __noreturn ip30_machine_restart(char *cmd) · line 66
Quoted 4 lines · Imre Kaloz
arch/mips/sgi-ip30/ip30-power.c +4
-0+/* +* The DS1687 RTC switches the power supply off. Halt powers off +* too, as the power button needs a running kernel. +*/
Hmm, up to you to decide really, however the usual custom for halt has
been to do nothing for systems that have no console monitor to return to. Shutdown scripts may arrange for no processes to remain running beyond init(8), making the system halted for any practical purpose though still technically running, but with:
# halt -f
command issued control just reaches back to the shell prompt. That's what my observation has been about Linux since 1990s.
Please consider this a point in discussion though rather than any kind of
objection. Perhaps your choice better matches the system in question.
Conversely, if you do have a console monitor to return to, then it's what
the halt action is supposed to do. The firmware may then decide what to do next based on its own settings ("halt action") where available, e.g. present a command prompt or try to boot the system again.
Maciej
PATCH v2 2/2 MIPS: SGI-IP30: shut down cleanly on a power button press
In reply to Imre KalozThe power button raises a kickstart interrupt on the DS1687, and rtc-ds1685 cuts power at once unless the platform supplies a prepare_poweroff hook. Supply one that starts an orderly power-off, cutting power directly on a second press or after 120 seconds.
Power-off drives the RTC's index/data register pair, so only the first of several concurrent callers proceeds and the rest wait. The timeout holds the device lock against an unbind of rtc-ds1685.
--- arch/mips/include/asm/sgi/heart.h | 2 ++ arch/mips/sgi-ip30/ip30-power.c | 60 +++++++++++++++++++++++++++++++ drivers/mfd/ioc3.c | 6 ++++ 3 files changed, 68 insertions(+)
arch/mips/include/asm/sgi/heart.h +2 -0
@@ -317,6 +317,8 @@ struct ip30_heart_regs { /* 0x0ff00000 */extern struct ip30_heart_regs __iomem *heart_regs; +void ip30_prepare_poweroff (void ); +#define heart_read ____raw_readq#define heart_write ____raw_writeq
arch/mips/sgi-ip30/ip30-power.c +60 -0
92 lines not on this page · raw message
drivers/mfd/ioc3.c +6 -0
@@ -24,6 +24,9 @@#include <asm/pci/bridge.h> #include <asm/sn/ioc3.h> +#ifdef CONFIG_SGI_IP30 +#include <asm/sgi/heart.h> +#endif #define IOC3_IRQ_SERIAL_A 6#define IOC3_IRQ_SERIAL_B 15
@@ -321,6 +324,9 @@ static struct ds1685_rtc_platform_data ip30_rtc_platform_data = { .no_irq = false, .uie_unsupported = true, .access_type = ds1685_reg_indirect, +#ifdef CONFIG_SGI_IP30 + .plat_prepare_poweroff = ip30_prepare_poweroff, +#endif };static const struct resource ioc3_rtc_ds1685_resources[] = {
Signature · 1 line
2.47.3
[email protected]
AI reviewRe: [PATCH v2 2/2] MIPS: SGI-IP30: shut down cleanly on a power button press
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] MIPS: SGI-IP30: atomic sleep and deadlock in ip30_machine_power_off() - [High] MIPS: SGI-IP30…
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] MIPS: SGI-IP30: atomic sleep and deadlock in ip30_machine_power_off()
- [High] MIPS: SGI-IP30: broken modular RTC power-off in ip30_prepare_poweroff()
- [High] MIPS: SGI-IP30: unserialized indirect RTC register access in poweroff --
commit dd15dcc6768c98c5a5f42977da8413212870ba81
Author: Imre Kaloz <[email protected]>
MIPS: SGI-IP30: shut down cleanly on a power button press
This patch supplies a plat_prepare_poweroff hook for SGI IP30 that initiates an orderly shutdown via userspace on the first power button press and forces an immediate power-off on a second press or after a 120-second timeout. It also serializes concurrent callers of ip30_machine_power_off() and enters an nbcon emergency section before invoking ds1685_rtc_poweroff().
Quoted 4 lines
arch/mips/sgi-ip30/ip30-power.c +0
-0
[ ... ]
Quoted 14 lines
arch/mips/sgi-ip30/ip30-power.c +3
-0@@ -32,6 +35,8 @@ static void __noreturn ip30_machine_restart(char *cmd)unreachable (); } +static atomic_t ip30_power_off_claimed =ATOMIC_INIT (0 ); +static void ip30_machine_power_off (void ) {struct device *dev;@@ -48,14 +53,69 @@ static void ip30_machine_power_off(void)return ; } +/*
arch/mips/sgi-ip30/ip30-power.c static void ip30_machine_power_off(void) · line 66
Quoted 10 lines
arch/mips/sgi-ip30/ip30-power.c +10
-0+* The RTC registers are reached through an index/data pair, so one +* CPU at a time may drive them, and a second button press, the +* timeout and pm_power_off can arrive together. Interrupts go off +* first so the claimant cannot be preempted by a waiter on its CPU; +* the waiters spin until the power drops. +*/ +local_irq_disable (); +if (atomic_xchg (&ip30_power_off_claimed,1 )) +for (;;) +cpu_relax ();
[Severity: High]
As reported by Sashiko on v1 [1], can another CPU still race on the indirect RTC index/data registers and bank selection while ds1685_rtc_poweroff() is running?
On SGI IP30, the DS1687 is accessed in indirect register mode (ds1685_reg_indirect), where every ds1685_indirect_read() and ds1685_indirect_write() performs two separate MMIO accesses to rtc->regs and rtc->data, and RTC_EXT_CTRL_4A (0x4a) is only accessible while Bank 1 is selected.
Claiming ip30_power_off_claimed only serializes callers of ip30_machine_power_off() against each other; it does not stop other CPUs or acquire rtc_lock(rtc->dev), and ds1685_rtc_poweroff() only calls disable_irq_nosync(rtc->irq_num):
1. If a second power button interrupt starts ds1685_rtc_extended_irq() on
another CPU before HEART interrupts are masked, that handler sleeps in
msleep(500) and wakes via the per-CPU CP0 timer interrupt (which is not
routed through HEART). It then accesses RTC_EXT_CTRL_4B via rtc->regs
and rtc->data before calling rtc->prepare_poweroff():
drivers/rtc/rtc-ds1685.c:ds1685_rtc_extended_irq() {
...
msleep(500);
rtc->write(rtc, RTC_EXT_CTRL_4B,
(rtc->read(rtc, RTC_EXT_CTRL_4B) |
RTC_CTRL_4B_KSE));
if (rtc->prepare_poweroff != NULL)
rtc->prepare_poweroff();
...
ds1685_rtc_switch_to_bank0(rtc);
} 2. After ip30_prepare_poweroff() returns on the first press, ds1685_rtc_extended_irq() calls ds1685_rtc_switch_to_bank0(rtc), which can race with a concurrent pm_power_off() call.
3. During ip30_power_timeout() or userspace shutdown (such as hwclock on another CPU), normal RTC ops, NVRAM accesses, or sysfs/procfs reads acquire rtc_lock(rtc->dev), drive rtc->regs and rtc->data, and switch the chip back to Bank 0.
If another CPU changes the index register or switches the RTC back to Bank 0 before ds1685_rtc_poweroff() writes RTC_CTRL_4A_PAB to RTC_EXT_CTRL_4A, wouldn't that write corrupt battery-backed Bank 0 NVRAM at offset 0x4a and leave the system spinning forever without cutting power?
Quoted 4 lines
arch/mips/sgi-ip30/ip30-power.c +1
-0+/* Devices losing power may still interrupt; keep their handlers out. */ for (cpu =0 ; cpu <HEART_MAX_CPUS ; cpu++)heart_write (HEART_CLR_ALL_MASK , &heart_regs->imr[cpu]);
arch/mips/sgi-ip30/ip30-power.c static void ip30_machine_power_off(void) · line 80
Quoted 10 lines
arch/mips/sgi-ip30/ip30-power.c +6
-0heart_write (HEART_ACK_ALL_MASK , &heart_regs->clear_isr); +/* +* Nothing schedules before the power drops, so the RTC's message must +* be printed from this context, not left to the console thread. The +* emergency section is never left. +*/ +nbcon_cpu_emergency_enter ();ds1685_rtc_poweroff (to_platform_device (dev)); }
[Severity: High]
Doesn't ds1685_rtc_poweroff() sleep in msleep(20) before cutting power, violating the atomic context established by local_irq_disable() and nbcon_cpu_emergency_enter()?
In drivers/rtc/rtc-ds1685.c, ds1685_rtc_poweroff() calls msleep(20) right before setting RTC_CTRL_4A_PAB:
drivers/rtc/rtc-ds1685.c:ds1685_rtc_poweroff() {
...
/* Set PAB to 1 in Control 4A to power the system down. */
dev_warn(&pdev->dev, "Powerdown.\n");
msleep(20);
rtc->write(rtc, RTC_EXT_CTRL_4A,
(ctrl4a | RTC_CTRL_4A_PAB));
}Because ip30_machine_power_off() disables local interrupts and calls nbcon_cpu_emergency_enter() (which disables preemption and increments the per-CPU emergency nesting count), calling msleep(20) -> schedule() triggers a scheduling-while-atomic bug, context-switches with the per-CPU nbcon emergency nesting count elevated, and re-enables local interrupts when the scheduler unlocks the runqueue.
Furthermore, while the claimant task is sleeping in msleep(20), if a concurrent caller of ip30_machine_power_off() (such as ip30_power_timeout(), poweroff_work from orderly_poweroff(), or the RTC IRQ thread on a second button press) gets scheduled onto the same CPU, it will call local_irq_disable(), see ip30_power_off_claimed == 1, and spin forever in cpu_relax() with interrupts disabled. Wouldn't that prevent the 20ms timer wakeup from ever completing and permanently deadlock the machine before RTC_CTRL_4A_PAB is written?
Quoted 45 lines
arch/mips/sgi-ip30/ip30-power.c +37
-0+#define IP30_POWERDOWN_TIMEOUT 120/* seconds */ + +static bool ip30_shutting_down; + +static void ip30_power_timeout (struct work_struct *work) +{ +struct device *dev; + + dev =bus_find_device_by_name (&platform_bus_type,NULL ,"rtc-ds1685" ); +if (!dev) +return ; + +/* +* Unbinding rtc-ds1685 frees the RTC under the device lock, so hold +* it across the drvdata check and the power-off. +*/ +device_lock (dev); +ip30_machine_power_off (); +device_unlock (dev); +put_device (dev); +} + +static DECLARE_DELAYED_WORK(ip30_power_work, ip30_power_timeout); + +/* Called by the RTC driver when the power button is pressed. */ +void ip30_prepare_poweroff (void ) +{ +/* A second press powers off at once. */ +if (ip30_shutting_down) +ip30_machine_power_off (); + +/* Shut down through userspace, powering off anyway if it stalls. */ + ip30_shutting_down = true; +orderly_poweroff (true); +schedule_delayed_work (&ip30_power_work,IP30_POWERDOWN_TIMEOUT *HZ ); +} +static int __init ip30_reboot_setup (void ) { _machine_restart = ip30_machine_restart;drivers/mfd/ioc3.c +0
-0
[ ... ]
drivers/mfd/ioc3.c static struct ds1685_rtc_platform_data ip30_rtc_platform_data = { · line 330
Quoted 8 lines
drivers/mfd/ioc3.c +3
-0@@ -321,6 +324,9 @@ static struct ds1685_rtc_platform_data ip30_rtc_platform_data = { .no_irq = false, .uie_unsupported = true, .access_type = ds1685_reg_indirect, +#ifdef CONFIG_SGI_IP30 + .plat_prepare_poweroff = ip30_prepare_poweroff, +#endif };
[Severity: High]
What happens on a power button press when CONFIG_SGI_IP30=y and CONFIG_RTC_DRV_DS1685_FAMILY=m?
Previously, ip30_rtc_platform_data.plat_prepare_poweroff was NULL, so when rtc-ds1685 was built as a module, ds1685_rtc_extended_irq() fell back to calling ds1685_rtc_poweroff(pdev) directly inside the module.
With this change, ip30_rtc_platform_data.plat_prepare_poweroff is wired up whenever CONFIG_SGI_IP30 is enabled, overriding the direct ds1685_rtc_poweroff(pdev) fallback. However, in built-in arch/mips/sgi-ip30/ip30-power.c, IS_REACHABLE(CONFIG_RTC_DRV_DS1685_FAMILY) is false when CONFIG_RTC_DRV_DS1685_FAMILY=m:
arch/mips/sgi-ip30/ip30-power.c:ip30_machine_power_off() {
if (!IS_REACHABLE(CONFIG_RTC_DRV_DS1685_FAMILY))
return;
...
}As a result, pm_power_off is not registered and ip30_machine_power_off() is a no-op, so neither orderly_poweroff(true), ip30_power_timeout(), nor a second power button press can cut power.
In addition, in ip30_prepare_poweroff(), the second-press path:
if (ip30_shutting_down) ip30_machine_power_off();
has no return statement, so whenever ip30_machine_power_off() returns early (for example when !IS_REACHABLE(CONFIG_RTC_DRV_DS1685_FAMILY) or if the rtc-ds1685 device is unbound), execution falls through and re-runs orderly_poweroff(true) and schedule_delayed_work(). Should plat_prepare_poweroff only be set when IS_REACHABLE(CONFIG_RTC_DRV_DS1685_FAMILY) (or Kconfig updated so ip30-power.c can always reach the driver), and should the second-press branch in ip30_prepare_poweroff() return?
[1] https://lore.kernel.org/r/[email protected]
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v2 2/2] MIPS: SGI-IP30: shut down cleanly on a power button press
Quoted 8 lines
arch/mips/sgi-ip30/ip30-power.c static void ip30_machine_power_off(void) · line 66
Quoted 4 lines
arch/mips/sgi-ip30/ip30-power.c +4
-0+local_irq_disable (); +if (atomic_xchg (&ip30_power_off_claimed,1 )) +for (;;) +cpu_relax ();As reported by Sashiko on v1 [1], can another CPU still race on the indirect RTC index/data registers and bank selection while
ds1685_rtc_poweroff()is running?
On dual-CPU Octanes the second CPU can still move the index or the bank. v3 will stop the other CPU with smp_send_stop() before touching HEART or the RTC.
Quoted 5 lines
Quoted 1 line
ds1685_rtc_poweroff(to_platform_device(dev));
Doesn't
ds1685_rtc_poweroff()sleep in msleep(20) before cutting power, violating the atomic context established bylocal_irq_disable()andnbcon_cpu_emergency_enter()?
Not with the dependency named in the cover letter. Commit 21a829af60a7 ("rtc: ds1685: do not sleep in the power-off handler"), in v7.4-rc1, replaces that msleep() with mdelay().
Quoted 6 lines
drivers/mfd/ioc3.c static struct ds1685_rtc_platform_data ip30_rtc_platform_data = { · line 329
Quoted 3 lines
drivers/mfd/ioc3.c +3
-0+#ifdef CONFIG_SGI_IP30 + .plat_prepare_poweroff = ip30_prepare_poweroff, +#endif What happens on a power button press when
CONFIG_SGI_IP30=yandCONFIG_RTC_DRV_DS1685_FAMILY=m?
The button stops cutting the power, as the built-in ip30_machine_power_off() cannot reach the module. v3 will set the hook only when rtc-ds1685 is built in, so a modular driver will power off on a press as it did before this series, and the second-press branch will return.
Thanks,
Imre