xref: /openbsd-src/sys/dev/acpi/dwgpio.c (revision c1a45aed656e7d5627c30c92421893a76f370ccb)
1 /*	$OpenBSD: dwgpio.c,v 1.7 2022/04/06 18:59:27 naddy 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_pin_intr(struct dwgpio_softc *, int);
95 int	dwgpio_intr(void *);
96 
97 int
98 dwgpio_match(struct device *parent, void *match, void *aux)
99 {
100 	struct acpi_attach_args *aaa = aux;
101 	struct cfdata *cf = match;
102 
103 	if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1)
104 		return 0;
105 	return acpi_matchhids(aaa, dwgpio_hids, cf->cf_driver->cd_name);
106 }
107 
108 void
109 dwgpio_attach(struct device *parent, struct device *self, void *aux)
110 {
111 	struct acpi_attach_args *aaa = aux;
112 	struct dwgpio_softc *sc = (struct dwgpio_softc *)self;
113 	int i;
114 
115 	sc->sc_acpi = (struct acpi_softc *)parent;
116 	sc->sc_node = aaa->aaa_node;
117 	printf(" %s", sc->sc_node->name);
118 
119 	printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
120 
121 	sc->sc_memt = aaa->aaa_bst[0];
122 	if (bus_space_map(sc->sc_memt, aaa->aaa_addr[0], aaa->aaa_size[0],
123 	    0, &sc->sc_memh)) {
124 		printf(": can't map registers\n");
125 		return;
126 	}
127 
128 	aml_find_node(sc->sc_node, "_HID", dwgpio_found_hid, sc);
129 
130 	if (sc->sc_npins == 0) {
131 		printf(": no pins\n");
132 		return;
133 	}
134 	if (sc->sc_npins >= GPIO_NUM_PINS) {
135 		printf(": too many pins\n");
136 		return;
137 	}
138 
139 	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
140 	    M_DEVBUF, M_WAITOK | M_ZERO);
141 
142 	printf(" irq");
143 
144 	sc->sc_nirq = aaa->aaa_nirq;
145 	sc->sc_ih = mallocarray(sc->sc_nirq, sizeof(*sc->sc_ih),
146 	    M_DEVBUF, M_WAITOK | M_ZERO);
147 	for (i = 0; i < sc->sc_nirq; i++) {
148 		printf(" %d", aaa->aaa_irq[i]);
149 
150 		sc->sc_ih[i] = acpi_intr_establish(aaa->aaa_irq[i],
151 		    aaa->aaa_irq_flags[i], IPL_BIO, dwgpio_intr,
152 		    sc, sc->sc_dev.dv_xname);
153 		if (sc->sc_ih[i] == NULL) {
154 			printf(": can't establish interrupt\n");
155 			goto unmap;
156 		}
157 	}
158 
159 	sc->sc_gpio.cookie = sc;
160 	sc->sc_gpio.read_pin = dwgpio_read_pin;
161 	sc->sc_gpio.write_pin = dwgpio_write_pin;
162 	sc->sc_gpio.intr_establish = dwgpio_intr_establish;
163 	sc->sc_node->gpio = &sc->sc_gpio;
164 
165 	printf(", %d pins\n", sc->sc_npins);
166 
167 	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
168 	return;
169 
170 unmap:
171 	for (i = 0; i < sc->sc_nirq; i++) {
172 		if (sc->sc_ih[i])
173 			acpi_intr_disestablish(sc->sc_ih[i]);
174 	}
175 	free(sc->sc_ih, M_DEVBUF, sc->sc_nirq * sizeof(*sc->sc_ih));
176 	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
177 	bus_space_unmap(sc->sc_memt, sc->sc_memh, aaa->aaa_size[0]);
178 }
179 
180 
181 int
182 dwgpio_found_hid(struct aml_node *child, void *arg)
183 {
184 	struct dwgpio_softc *sc = (struct dwgpio_softc *)arg;
185 	struct aml_node *node = child->parent;
186 	int reg;
187 
188 	/* Skip our own _HID. */
189 	if (node == sc->sc_node)
190 		return 0;
191 
192 	/* Only handle a single port for now. */
193 	reg = acpi_getpropint(node, "reg", -1);
194 	if (reg != 0)
195 		return 0;
196 
197 	sc->sc_npins = acpi_getpropint(node, "snps,nr-gpios", GPIO_NUM_PINS);
198 	node->attached = 1;
199 	return 0;
200 }
201 
202 int
203 dwgpio_read_pin(void *cookie, int pin)
204 {
205 	struct dwgpio_softc *sc = cookie;
206 	uint32_t reg;
207 	int val;
208 
209 	if (pin < 0 || pin >= sc->sc_npins)
210 		return 0;
211 
212 	reg = HREAD4(sc, GPIO_EXT_PORTA);
213 	val = (reg >> pin) & 1;
214 	return val;
215 }
216 
217 void
218 dwgpio_write_pin(void *cookie, int pin, int val)
219 {
220 	struct dwgpio_softc *sc = cookie;
221 
222 	if (pin < 0 || pin >= sc->sc_npins)
223 		return;
224 
225 	if (val)
226 		HSET4(sc, GPIO_SWPORTA_DR, (1 << pin));
227 	else
228 		HCLR4(sc, GPIO_SWPORTA_DR, (1 << pin));
229 }
230 
231 void
232 dwgpio_intr_establish(void *cookie, int pin, int flags,
233     int (*func)(void *), void *arg)
234 {
235 	struct dwgpio_softc *sc = cookie;
236 
237 	if (pin < 0 || pin >= sc->sc_npins)
238 		return;
239 
240 	sc->sc_pin_ih[pin].ih_func = func;
241 	sc->sc_pin_ih[pin].ih_arg = arg;
242 
243 	if ((flags & LR_GPIO_MODE) == LR_GPIO_EDGE)
244 		HSET4(sc, GPIO_INTTYPE_LEVEL, 1 << pin);
245 	else
246 		HCLR4(sc, GPIO_INTTYPE_LEVEL, 1 << pin);
247 	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTHI)
248 		HSET4(sc, GPIO_INT_POLARITY, 1 << pin);
249 	else
250 		HCLR4(sc, GPIO_INT_POLARITY, 1 << pin);
251 
252 	HCLR4(sc, GPIO_SWPORTA_DDR, 1 << pin);
253 	HSET4(sc, GPIO_INTEN, 1 << pin);
254 	HCLR4(sc, GPIO_INTMASK, 1 << pin);
255 }
256 
257 int
258 dwgpio_intr(void *arg)
259 {
260 	struct dwgpio_softc *sc = arg;
261 	uint32_t status;
262 	int pin, handled = 0;
263 
264 	status = HREAD4(sc, GPIO_INT_STATUS);
265 	HWRITE4(sc, GPIO_PORTS_EOI, status);
266 
267 	for (pin = 0; pin < sc->sc_npins; pin++) {
268 		if ((status & (1 << pin)) && sc->sc_pin_ih[pin].ih_func) {
269 			sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
270 			handled = 1;
271 		}
272 	}
273 
274 	return handled;
275 }
276