xref: /openbsd-src/sys/dev/fdt/qcpon.c (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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 
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 	    OF_is_compatible(saa->sa_node, "qcom,pmk8350-pon"));
68 }
69 
70 void
71 qcpon_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 
102 int
103 qcpon_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 
125 void
126 qcpon_powerdown_task(void *arg)
127 {
128 	extern int allowpowerdown;
129 
130 	if (allowpowerdown == 1) {
131 		allowpowerdown = 0;
132 		prsignal(initprocess, SIGUSR2);
133 	}
134 }
135