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