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