diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2022-03-25 15:52:04 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2022-03-25 15:52:04 +0000 |
commit | 03cf5d9ee57129e65384678c6fa164ed2099138f (patch) | |
tree | 22fad3e74b83f51b8fca883d47aa99a3d5edeaa8 /sys | |
parent | 5c2b458d305dc7c8bcd474f30f0c161152a67a5a (diff) |
Implement reboot/powerdown support based on nvmem cells. This works on
M1 Pro/Max machines and will hopefully continue to work on future models
by abstracting the magic flag that needs to be set using device tree
properties.
ok patrick@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/arm64/dev/aplsmc.c | 61 |
1 files changed, 54 insertions, 7 deletions
diff --git a/sys/arch/arm64/dev/aplsmc.c b/sys/arch/arm64/dev/aplsmc.c index c5c9a2c793b..b9cb2b5d69c 100644 --- a/sys/arch/arm64/dev/aplsmc.c +++ b/sys/arch/arm64/dev/aplsmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplsmc.c,v 1.10 2022/03/15 18:46:15 kettenis Exp $ */ +/* $OpenBSD: aplsmc.c,v 1.11 2022/03/25 15:52:03 kettenis Exp $ */ /* * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> * @@ -36,6 +36,7 @@ #include "apm.h" extern void (*cpuresetfn)(void); +extern void (*powerdownfn)(void); /* SMC mailbox endpoint */ #define SMC_EP 32 @@ -99,7 +100,9 @@ struct aplsmc_softc { int sc_rtc_node; struct todr_chip_handle sc_todr; - + + int sc_reboot_node; + struct aplsmc_sensor *sc_smcsensors[APLSMC_MAX_SENSORS]; struct ksensor sc_sensors[APLSMC_MAX_SENSORS]; int sc_nsensors; @@ -157,6 +160,8 @@ void aplsmc_set_pin(void *, uint32_t *, int); int aplsmc_gettime(struct todr_chip_handle *, struct timeval *); int aplsmc_settime(struct todr_chip_handle *, struct timeval *); void aplsmc_reset(void); +void aplsmc_powerdown(void); +void aplsmc_reboot_attachhook(struct device *); int aplsmc_match(struct device *parent, void *match, void *aux) @@ -222,6 +227,8 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux) printf("\n"); + aplsmc_sc = sc; + node = OF_getnodebyname(faa->fa_node, "gpio"); if (node) { sc->sc_gc.gc_node = node; @@ -235,7 +242,6 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux) * supported by the SMC firmware. */ error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN); - node = OF_getnodebyname(faa->fa_node, "rtc"); if (node && error == 0) { sc->sc_rtc_node = node; @@ -245,8 +251,13 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux) todr_attach(&sc->sc_todr); } - aplsmc_sc = sc; - cpuresetfn = aplsmc_reset; + node = OF_getnodebyname(faa->fa_node, "reboot"); + if (node) { + sc->sc_reboot_node = node; + cpuresetfn = aplsmc_reset; + powerdownfn = aplsmc_powerdown; + config_mountroot(self, aplsmc_reboot_attachhook); + } #ifndef SMALL_KERNEL @@ -578,11 +589,47 @@ aplsmc_settime(struct todr_chip_handle *handle, struct timeval *tv) } void +aplsmc_reboot_attachhook(struct device *self) +{ + struct aplsmc_softc *sc = (struct aplsmc_softc *)self; + uint8_t count = 0; + + /* Reset error counters. */ + nvmem_write_cell(sc->sc_reboot_node, "boot_error_count", + &count, sizeof(count)); + nvmem_write_cell(sc->sc_reboot_node, "panic_count", + &count, sizeof(count)); +} + +void aplsmc_reset(void) { struct aplsmc_softc *sc = aplsmc_sc; uint32_t key = SMC_KEY("MBSE"); - uint32_t data = SMC_KEY("off1"); + uint32_t rest = SMC_KEY("rest"); + uint32_t phra = SMC_KEY("phra"); + uint8_t boot_stage = 0; + + aplsmc_write_key(sc, key, &rest, sizeof(rest)); + nvmem_write_cell(sc->sc_reboot_node, "boot_stage", + &boot_stage, sizeof(boot_stage)); + aplsmc_write_key(sc, key, &phra, sizeof(phra)); +} - aplsmc_write_key(sc, key, &data, sizeof(data)); +void +aplsmc_powerdown(void) +{ + struct aplsmc_softc *sc = aplsmc_sc; + uint32_t key = SMC_KEY("MBSE"); + uint32_t offw = SMC_KEY("offw"); + uint32_t off1 = SMC_KEY("off1"); + uint8_t boot_stage = 0; + uint8_t shutdown_flag = 1; + + aplsmc_write_key(sc, key, &offw, sizeof(offw)); + nvmem_write_cell(sc->sc_reboot_node, "boot_stage", + &boot_stage, sizeof(boot_stage)); + nvmem_write_cell(sc->sc_reboot_node, "shutdown_flag", + &shutdown_flag, sizeof(shutdown_flag)); + aplsmc_write_key(sc, key, &off1, sizeof(off1)); } |