1 /* $OpenBSD: qcpon.c,v 1.3 2022/12/21 23:26:54 patrick Exp $ */ 2 /* 3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/malloc.h> 20 #include <sys/systm.h> 21 #include <sys/task.h> 22 #include <sys/proc.h> 23 #include <sys/signalvar.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/fdt/spmivar.h> 29 30 #include <dev/ofw/openfirm.h> 31 #include <dev/ofw/ofw_clock.h> 32 #include <dev/ofw/ofw_power.h> 33 #include <dev/ofw/fdt.h> 34 35 struct qcpon_softc { 36 struct device sc_dev; 37 int sc_node; 38 39 spmi_tag_t sc_tag; 40 int8_t sc_sid; 41 42 void *sc_pwrkey_ih; 43 int sc_pwrkey_debounce; 44 struct task sc_powerdown_task; 45 }; 46 47 int qcpon_match(struct device *, void *, void *); 48 void qcpon_attach(struct device *, struct device *, void *); 49 50 int qcpon_pwrkey_intr(void *); 51 void qcpon_powerdown_task(void *); 52 53 const struct cfattach qcpon_ca = { 54 sizeof(struct qcpon_softc), qcpon_match, qcpon_attach 55 }; 56 57 struct cfdriver qcpon_cd = { 58 NULL, "qcpon", DV_DULL 59 }; 60 61 int 62 qcpon_match(struct device *parent, void *match, void *aux) 63 { 64 struct spmi_attach_args *saa = aux; 65 66 return OF_is_compatible(saa->sa_node, "qcom,pm8998-pon"); 67 } 68 69 void 70 qcpon_attach(struct device *parent, struct device *self, void *aux) 71 { 72 struct spmi_attach_args *saa = aux; 73 struct qcpon_softc *sc = (struct qcpon_softc *)self; 74 int node, reg; 75 76 reg = OF_getpropint(saa->sa_node, "reg", -1); 77 if (reg < 0) { 78 printf(": can't find registers\n"); 79 return; 80 } 81 82 sc->sc_node = saa->sa_node; 83 sc->sc_tag = saa->sa_tag; 84 sc->sc_sid = saa->sa_sid; 85 86 task_set(&sc->sc_powerdown_task, qcpon_powerdown_task, sc); 87 88 printf("\n"); 89 90 for (node = OF_child(saa->sa_node); node; node = OF_peer(node)) { 91 if (OF_is_compatible(node, "qcom,pmk8350-pwrkey")) { 92 sc->sc_pwrkey_ih = fdt_intr_establish(node, 93 IPL_BIO | IPL_WAKEUP, qcpon_pwrkey_intr, sc, 94 sc->sc_dev.dv_xname); 95 if (sc->sc_pwrkey_ih == NULL) { 96 printf("%s: can't establish interrupt\n", 97 sc->sc_dev.dv_xname); 98 continue; 99 } 100 #ifdef SUSPEND 101 device_register_wakeup(&sc->sc_dev); 102 #endif 103 } 104 } 105 } 106 107 int 108 qcpon_pwrkey_intr(void *arg) 109 { 110 struct qcpon_softc *sc = arg; 111 #ifdef SUSPEND 112 extern int cpu_suspended; 113 #endif 114 115 /* Ignore presses, handle releases. */ 116 sc->sc_pwrkey_debounce = (sc->sc_pwrkey_debounce + 1) % 2; 117 if (sc->sc_pwrkey_debounce == 1) 118 return 1; 119 120 #ifdef SUSPEND 121 if (cpu_suspended) 122 cpu_suspended = 0; 123 else 124 #endif 125 task_add(systq, &sc->sc_powerdown_task); 126 127 return 1; 128 } 129 130 void 131 qcpon_powerdown_task(void *arg) 132 { 133 extern int allowpowerdown; 134 135 if (allowpowerdown == 1) { 136 allowpowerdown = 0; 137 prsignal(initprocess, SIGUSR2); 138 } 139 } 140