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 35struct 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 47int qcpon_match(struct device *, void *, void *); 48void qcpon_attach(struct device *, struct device *, void *); 49 50int qcpon_pwrkey_intr(void *); 51void qcpon_powerdown_task(void *); 52 53const struct cfattach qcpon_ca = { 54 sizeof(struct qcpon_softc), qcpon_match, qcpon_attach 55}; 56 57struct cfdriver qcpon_cd = { 58 NULL, "qcpon", DV_DULL 59}; 60 61int 62qcpon_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 70void 71qcpon_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 102int 103qcpon_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 125void 126qcpon_powerdown_task(void *arg) 127{ 128 extern int allowpowerdown; 129 130 if (allowpowerdown == 1) { 131 allowpowerdown = 0; 132 prsignal(initprocess, SIGUSR2); 133 } 134} 135