xref: /openbsd/sys/dev/fdt/qcpon.c (revision 5ecd7399)
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