xref: /openbsd-src/sys/dev/acpi/aplgpio.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: aplgpio.c,v 1.1 2019/06/17 18:28:18 patrick Exp $	*/
2 /*
3  * Copyright (c) 2016 Mark Kettenis
4  * Copyright (c) 2019 James Hastings
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/malloc.h>
21 #include <sys/systm.h>
22 
23 #include <dev/acpi/acpireg.h>
24 #include <dev/acpi/acpivar.h>
25 #include <dev/acpi/acpidev.h>
26 #include <dev/acpi/amltypes.h>
27 #include <dev/acpi/dsdt.h>
28 
29 #define APLGPIO_CONF_TXSTATE	0x00000001
30 #define APLGPIO_CONF_RXSTATE	0x00000002
31 #define APLGPIO_CONF_RXINV	0x00800000
32 #define APLGPIO_CONF_RXEV_EDGE	0x02000000
33 #define APLGPIO_CONF_RXEV_ZERO	0x04000000
34 #define APLGPIO_CONF_RXEV_MASK	0x06000000
35 
36 #define APLGPIO_IRQ_STS		0x100
37 #define APLGPIO_IRQ_EN		0x110
38 #define APLGPIO_PAD_CFG0	0x500
39 
40 struct aplgpio_intrhand {
41 	int (*ih_func)(void *);
42 	void *ih_arg;
43 };
44 
45 struct aplgpio_softc {
46 	struct device sc_dev;
47 	struct acpi_softc *sc_acpi;
48 	struct aml_node *sc_node;
49 
50 	bus_space_tag_t sc_memt;
51 	bus_space_handle_t sc_memh;
52 	bus_addr_t sc_addr;
53 	bus_size_t sc_size;
54 
55 	int sc_irq;
56 	int sc_irq_flags;
57 	void *sc_ih;
58 
59 	int sc_npins;
60 	struct aplgpio_intrhand *sc_pin_ih;
61 
62 	struct acpi_gpio sc_gpio;
63 };
64 
65 int	aplgpio_match(struct device *, void *, void *);
66 void	aplgpio_attach(struct device *, struct device *, void *);
67 
68 struct cfattach aplgpio_ca = {
69 	sizeof(struct aplgpio_softc), aplgpio_match, aplgpio_attach
70 };
71 
72 struct cfdriver aplgpio_cd = {
73 	NULL, "aplgpio", DV_DULL
74 };
75 
76 const char *aplgpio_hids[] = {
77 	"INT3452",
78 	NULL
79 };
80 
81 int	aplgpio_parse_resources(int, union acpi_resource *, void *);
82 int	aplgpio_read_pin(void *, int);
83 void	aplgpio_write_pin(void *, int, int);
84 void	aplgpio_intr_establish(void *, int, int, int (*)(), void *);
85 int	aplgpio_intr(void *);
86 
87 int
88 aplgpio_match(struct device *parent, void *match, void *aux)
89 {
90 	struct acpi_attach_args *aaa = aux;
91 	struct cfdata *cf = match;
92 
93 	return acpi_matchhids(aaa, aplgpio_hids, cf->cf_driver->cd_name);
94 }
95 
96 void
97 aplgpio_attach(struct device *parent, struct device *self, void *aux)
98 {
99 	struct acpi_attach_args *aaa = aux;
100 	struct aplgpio_softc *sc = (struct aplgpio_softc *)self;
101 	struct aml_value res;
102 	int64_t uid;
103 	int i;
104 
105 	sc->sc_acpi = (struct acpi_softc *)parent;
106 	sc->sc_node = aaa->aaa_node;
107 	printf(": %s", sc->sc_node->name);
108 
109 	if (aml_evalinteger(sc->sc_acpi, sc->sc_node, "_UID", 0, NULL, &uid)) {
110 		printf(", can't find uid\n");
111 		return;
112 	}
113 
114 	printf(" uid %lld", uid);
115 
116 	switch (uid) {
117 	case 1:
118 		sc->sc_npins = 78;
119 		break;
120 	case 2:
121 		sc->sc_npins = 77;
122 		break;
123 	case 3:
124 		sc->sc_npins = 47;
125 		break;
126 	case 4:
127 		sc->sc_npins = 43;
128 		break;
129 	default:
130 		printf("\n");
131 		return;
132 	}
133 
134 	if (aml_evalname(sc->sc_acpi, sc->sc_node, "_CRS", 0, NULL, &res)) {
135 		printf(", can't find registers\n");
136 		return;
137 	}
138 
139 	aml_parse_resource(&res, aplgpio_parse_resources, sc);
140 	printf(" addr 0x%lx/0x%lx", sc->sc_addr, sc->sc_size);
141 	if (sc->sc_addr == 0 || sc->sc_size == 0) {
142 		printf("\n");
143 		return;
144 	}
145 	aml_freevalue(&res);
146 
147 	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
148 	    M_DEVBUF, M_NOWAIT | M_ZERO);
149 	if (sc->sc_pin_ih == NULL) {
150 		printf("\n");
151 		return;
152 	}
153 
154 	printf(" irq %d", sc->sc_irq);
155 
156 	sc->sc_memt = aaa->aaa_memt;
157 	if (bus_space_map(sc->sc_memt, sc->sc_addr, sc->sc_size, 0,
158 	    &sc->sc_memh)) {
159 		printf(", can't map registers\n");
160 		goto free;
161 	}
162 
163 	sc->sc_ih = acpi_intr_establish(sc->sc_irq, sc->sc_irq_flags, IPL_BIO,
164 	    aplgpio_intr, sc, sc->sc_dev.dv_xname);
165 	if (sc->sc_ih == NULL) {
166 		printf(", can't establish interrupt\n");
167 		goto unmap;
168 	}
169 
170 	sc->sc_gpio.cookie = sc;
171 	sc->sc_gpio.read_pin = aplgpio_read_pin;
172 	sc->sc_gpio.write_pin = aplgpio_write_pin;
173 	sc->sc_gpio.intr_establish = aplgpio_intr_establish;
174 	sc->sc_node->gpio = &sc->sc_gpio;
175 
176 	/* Mask and clear all interrupts. */
177 	for (i = 0; i < sc->sc_npins; i++) {
178 		if (i % 32 == 0) {
179 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
180 			    APLGPIO_IRQ_EN + (i / 32) * 4, 0x00000000);
181 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
182 			    APLGPIO_IRQ_STS + (i / 32) * 4, 0xffffffff);
183 		}
184 	}
185 
186 	printf(", %d pins\n", sc->sc_npins);
187 
188 	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
189 	return;
190 
191 unmap:
192 	bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size);
193 free:
194 	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
195 }
196 
197 int
198 aplgpio_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
199 {
200 	struct aplgpio_softc *sc = arg;
201 	int type = AML_CRSTYPE(crs);
202 
203 	switch (type) {
204 	case LR_MEM32FIXED:
205 		sc->sc_addr = crs->lr_m32fixed._bas;
206 		sc->sc_size = crs->lr_m32fixed._len;
207 		break;
208 	case LR_EXTIRQ:
209 		sc->sc_irq = crs->lr_extirq.irq[0];
210 		sc->sc_irq_flags = crs->lr_extirq.flags;
211 		break;
212 	default:
213 		printf(" type 0x%x\n", type);
214 		break;
215 	}
216 
217 	return 0;
218 }
219 
220 int
221 aplgpio_read_pin(void *cookie, int pin)
222 {
223 	struct aplgpio_softc *sc = cookie;
224 	uint32_t reg;
225 
226 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
227 	    APLGPIO_PAD_CFG0 + pin * 8);
228 
229 	return !!(reg & APLGPIO_CONF_RXSTATE);
230 }
231 
232 void
233 aplgpio_write_pin(void *cookie, int pin, int value)
234 {
235 	struct aplgpio_softc *sc = cookie;
236 	uint32_t reg;
237 
238 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
239 	    APLGPIO_PAD_CFG0 + pin * 8);
240 	if (value)
241 		reg |= APLGPIO_CONF_TXSTATE;
242 	else
243 		reg &= ~APLGPIO_CONF_TXSTATE;
244 	bus_space_write_4(sc->sc_memt, sc->sc_memh,
245 	    APLGPIO_PAD_CFG0 + pin * 8, reg);
246 }
247 
248 void
249 aplgpio_intr_establish(void *cookie, int pin, int flags,
250     int (*func)(void *), void *arg)
251 {
252 	struct aplgpio_softc *sc = cookie;
253 	uint32_t reg;
254 
255 	KASSERT(pin >= 0 && pin < sc->sc_npins);
256 
257 	sc->sc_pin_ih[pin].ih_func = func;
258 	sc->sc_pin_ih[pin].ih_arg = arg;
259 
260 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
261 	    APLGPIO_PAD_CFG0 + pin * 8);
262 	reg &= ~(APLGPIO_CONF_RXEV_MASK | APLGPIO_CONF_RXINV);
263 	if ((flags & LR_GPIO_MODE) == 1)
264 		reg |= APLGPIO_CONF_RXEV_EDGE;
265 	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO)
266 		reg |= APLGPIO_CONF_RXINV;
267 	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH)
268 		reg |= APLGPIO_CONF_RXEV_EDGE | APLGPIO_CONF_RXEV_ZERO;
269 	bus_space_write_4(sc->sc_memt, sc->sc_memh,
270 	    APLGPIO_PAD_CFG0 + pin * 8, reg);
271 
272 	reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
273 	    APLGPIO_IRQ_EN + (pin / 32) * 4);
274 	reg |= (1 << (pin % 32));
275 	bus_space_write_4(sc->sc_memt, sc->sc_memh,
276 	    APLGPIO_IRQ_EN + (pin / 32) * 4, reg);
277 }
278 
279 int
280 aplgpio_intr(void *arg)
281 {
282 	struct aplgpio_softc *sc = arg;
283 	uint32_t status, enable;
284 	int rc = 0;
285 	int pin;
286 
287 	for (pin = 0; pin < sc->sc_npins; pin++) {
288 		if (pin % 32 == 0) {
289 			status = bus_space_read_4(sc->sc_memt, sc->sc_memh,
290 			    APLGPIO_IRQ_STS + (pin / 32) * 4);
291 			bus_space_write_4(sc->sc_memt, sc->sc_memh,
292 			    APLGPIO_IRQ_STS + (pin / 32) * 4, status);
293 			enable = bus_space_read_4(sc->sc_memt, sc->sc_memh,
294 			    APLGPIO_IRQ_EN + (pin / 32) * 4);
295 			status &= enable;
296 		}
297 		if (status & (1 << (pin % 32))) {
298 			if (sc->sc_pin_ih[pin].ih_func)
299 				sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
300 			rc = 1;
301 		}
302 	}
303 	return rc;
304 }
305