xref: /openbsd-src/sys/dev/acpi/dwgpio.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1 /*	$OpenBSD: dwgpio.c,v 1.8 2024/05/13 01:15:50 jsg Exp $	*/
2 /*
3  * Copyright (c) 2020 Mark Kettenis
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 
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/acpidev.h>
25 #include <dev/acpi/amltypes.h>
26 #include <dev/acpi/dsdt.h>
27 
28 /* Registers. */
29 #define GPIO_SWPORTA_DR		0x0000
30 #define GPIO_SWPORTA_DDR	0x0004
31 #define GPIO_INTEN		0x0030
32 #define GPIO_INTTYPE_LEVEL	0x0034
33 #define GPIO_INT_POLARITY	0x0038
34 #define GPIO_INT_STATUS		0x003c
35 #define GPIO_PORTS_EOI		0x0040
36 #define GPIO_INTMASK		0x0044
37 #define GPIO_DEBOUNCE		0x0048
38 #define GPIO_EXT_PORTA		0x0050
39 
40 #define GPIO_NUM_PORTS		4
41 #define GPIO_NUM_PINS		32
42 
43 #define HREAD4(sc, reg)							\
44 	(bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (reg)))
45 #define HWRITE4(sc, reg, val)						\
46 	bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (reg), (val))
47 #define HSET4(sc, reg, bits)						\
48 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
49 #define HCLR4(sc, reg, bits)						\
50 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
51 
52 struct dwgpio_intrhand {
53 	int (*ih_func)(void *);
54 	void *ih_arg;
55 };
56 
57 struct dwgpio_softc {
58 	struct device		sc_dev;
59 	struct acpi_softc	*sc_acpi;
60 	struct aml_node		*sc_node;
61 
62 	bus_space_tag_t		sc_memt;
63 	bus_space_handle_t	sc_memh;
64 
65 	int			sc_nirq;
66 	void			**sc_ih;
67 
68 	uint32_t		sc_npins;
69 	struct dwgpio_intrhand	*sc_pin_ih;
70 
71 	struct acpi_gpio sc_gpio;
72 };
73 
74 int	dwgpio_match(struct device *, void *, void *);
75 void	dwgpio_attach(struct device *, struct device *, void *);
76 
77 const struct cfattach dwgpio_ca = {
78 	sizeof(struct dwgpio_softc), dwgpio_match, dwgpio_attach
79 };
80 
81 struct cfdriver dwgpio_cd = {
82 	NULL, "dwgpio", DV_DULL
83 };
84 
85 const char *dwgpio_hids[] = {
86 	"APMC0D81",
87 	NULL
88 };
89 
90 int	dwgpio_found_hid(struct aml_node *, void *);
91 int	dwgpio_read_pin(void *, int);
92 void	dwgpio_write_pin(void *, int, int);
93 void	dwgpio_intr_establish(void *, int, int, int (*)(void *), void *);
94 int	dwgpio_intr(void *);
95 
96 int
dwgpio_match(struct device * parent,void * match,void * aux)97 dwgpio_match(struct device *parent, void *match, void *aux)
98 {
99 	struct acpi_attach_args *aaa = aux;
100 	struct cfdata *cf = match;
101 
102 	if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1)
103 		return 0;
104 	return acpi_matchhids(aaa, dwgpio_hids, cf->cf_driver->cd_name);
105 }
106 
107 void
dwgpio_attach(struct device * parent,struct device * self,void * aux)108 dwgpio_attach(struct device *parent, struct device *self, void *aux)
109 {
110 	struct acpi_attach_args *aaa = aux;
111 	struct dwgpio_softc *sc = (struct dwgpio_softc *)self;
112 	int i;
113 
114 	sc->sc_acpi = (struct acpi_softc *)parent;
115 	sc->sc_node = aaa->aaa_node;
116 	printf(" %s", sc->sc_node->name);
117 
118 	printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
119 
120 	sc->sc_memt = aaa->aaa_bst[0];
121 	if (bus_space_map(sc->sc_memt, aaa->aaa_addr[0], aaa->aaa_size[0],
122 	    0, &sc->sc_memh)) {
123 		printf(": can't map registers\n");
124 		return;
125 	}
126 
127 	aml_find_node(sc->sc_node, "_HID", dwgpio_found_hid, sc);
128 
129 	if (sc->sc_npins == 0) {
130 		printf(": no pins\n");
131 		return;
132 	}
133 	if (sc->sc_npins >= GPIO_NUM_PINS) {
134 		printf(": too many pins\n");
135 		return;
136 	}
137 
138 	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
139 	    M_DEVBUF, M_WAITOK | M_ZERO);
140 
141 	printf(" irq");
142 
143 	sc->sc_nirq = aaa->aaa_nirq;
144 	sc->sc_ih = mallocarray(sc->sc_nirq, sizeof(*sc->sc_ih),
145 	    M_DEVBUF, M_WAITOK | M_ZERO);
146 	for (i = 0; i < sc->sc_nirq; i++) {
147 		printf(" %d", aaa->aaa_irq[i]);
148 
149 		sc->sc_ih[i] = acpi_intr_establish(aaa->aaa_irq[i],
150 		    aaa->aaa_irq_flags[i], IPL_BIO, dwgpio_intr,
151 		    sc, sc->sc_dev.dv_xname);
152 		if (sc->sc_ih[i] == NULL) {
153 			printf(": can't establish interrupt\n");
154 			goto unmap;
155 		}
156 	}
157 
158 	sc->sc_gpio.cookie = sc;
159 	sc->sc_gpio.read_pin = dwgpio_read_pin;
160 	sc->sc_gpio.write_pin = dwgpio_write_pin;
161 	sc->sc_gpio.intr_establish = dwgpio_intr_establish;
162 	sc->sc_node->gpio = &sc->sc_gpio;
163 
164 	printf(", %d pins\n", sc->sc_npins);
165 
166 	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
167 	return;
168 
169 unmap:
170 	for (i = 0; i < sc->sc_nirq; i++) {
171 		if (sc->sc_ih[i])
172 			acpi_intr_disestablish(sc->sc_ih[i]);
173 	}
174 	free(sc->sc_ih, M_DEVBUF, sc->sc_nirq * sizeof(*sc->sc_ih));
175 	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
176 	bus_space_unmap(sc->sc_memt, sc->sc_memh, aaa->aaa_size[0]);
177 }
178 
179 
180 int
dwgpio_found_hid(struct aml_node * child,void * arg)181 dwgpio_found_hid(struct aml_node *child, void *arg)
182 {
183 	struct dwgpio_softc *sc = (struct dwgpio_softc *)arg;
184 	struct aml_node *node = child->parent;
185 	int reg;
186 
187 	/* Skip our own _HID. */
188 	if (node == sc->sc_node)
189 		return 0;
190 
191 	/* Only handle a single port for now. */
192 	reg = acpi_getpropint(node, "reg", -1);
193 	if (reg != 0)
194 		return 0;
195 
196 	sc->sc_npins = acpi_getpropint(node, "snps,nr-gpios", GPIO_NUM_PINS);
197 	node->attached = 1;
198 	return 0;
199 }
200 
201 int
dwgpio_read_pin(void * cookie,int pin)202 dwgpio_read_pin(void *cookie, int pin)
203 {
204 	struct dwgpio_softc *sc = cookie;
205 	uint32_t reg;
206 	int val;
207 
208 	if (pin < 0 || pin >= sc->sc_npins)
209 		return 0;
210 
211 	reg = HREAD4(sc, GPIO_EXT_PORTA);
212 	val = (reg >> pin) & 1;
213 	return val;
214 }
215 
216 void
dwgpio_write_pin(void * cookie,int pin,int val)217 dwgpio_write_pin(void *cookie, int pin, int val)
218 {
219 	struct dwgpio_softc *sc = cookie;
220 
221 	if (pin < 0 || pin >= sc->sc_npins)
222 		return;
223 
224 	if (val)
225 		HSET4(sc, GPIO_SWPORTA_DR, (1 << pin));
226 	else
227 		HCLR4(sc, GPIO_SWPORTA_DR, (1 << pin));
228 }
229 
230 void
dwgpio_intr_establish(void * cookie,int pin,int flags,int (* func)(void *),void * arg)231 dwgpio_intr_establish(void *cookie, int pin, int flags,
232     int (*func)(void *), void *arg)
233 {
234 	struct dwgpio_softc *sc = cookie;
235 
236 	if (pin < 0 || pin >= sc->sc_npins)
237 		return;
238 
239 	sc->sc_pin_ih[pin].ih_func = func;
240 	sc->sc_pin_ih[pin].ih_arg = arg;
241 
242 	if ((flags & LR_GPIO_MODE) == LR_GPIO_EDGE)
243 		HSET4(sc, GPIO_INTTYPE_LEVEL, 1 << pin);
244 	else
245 		HCLR4(sc, GPIO_INTTYPE_LEVEL, 1 << pin);
246 	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTHI)
247 		HSET4(sc, GPIO_INT_POLARITY, 1 << pin);
248 	else
249 		HCLR4(sc, GPIO_INT_POLARITY, 1 << pin);
250 
251 	HCLR4(sc, GPIO_SWPORTA_DDR, 1 << pin);
252 	HSET4(sc, GPIO_INTEN, 1 << pin);
253 	HCLR4(sc, GPIO_INTMASK, 1 << pin);
254 }
255 
256 int
dwgpio_intr(void * arg)257 dwgpio_intr(void *arg)
258 {
259 	struct dwgpio_softc *sc = arg;
260 	uint32_t status;
261 	int pin, handled = 0;
262 
263 	status = HREAD4(sc, GPIO_INT_STATUS);
264 	HWRITE4(sc, GPIO_PORTS_EOI, status);
265 
266 	for (pin = 0; pin < sc->sc_npins; pin++) {
267 		if ((status & (1 << pin)) && sc->sc_pin_ih[pin].ih_func) {
268 			sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
269 			handled = 1;
270 		}
271 	}
272 
273 	return handled;
274 }
275