1 /* $OpenBSD: qcpon.c,v 1.4 2023/04/24 14:34:13 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 OF_is_compatible(saa->sa_node, "qcom,pmk8350-pon")); 68 } 69 70 void 71 qcpon_attach(struct device *parent, struct device *self, void *aux) 72 { 73 struct spmi_attach_args *saa = aux; 74 struct qcpon_softc *sc = (struct qcpon_softc *)self; 75 int node; 76 77 sc->sc_node = saa->sa_node; 78 sc->sc_tag = saa->sa_tag; 79 sc->sc_sid = saa->sa_sid; 80 81 task_set(&sc->sc_powerdown_task, qcpon_powerdown_task, sc); 82 83 printf("\n"); 84 85 for (node = OF_child(saa->sa_node); node; node = OF_peer(node)) { 86 if (OF_is_compatible(node, "qcom,pmk8350-pwrkey")) { 87 sc->sc_pwrkey_ih = fdt_intr_establish(node, 88 IPL_BIO | IPL_WAKEUP, qcpon_pwrkey_intr, sc, 89 sc->sc_dev.dv_xname); 90 if (sc->sc_pwrkey_ih == NULL) { 91 printf("%s: can't establish interrupt\n", 92 sc->sc_dev.dv_xname); 93 continue; 94 } 95 #ifdef SUSPEND 96 device_register_wakeup(&sc->sc_dev); 97 #endif 98 } 99 } 100 } 101 102 int 103 qcpon_pwrkey_intr(void *arg) 104 { 105 struct qcpon_softc *sc = arg; 106 #ifdef SUSPEND 107 extern int cpu_suspended; 108 #endif 109 110 /* Ignore presses, handle releases. */ 111 sc->sc_pwrkey_debounce = (sc->sc_pwrkey_debounce + 1) % 2; 112 if (sc->sc_pwrkey_debounce == 1) 113 return 1; 114 115 #ifdef SUSPEND 116 if (cpu_suspended) 117 cpu_suspended = 0; 118 else 119 #endif 120 task_add(systq, &sc->sc_powerdown_task); 121 122 return 1; 123 } 124 125 void 126 qcpon_powerdown_task(void *arg) 127 { 128 extern int allowpowerdown; 129 130 if (allowpowerdown == 1) { 131 allowpowerdown = 0; 132 prsignal(initprocess, SIGUSR2); 133 } 134 } 135