1*5ecd7399Skettenis /* $OpenBSD: qcpon.c,v 1.6 2025/01/03 14:14:49 kettenis 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/fdt.h>
32c756b8eeSpatrick
3347e19253Skettenis /* Registers. */
3447e19253Skettenis #define PON_RT_STS 0x10
3547e19253Skettenis #define PON_PMK8350_KPDPWR_N_SET (1U << 7)
3647e19253Skettenis
37c756b8eeSpatrick struct qcpon_softc {
38c756b8eeSpatrick struct device sc_dev;
39c756b8eeSpatrick int sc_node;
40c756b8eeSpatrick
41c756b8eeSpatrick spmi_tag_t sc_tag;
42c756b8eeSpatrick int8_t sc_sid;
4347e19253Skettenis uint16_t sc_addr;
44c756b8eeSpatrick
45c756b8eeSpatrick void *sc_pwrkey_ih;
4647e19253Skettenis uint32_t sc_last_sts;
47c756b8eeSpatrick struct task sc_powerdown_task;
48c756b8eeSpatrick };
49c756b8eeSpatrick
50c756b8eeSpatrick int qcpon_match(struct device *, void *, void *);
51c756b8eeSpatrick void qcpon_attach(struct device *, struct device *, void *);
52c756b8eeSpatrick
53c756b8eeSpatrick int qcpon_pwrkey_intr(void *);
54c756b8eeSpatrick void qcpon_powerdown_task(void *);
55c756b8eeSpatrick
56c756b8eeSpatrick const struct cfattach qcpon_ca = {
57c756b8eeSpatrick sizeof(struct qcpon_softc), qcpon_match, qcpon_attach
58c756b8eeSpatrick };
59c756b8eeSpatrick
60c756b8eeSpatrick struct cfdriver qcpon_cd = {
61c756b8eeSpatrick NULL, "qcpon", DV_DULL
62c756b8eeSpatrick };
63c756b8eeSpatrick
64c756b8eeSpatrick int
qcpon_match(struct device * parent,void * match,void * aux)65c756b8eeSpatrick qcpon_match(struct device *parent, void *match, void *aux)
66c756b8eeSpatrick {
67c756b8eeSpatrick struct spmi_attach_args *saa = aux;
68c756b8eeSpatrick
69a639195eSpatrick return (OF_is_compatible(saa->sa_node, "qcom,pm8998-pon") ||
70a639195eSpatrick OF_is_compatible(saa->sa_node, "qcom,pmk8350-pon"));
71c756b8eeSpatrick }
72c756b8eeSpatrick
73c756b8eeSpatrick void
qcpon_attach(struct device * parent,struct device * self,void * aux)74c756b8eeSpatrick qcpon_attach(struct device *parent, struct device *self, void *aux)
75c756b8eeSpatrick {
76c756b8eeSpatrick struct spmi_attach_args *saa = aux;
77c756b8eeSpatrick struct qcpon_softc *sc = (struct qcpon_softc *)self;
7847e19253Skettenis uint32_t reg[2];
79a639195eSpatrick int node;
80c756b8eeSpatrick
8147e19253Skettenis if (OF_getpropintarray(saa->sa_node, "reg",
8247e19253Skettenis reg, sizeof(reg)) != sizeof(reg)) {
8347e19253Skettenis printf(": can't find registers\n");
8447e19253Skettenis return;
8547e19253Skettenis }
8647e19253Skettenis
87c756b8eeSpatrick sc->sc_node = saa->sa_node;
88c756b8eeSpatrick sc->sc_tag = saa->sa_tag;
89c756b8eeSpatrick sc->sc_sid = saa->sa_sid;
9047e19253Skettenis sc->sc_addr = reg[0];
91c756b8eeSpatrick
92c756b8eeSpatrick task_set(&sc->sc_powerdown_task, qcpon_powerdown_task, sc);
93c756b8eeSpatrick
94c756b8eeSpatrick printf("\n");
95c756b8eeSpatrick
96c756b8eeSpatrick for (node = OF_child(saa->sa_node); node; node = OF_peer(node)) {
97c756b8eeSpatrick if (OF_is_compatible(node, "qcom,pmk8350-pwrkey")) {
980f49d0faSpatrick sc->sc_pwrkey_ih = fdt_intr_establish(node,
990f49d0faSpatrick IPL_BIO | IPL_WAKEUP, qcpon_pwrkey_intr, sc,
1000f49d0faSpatrick sc->sc_dev.dv_xname);
101c756b8eeSpatrick if (sc->sc_pwrkey_ih == NULL) {
102c756b8eeSpatrick printf("%s: can't establish interrupt\n",
103c756b8eeSpatrick sc->sc_dev.dv_xname);
104c756b8eeSpatrick continue;
105c756b8eeSpatrick }
1060f49d0faSpatrick #ifdef SUSPEND
1070f49d0faSpatrick device_register_wakeup(&sc->sc_dev);
1080f49d0faSpatrick #endif
109c756b8eeSpatrick }
110c756b8eeSpatrick }
111c756b8eeSpatrick }
112c756b8eeSpatrick
113c756b8eeSpatrick int
qcpon_pwrkey_intr(void * arg)114c756b8eeSpatrick qcpon_pwrkey_intr(void *arg)
115c756b8eeSpatrick {
116c756b8eeSpatrick struct qcpon_softc *sc = arg;
1170f49d0faSpatrick #ifdef SUSPEND
1180f49d0faSpatrick extern int cpu_suspended;
1190f49d0faSpatrick #endif
12047e19253Skettenis uint32_t sts;
12147e19253Skettenis int error;
1222da80933Spatrick
1230f49d0faSpatrick #ifdef SUSPEND
12447e19253Skettenis if (cpu_suspended) {
1250f49d0faSpatrick cpu_suspended = 0;
12647e19253Skettenis return 1;
12747e19253Skettenis }
1280f49d0faSpatrick #endif
12947e19253Skettenis
13047e19253Skettenis error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL,
13147e19253Skettenis sc->sc_addr + PON_RT_STS, &sts, sizeof(sts));
13247e19253Skettenis if (error)
13347e19253Skettenis return 0;
13447e19253Skettenis
13547e19253Skettenis /* Ignore presses, handle releases. */
13647e19253Skettenis if ((sc->sc_last_sts & PON_PMK8350_KPDPWR_N_SET) &&
13747e19253Skettenis (sts & PON_PMK8350_KPDPWR_N_SET) == 0)
138c756b8eeSpatrick task_add(systq, &sc->sc_powerdown_task);
1390f49d0faSpatrick
14047e19253Skettenis sc->sc_last_sts = sts;
141c756b8eeSpatrick return 1;
142c756b8eeSpatrick }
143c756b8eeSpatrick
144c756b8eeSpatrick void
qcpon_powerdown_task(void * arg)145c756b8eeSpatrick qcpon_powerdown_task(void *arg)
146c756b8eeSpatrick {
147c756b8eeSpatrick extern int allowpowerdown;
148c756b8eeSpatrick
149c756b8eeSpatrick if (allowpowerdown == 1) {
150c756b8eeSpatrick allowpowerdown = 0;
151c756b8eeSpatrick prsignal(initprocess, SIGUSR2);
152c756b8eeSpatrick }
153c756b8eeSpatrick }
154