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

Reply via email to