Add power button support.
authorkettenis <kettenis@openbsd.org>
Sat, 27 Aug 2022 16:56:25 +0000 (16:56 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 27 Aug 2022 16:56:25 +0000 (16:56 +0000)
ok tobhe@

sys/arch/arm64/dev/aplsmc.c

index 43ee3bc..578711b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: aplsmc.c,v 1.12 2022/06/12 16:00:12 kettenis Exp $    */
+/*     $OpenBSD: aplsmc.c,v 1.13 2022/08/27 16:56:25 kettenis Exp $    */
 /*
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
  *
@@ -18,7 +18,9 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/device.h>
+#include <sys/proc.h>
 #include <sys/sensors.h>
+#include <sys/signalvar.h>
 
 #include <machine/apmvar.h>
 #include <machine/bus.h>
@@ -42,12 +44,14 @@ extern void (*powerdownfn)(void);
 #define SMC_EP                 32
 
 /* SMC commands */
+#define SMC_CMD(d)             ((d) & 0xff)
 #define SMC_READ_KEY           0x10
 #define SMC_WRITE_KEY          0x11
 #define SMC_GET_KEY_BY_INDEX   0x12
 #define SMC_GET_KEY_INFO       0x13
 #define SMC_GET_SRAM_ADDR      0x17
 #define  SMC_SRAM_SIZE         0x4000
+#define SMC_NOTIFICATION       0x18
 
 /* SMC errors */
 #define SMC_ERROR(d)           ((d) & 0xff)
@@ -79,6 +83,18 @@ struct aplsmc_sensor {
        int             flags;
 };
 
+/* SMC events */
+#define SMC_EV_TYPE(d)         (((d) >> 48) & 0xffff)
+#define SMC_EV_TYPE_BTN                0x7201
+#define SMC_EV_TYPE_LID                0x7203
+#define SMC_EV_SUBTYPE(d)      (((d) >> 40) & 0xff)
+#define SMC_EV_DATA(d)         (((d) >> 32) & 0xff)
+
+/* Button events */
+#define SMC_PWRBTN_OFF         0x00
+#define SMC_PWRBTN_SHORT       0x06
+#define SMC_PWRBTN_LONG                0xfe
+
 #define APLSMC_BE              (1 << 0)
 #define APLSMC_HIDDEN          (1 << 1)
 
@@ -154,6 +170,7 @@ void        aplsmc_callback(void *, uint64_t);
 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);
+int    aplsmc_write_key(struct aplsmc_softc *, uint32_t, void *, size_t);
 void   aplsmc_refresh_sensors(void *);
 int    aplsmc_apminfo(struct apm_power_info *);
 void   aplsmc_set_pin(void *, uint32_t *, int);
@@ -177,6 +194,7 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux)
        struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
        struct fdt_attach_args *faa = aux;
        uint8_t data[SMC_CLKM_LEN];
+       uint8_t ntap;
        int error, node;
 #ifndef SMALL_KERNEL
        int i;
@@ -259,6 +277,10 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux)
                config_mountroot(self, aplsmc_reboot_attachhook);
        }
 
+       /* Enable notifications. */
+       ntap = 1;
+       aplsmc_write_key(sc, SMC_KEY("NTAP"), &ntap, sizeof(ntap));
+
 #ifndef SMALL_KERNEL
 
        for (i = 0; i < nitems(aplsmc_sensors); i++) {
@@ -309,11 +331,53 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux)
 #endif
 }
 
+void
+aplsmc_handle_notification(struct aplsmc_softc *sc, uint64_t data)
+{
+       extern int allowpowerdown;
+
+       switch (SMC_EV_TYPE(data)) {
+       case SMC_EV_TYPE_BTN:
+               switch (SMC_EV_SUBTYPE(data)) {
+               case SMC_PWRBTN_SHORT:
+                       if (SMC_EV_DATA(data) == 1 && allowpowerdown) {
+                               allowpowerdown = 0;
+                               prsignal(initprocess, SIGUSR2);
+                       }
+                       break;
+               case SMC_PWRBTN_LONG:
+                       break;
+               case SMC_PWRBTN_OFF:
+                       /* XXX Do emergency shutdown? */
+                       break;
+               default:
+                       printf("%s: SMV_EV_TYPE_BTN 0x%016llx\n",
+                              sc->sc_dev.dv_xname, data);
+                       break;
+               }
+               break;
+       case SMC_EV_TYPE_LID:
+               /* XXX Handle lid events. */
+               break;
+       default:
+#ifdef APLSMC_DEBUG
+               printf("%s: unhandled event 0x%016llx\n",
+                   sc->sc_dev.dv_xname, data);
+#endif
+               break;
+       }
+}
+
 void
 aplsmc_callback(void *arg, uint64_t data)
 {
        struct aplsmc_softc *sc = arg;
 
+       if (SMC_CMD(data) == SMC_NOTIFICATION) {
+               aplsmc_handle_notification(sc, data);
+               return;
+       }
+
        sc->sc_data = data;
        wakeup(&sc->sc_data);
 }