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