On Thu, Aug 18 2022, Jeremie Courreges-Anglas <j...@wxcvbn.org> wrote: > Some time ago I wanted to get a clean poweroff from the power button on > my Unmatched, so that I don't get fsck at reboot the morning after > someone sleeps in the room where the machine lives. kettenis kindly > provided sfgpio(4) to get interrupts working on dapmic(4) instead of my > initial hack that used polling. > > One issue I struggled with for a bit is that masking irqs also masks the > wake-up events, particularly the events we use for dapmic_reset(). > With the diff below most interrupt events are masked off during runtime, > until we're shutting down/rebooting. Maybe this is too paranoid and > I should let more events go through the intr handler, eg for wake-up > events that I don't envision yet? (I would love to get wake on lan > support in cad(4) but my attempts led nowhere so far.) > > Also interesting, the fault register needs to be cleared at boot, else > the interrupt will keep triggering after eg a hard button-driven poweroff. > We could log the faults found in FAULT_LOG at boot time to know why the > machine has stopped. But I'm more concerned about what to do if we get > a fault at runtime (see the XXX). When I tried to disestablish the > interrupt handler with fdt_intr_disestablish(sc->sc_ih) from > dapmic_reset(), I got a fault. Maybe something worth investigating.
The FAULT_LOG register handling seems appropriate after reading entry 6.2 in: https://www.renesas.com/eu/en/document/apn/shared-irq-line-considerations-pm-059 I wish I had read this document earlier. :) > The code below is based off da9063_datasheet_2v2.pdf. I don't know of > other machines we run on that use this controller, the only entry in > dmesglog is matthieu's Unmatched machine. > > Tests, input and oks welcome. In the case where my hifive unmatched lives, I have wired both the shutdown and the reset buttons. Yesterday I tried to see whether the reset button could be handled as a clean reboot, but looking at the hifive unmatched board schematics it seems that the PMIC has no control over it (even if tweaking nRES_MODE). So the diff stands for scrutiny. I would like input and maybe oks to get this in. ;) Index: dev/fdt/dapmic.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/dapmic.c,v retrieving revision 1.2 diff -u -p -r1.2 dapmic.c --- dev/fdt/dapmic.c 6 Apr 2022 18:59:28 -0000 1.2 +++ dev/fdt/dapmic.c 17 Aug 2022 21:59:57 -0000 @@ -19,6 +19,9 @@ #include <sys/systm.h> #include <sys/device.h> #include <sys/malloc.h> +#include <sys/task.h> +#include <sys/proc.h> +#include <sys/signalvar.h> #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_regulator.h> @@ -28,11 +31,31 @@ #include <dev/clock_subr.h> +#include <machine/fdt.h> + extern void (*cpuresetfn)(void); extern void (*powerdownfn)(void); /* Registers */ +#define FAULT_LOG 0x05 #define EVENT_A 0x06 +#define EVENT_A_EVENTS_D (1 << 7) +#define EVENT_A_EVENTS_C (1 << 6) +#define EVENT_A_EVENTS_B (1 << 5) +#define EVENT_A_E_nONKEY (1 << 0) +#define EVENT_B 0x07 +#define EVENT_C 0x08 +#define EVENT_D 0x09 +#define IRQ_MASK_A 0x0a +#define IRQ_MASK_A_M_RESERVED ((1 << 7) | (1 << 6) | (1 << 5)) +#define IRQ_MASK_A_M_SEQ_RDY (1 << 4) +#define IRQ_MASK_A_M_ADC_RDY (1 << 3) +#define IRQ_MASK_A_M_TICK (1 << 2) +#define IRQ_MASK_A_M_ALARM (1 << 1) +#define IRQ_MASK_A_M_nONKEY (1 << 0) +#define IRQ_MASK_B 0x0b +#define IRQ_MASK_C 0x0c +#define IRQ_MASK_D 0x0d #define CONTROL_F 0x13 #define CONTROL_F_WAKE_UP (1 << 2) #define CONTROL_F_SHUTDOWN (1 << 1) @@ -55,11 +78,20 @@ extern void (*powerdownfn)(void); #define ALARM_Y 0x4b #define ALARM_Y_TICK_ON (1 << 7) +#ifdef DAPMIC_DEBUG +# define DPRINTF(args) do { printf args; } while (0) +#else +# define DPRINTF(args) do {} while (0) +#endif + struct dapmic_softc { struct device sc_dev; i2c_tag_t sc_tag; i2c_addr_t sc_addr; + int (*sc_ih)(void *); + struct task sc_task; + struct todr_chip_handle sc_todr; }; @@ -80,8 +112,11 @@ int dapmic_clock_read(struct dapmic_soft int dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *); int dapmic_gettime(struct todr_chip_handle *, struct timeval *); int dapmic_settime(struct todr_chip_handle *, struct timeval *); +void dapmic_reset_irq_mask(struct dapmic_softc *); void dapmic_reset(void); void dapmic_powerdown(void); +int dapmic_intr(void *); +void dapmic_shutdown_task(void *); int dapmic_match(struct device *parent, void *match, void *aux) @@ -96,6 +131,7 @@ dapmic_attach(struct device *parent, str { struct dapmic_softc *sc = (struct dapmic_softc *)self; struct i2c_attach_args *ia = aux; + int node = *(int *)ia->ia_cookie; sc->sc_tag = ia->ia_tag; sc->sc_addr = ia->ia_addr; @@ -105,12 +141,35 @@ dapmic_attach(struct device *parent, str sc->sc_todr.todr_settime = dapmic_settime; todr_attach(&sc->sc_todr); - printf("\n"); - if (cpuresetfn == NULL) cpuresetfn = dapmic_reset; if (powerdownfn == NULL) powerdownfn = dapmic_powerdown; + + task_set(&sc->sc_task, dapmic_shutdown_task, sc); + + /* Mask away events we don't care about */ + dapmic_reg_write(sc, IRQ_MASK_A, + 0xff & ~(IRQ_MASK_A_M_RESERVED | IRQ_MASK_A_M_nONKEY)); + dapmic_reg_write(sc, IRQ_MASK_B, 0xff); + dapmic_reg_write(sc, IRQ_MASK_C, 0xff); + dapmic_reg_write(sc, IRQ_MASK_D, 0xff); + + /* Clear past faults and events */ + dapmic_reg_write(sc, FAULT_LOG, dapmic_reg_read(sc, FAULT_LOG)); + dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A)); + dapmic_reg_write(sc, EVENT_B, dapmic_reg_read(sc, EVENT_B)); + dapmic_reg_write(sc, EVENT_C, dapmic_reg_read(sc, EVENT_C)); + dapmic_reg_write(sc, EVENT_D, dapmic_reg_read(sc, EVENT_D)); + + if (node != 0) { + sc->sc_ih = fdt_intr_establish_idx(node, 0, IPL_CLOCK, + dapmic_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) + printf(", can't establish interrupt"); + } + + printf("\n"); } uint8_t @@ -239,11 +298,23 @@ dapmic_settime(struct todr_chip_handle * } void +dapmic_reset_irq_mask(struct dapmic_softc *sc) +{ + dapmic_reg_write(sc, IRQ_MASK_A, 0); + dapmic_reg_write(sc, IRQ_MASK_B, 0); + dapmic_reg_write(sc, IRQ_MASK_C, 0); + dapmic_reg_write(sc, IRQ_MASK_D, 0); +} + +void dapmic_reset(void) { struct dapmic_softc *sc = dapmic_cd.cd_devs[0]; uint8_t reg; + /* Re-enable irqs and the associated wake-up events */ + dapmic_reset_irq_mask(sc); + /* Enable tick alarm wakeup with a one second interval. */ reg = dapmic_reg_read(sc, ALARM_MO); reg &= ~ALARM_MO_TICK_TYPE; @@ -266,10 +337,90 @@ dapmic_powerdown(void) struct dapmic_softc *sc = dapmic_cd.cd_devs[0]; uint8_t reg; + /* Re-enable irqs and the associated wake-up events */ + dapmic_reset_irq_mask(sc); + /* Disable tick function such that it doesn't wake us up. */ reg = dapmic_reg_read(sc, ALARM_Y); reg &= ~ALARM_Y_TICK_ON; dapmic_reg_write(sc, ALARM_Y, reg); dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN); +} + +void +dapmic_shutdown_task(void *arg) +{ + extern int allowpowerdown; + + if (allowpowerdown == 1) { + allowpowerdown = 0; + prsignal(initprocess, SIGUSR2); + } +} + +int +dapmic_intr(void *arg) +{ + struct dapmic_softc *sc = arg; + uint8_t event_a, event_b, event_c, event_d, fault; + + event_b = event_c = event_d = 0; + + event_a = dapmic_reg_read(sc, EVENT_A); + DPRINTF(("%s: %s: event_a %#02.2hhx", sc->sc_dev.dv_xname, __func__, + event_a)); + + /* Acknowledge all events */ + if (event_a & EVENT_A_EVENTS_B) { + event_b = dapmic_reg_read(sc, EVENT_B); + DPRINTF((", event_b %#02.2hhx", event_b)); + if (event_b != 0) + dapmic_reg_write(sc, EVENT_B, event_b); + } + if (event_a & EVENT_A_EVENTS_C) { + event_c = dapmic_reg_read(sc, EVENT_C); + DPRINTF((", event_c %#02.2hhx", event_c)); + if (event_c != 0) + dapmic_reg_write(sc, EVENT_C, event_c); + } + if (event_a & EVENT_A_EVENTS_D) { + event_d = dapmic_reg_read(sc, EVENT_D); + DPRINTF((", event_d %#02.2hhx", event_d)); + if (event_d != 0) + dapmic_reg_write(sc, EVENT_D, event_d); + } + event_a &= ~(EVENT_A_EVENTS_B|EVENT_A_EVENTS_C|EVENT_A_EVENTS_D); + if (event_a != 0) + dapmic_reg_write(sc, EVENT_A, event_a); + + DPRINTF(("\n")); + + fault = dapmic_reg_read(sc, FAULT_LOG); + if (fault != 0) { + static int warned; + if (!warned) { + warned = 1; + printf("%s: FAULT_LOG %#02.2hhx\n", sc->sc_dev.dv_xname, + fault); + } + /* + * Don't blindly acknowledge the fault log bits, else we may + * prevent legit behavior like a forced poweroff with a long + * power button press. + * XXX If nothing useful can be done here, disestablish the + * handler instead? + */ +#if 0 + dapmic_reg_write(sc, FAULT_LOG, fault); +#endif + } + + if (event_a & EVENT_A_E_nONKEY) + task_add(systq, &sc->sc_task); + + if (event_a | event_b | event_c | event_d) + return 1; + + return 0; } -- jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF DDCC 0DFA 74AE 1524 E7EE