Implement powerdown. This involves writing a magic bit somewhere in the
authorkettenis <kettenis@openbsd.org>
Thu, 13 Jan 2022 08:59:10 +0000 (08:59 +0000)
committerkettenis <kettenis@openbsd.org>
Thu, 13 Jan 2022 08:59:10 +0000 (08:59 +0000)
address space of the SPMI PMU to prevent the machine from immediately
starting up again.

The implementaton makes aplpmu(4) provide powerdownfn(), which sets the
magic bit and then chains into cpuresetfn().  It also makes aplsmc(4)
provide cpuresetfn() to reset the machine via the SMC.  Resetting via
the watchdog works as well (and will powerdown the machine if the magic
bit is set) but letting the SMC handle things might do some other
required steps.

ok patrick@

sys/arch/arm64/dev/apldog.c
sys/arch/arm64/dev/aplpmu.c
sys/arch/arm64/dev/aplsmc.c

index 0de95db..94d116a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: apldog.c,v 1.2 2021/11/13 23:24:24 kettenis Exp $     */
+/*     $OpenBSD: apldog.c,v 1.3 2022/01/13 08:59:10 kettenis Exp $     */
 /*
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
  *
@@ -100,7 +100,8 @@ apldog_attach(struct device *parent, struct device *self, void *aux)
        HWRITE4(sc, WDT_SYS_CTL, 0);
 
        apldog_sc = sc;
-       cpuresetfn = apldog_reset;
+       if (cpuresetfn == NULL)
+               cpuresetfn = apldog_reset;
 }
 
 void
index 943d2bc..e0d175e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: aplpmu.c,v 1.2 2021/05/27 08:10:12 kettenis Exp $     */
+/*     $OpenBSD: aplpmu.c,v 1.3 2022/01/13 08:59:10 kettenis Exp $     */
 /*
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
  *
@@ -27,6 +27,9 @@
 #include <dev/ofw/openfirm.h>
 #include <dev/ofw/fdt.h>
 
+extern void (*cpuresetfn)(void);
+extern void (*powerdownfn)(void);
+
 /*
  * This driver is based on preliminary device tree bindings and will
  * almost certainly need changes once the official bindings land in
@@ -45,6 +48,9 @@
 #define SERA_TIME_OFFSET       0xd100
 #define SERA_TIME_LEN          6
 
+#define SERA_POWERDOWN         0x9f0f
+#define SERA_POWERDOWN_MAGIC   0x08
+
 struct aplpmu_softc {
        struct device           sc_dev;
        spmi_tag_t              sc_tag;
@@ -54,6 +60,8 @@ struct aplpmu_softc {
        uint64_t                sc_offset;
 };
 
+struct aplpmu_softc *aplpmu_sc;
+
 int    aplpmu_match(struct device *, void *, void *);
 void   aplpmu_attach(struct device *, struct device *, void *);
 
@@ -67,6 +75,7 @@ struct cfdriver aplpmu_cd = {
 
 int    aplpmu_gettime(struct todr_chip_handle *, struct timeval *);
 int    aplpmu_settime(struct todr_chip_handle *, struct timeval *);
+void   aplpmu_powerdown(void);
 
 int
 aplpmu_match(struct device *parent, void *match, void *aux)
@@ -101,6 +110,9 @@ aplpmu_attach(struct device *parent, struct device *self, void *aux)
        sc->sc_todr.todr_gettime = aplpmu_gettime;
        sc->sc_todr.todr_settime = aplpmu_settime;
        todr_attach(&sc->sc_todr);
+
+       aplpmu_sc = sc;
+       powerdownfn = aplpmu_powerdown;
 }
 
 int
@@ -143,3 +155,15 @@ aplpmu_settime(struct todr_chip_handle *handle, struct timeval *tv)
        return spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL,
            SERA_TIME_OFFSET, &data, SERA_TIME_LEN);
 }
+
+void
+aplpmu_powerdown(void)
+{
+       struct aplpmu_softc *sc = aplpmu_sc;
+       uint8_t data = SERA_POWERDOWN_MAGIC;
+
+       spmi_cmd_write(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_WRITEL,
+           SERA_POWERDOWN, &data, sizeof(data));
+
+       cpuresetfn();
+}
index 80f9f52..b7f1aa4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: aplsmc.c,v 1.5 2022/01/12 15:05:38 robert Exp $       */
+/*     $OpenBSD: aplsmc.c,v 1.6 2022/01/13 08:59:10 kettenis Exp $     */
 /*
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
  *
@@ -33,6 +33,8 @@
 
 #include "apm.h"
 
+extern void (*cpuresetfn)(void);
+
 #define SMC_EP                 32
 
 #define SMC_READ_KEY           0x10
@@ -85,7 +87,9 @@ struct aplsmc_softc {
        struct ksensor          sc_sensors[APLSMC_MAX_SENSORS];
        int                     sc_nsensors;
        struct ksensordev       sc_sensordev;
-} *aplsmc_sc;
+};
+
+struct aplsmc_softc *aplsmc_sc;
 
 struct aplsmc_sensor aplsmc_sensors[] = {
        { "ACDI", "ui16", SENSOR_INDICATOR, 1, "power supply" },
@@ -182,6 +186,7 @@ int aplsmc_send_cmd(struct aplsmc_softc *, uint16_t, uint32_t, uint16_t);
 int    aplsmc_wait_cmd(struct aplsmc_softc *sc);
 int    aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t);
 void   aplsmc_refresh_sensors(void *);
+void   aplsmc_reset(void);
 
 int
 aplsmc_match(struct device *parent, void *match, void *aux)
@@ -286,6 +291,7 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux)
        sensor_task_register(sc, aplsmc_refresh_sensors, 5);
 
        aplsmc_sc = sc;
+       cpuresetfn = aplsmc_reset;
 
 #if NAPM > 0
        apm_setinfohook(aplsmc_apminfo);
@@ -423,3 +429,18 @@ aplsmc_refresh_sensors(void *arg)
                        hw_power = (value > 0);
        }
 }
+
+void
+aplsmc_reset(void)
+{
+       struct aplsmc_softc *sc = aplsmc_sc;
+       uint32_t key = SMC_KEY("MBSE");
+       uint32_t data = SMC_KEY("off1");
+
+       bus_space_write_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
+           (uint8_t *)&data, sizeof(data));
+       bus_space_barrier(sc->sc_iot, sc->sc_sram_ioh, 0, sizeof(data),
+           BUS_SPACE_BARRIER_WRITE);
+       aplsmc_send_cmd(sc, SMC_WRITE_KEY, key, sizeof(data));
+       aplsmc_wait_cmd(sc);
+}