Line data Source code
1 : /* $OpenBSD: chvgpio.c,v 1.8 2017/11/29 22:51:01 kettenis Exp $ */
2 : /*
3 : * Copyright (c) 2016 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 : #define CHVGPIO_INTERRUPT_STATUS 0x0300
29 : #define CHVGPIO_INTERRUPT_MASK 0x0380
30 : #define CHVGPIO_PAD_CFG0 0x4400
31 : #define CHVGPIO_PAD_CFG1 0x4404
32 :
33 : #define CHVGPIO_PAD_CFG0_GPIORXSTATE 0x00000001
34 : #define CHVGPIO_PAD_CFG0_GPIOTXSTATE 0x00000002
35 : #define CHVGPIO_PAD_CFG0_INTSEL_MASK 0xf0000000
36 : #define CHVGPIO_PAD_CFG0_INTSEL_SHIFT 28
37 :
38 : #define CHVGPIO_PAD_CFG1_INTWAKECFG_MASK 0x00000007
39 : #define CHVGPIO_PAD_CFG1_INTWAKECFG_FALLING 0x00000001
40 : #define CHVGPIO_PAD_CFG1_INTWAKECFG_RISING 0x00000002
41 : #define CHVGPIO_PAD_CFG1_INTWAKECFG_BOTH 0x00000003
42 : #define CHVGPIO_PAD_CFG1_INTWAKECFG_LEVEL 0x00000004
43 : #define CHVGPIO_PAD_CFG1_INVRXTX_MASK 0x000000f0
44 : #define CHVGPIO_PAD_CFG1_INVRXTX_RXDATA 0x00000040
45 :
46 : /* OEM defined RegionSpace. */
47 : #define CHVGPIO_REGIONSPACE_BASE 0x90
48 :
49 : struct chvgpio_intrhand {
50 : int (*ih_func)(void *);
51 : void *ih_arg;
52 : };
53 :
54 : struct chvgpio_softc {
55 : struct device sc_dev;
56 : struct acpi_softc *sc_acpi;
57 : struct aml_node *sc_node;
58 :
59 : bus_space_tag_t sc_memt;
60 : bus_space_handle_t sc_memh;
61 : bus_addr_t sc_addr;
62 : bus_size_t sc_size;
63 :
64 : int sc_irq;
65 : int sc_irq_flags;
66 : void *sc_ih;
67 :
68 : const int *sc_pins;
69 : int sc_npins;
70 : int sc_ngroups;
71 :
72 : struct chvgpio_intrhand sc_pin_ih[16];
73 :
74 : struct acpi_gpio sc_gpio;
75 : };
76 :
77 : static inline int
78 0 : chvgpio_pad_cfg0_offset(int pin)
79 : {
80 0 : return (CHVGPIO_PAD_CFG0 + 1024 * (pin / 15) + 8 * (pin % 15));
81 : }
82 :
83 : static inline int
84 0 : chvgpio_read_pad_cfg0(struct chvgpio_softc *sc, int pin)
85 : {
86 0 : return bus_space_read_4(sc->sc_memt, sc->sc_memh,
87 : chvgpio_pad_cfg0_offset(pin));
88 : }
89 :
90 : static inline void
91 0 : chvgpio_write_pad_cfg0(struct chvgpio_softc *sc, int pin, uint32_t val)
92 : {
93 0 : bus_space_write_4(sc->sc_memt, sc->sc_memh,
94 : chvgpio_pad_cfg0_offset(pin), val);
95 0 : }
96 :
97 : static inline int
98 0 : chvgpio_read_pad_cfg1(struct chvgpio_softc *sc, int pin)
99 : {
100 0 : return bus_space_read_4(sc->sc_memt, sc->sc_memh,
101 : chvgpio_pad_cfg0_offset(pin) + 4);
102 : }
103 :
104 : static inline void
105 0 : chvgpio_write_pad_cfg1(struct chvgpio_softc *sc, int pin, uint32_t val)
106 : {
107 0 : bus_space_write_4(sc->sc_memt, sc->sc_memh,
108 : chvgpio_pad_cfg0_offset(pin) + 4, val);
109 0 : }
110 :
111 : int chvgpio_match(struct device *, void *, void *);
112 : void chvgpio_attach(struct device *, struct device *, void *);
113 :
114 : struct cfattach chvgpio_ca = {
115 : sizeof(struct chvgpio_softc), chvgpio_match, chvgpio_attach
116 : };
117 :
118 : struct cfdriver chvgpio_cd = {
119 : NULL, "chvgpio", DV_DULL
120 : };
121 :
122 : const char *chvgpio_hids[] = {
123 : "INT33FF",
124 : NULL
125 : };
126 :
127 : /*
128 : * The pads for the pins are arranged in groups of maximal 15 pins.
129 : * The arrays below give the number of pins per group, such that we
130 : * can validate the (untrusted) pin numbers from ACPI.
131 : */
132 :
133 : const int chv_southwest_pins[] = {
134 : 8, 8, 8, 8, 8, 8, 8, -1
135 : };
136 :
137 : const int chv_north_pins[] = {
138 : 9, 13, 12, 12, 13, -1
139 : };
140 :
141 : const int chv_east_pins[] = {
142 : 12, 12, -1
143 : };
144 :
145 : const int chv_southeast_pins[] = {
146 : 8, 12, 6, 8, 10, 11, -1
147 : };
148 :
149 : int chvgpio_parse_resources(int, union acpi_resource *, void *);
150 : int chvgpio_check_pin(struct chvgpio_softc *, int);
151 : int chvgpio_read_pin(void *, int);
152 : void chvgpio_write_pin(void *, int, int);
153 : void chvgpio_intr_establish(void *, int, int, int (*)(), void *);
154 : int chvgpio_intr(void *);
155 : int chvgpio_opreg_handler(void *, int, uint64_t, int, uint64_t *);
156 :
157 : int
158 0 : chvgpio_match(struct device *parent, void *match, void *aux)
159 : {
160 0 : struct acpi_attach_args *aaa = aux;
161 0 : struct cfdata *cf = match;
162 :
163 0 : return acpi_matchhids(aaa, chvgpio_hids, cf->cf_driver->cd_name);
164 : }
165 :
166 : void
167 0 : chvgpio_attach(struct device *parent, struct device *self, void *aux)
168 : {
169 0 : struct acpi_attach_args *aaa = aux;
170 0 : struct chvgpio_softc *sc = (struct chvgpio_softc *)self;
171 0 : struct aml_value res;
172 0 : int64_t uid;
173 : int i;
174 :
175 0 : sc->sc_acpi = (struct acpi_softc *)parent;
176 0 : sc->sc_node = aaa->aaa_node;
177 0 : printf(": %s", sc->sc_node->name);
178 :
179 0 : if (aml_evalinteger(sc->sc_acpi, sc->sc_node, "_UID", 0, NULL, &uid)) {
180 0 : printf(", can't find uid\n");
181 0 : return;
182 : }
183 :
184 0 : printf(" uid %lld", uid);
185 :
186 0 : switch (uid) {
187 : case 1:
188 0 : sc->sc_pins = chv_southwest_pins;
189 0 : break;
190 : case 2:
191 0 : sc->sc_pins = chv_north_pins;
192 0 : break;
193 : case 3:
194 0 : sc->sc_pins = chv_east_pins;
195 0 : break;
196 : case 4:
197 0 : sc->sc_pins = chv_southeast_pins;
198 0 : break;
199 : default:
200 0 : printf("\n");
201 0 : return;
202 : }
203 :
204 0 : for (i = 0; sc->sc_pins[i] >= 0; i++) {
205 0 : sc->sc_npins += sc->sc_pins[i];
206 0 : sc->sc_ngroups++;
207 : }
208 :
209 0 : if (aml_evalname(sc->sc_acpi, sc->sc_node, "_CRS", 0, NULL, &res)) {
210 0 : printf(", can't find registers\n");
211 0 : return;
212 : }
213 :
214 0 : aml_parse_resource(&res, chvgpio_parse_resources, sc);
215 0 : printf(" addr 0x%lx/0x%lx", sc->sc_addr, sc->sc_size);
216 0 : if (sc->sc_addr == 0 || sc->sc_size == 0) {
217 0 : printf("\n");
218 0 : return;
219 : }
220 0 : aml_freevalue(&res);
221 :
222 0 : printf(" irq %d", sc->sc_irq);
223 :
224 0 : sc->sc_memt = aaa->aaa_memt;
225 0 : if (bus_space_map(sc->sc_memt, sc->sc_addr, sc->sc_size, 0,
226 0 : &sc->sc_memh)) {
227 0 : printf(", can't map registers\n");
228 0 : return;
229 : }
230 :
231 0 : sc->sc_ih = acpi_intr_establish(sc->sc_irq, sc->sc_irq_flags, IPL_BIO,
232 0 : chvgpio_intr, sc, sc->sc_dev.dv_xname);
233 0 : if (sc->sc_ih == NULL) {
234 0 : printf(", can't establish interrupt\n");
235 : goto unmap;
236 : }
237 :
238 0 : sc->sc_gpio.cookie = sc;
239 0 : sc->sc_gpio.read_pin = chvgpio_read_pin;
240 0 : sc->sc_gpio.write_pin = chvgpio_write_pin;
241 0 : sc->sc_gpio.intr_establish = chvgpio_intr_establish;
242 0 : sc->sc_node->gpio = &sc->sc_gpio;
243 :
244 : /* Mask and ack all interrupts. */
245 0 : bus_space_write_4(sc->sc_memt, sc->sc_memh,
246 : CHVGPIO_INTERRUPT_MASK, 0);
247 0 : bus_space_write_4(sc->sc_memt, sc->sc_memh,
248 : CHVGPIO_INTERRUPT_STATUS, 0xffff);
249 :
250 0 : printf(", %d pins\n", sc->sc_npins);
251 :
252 : /* Register OEM defined address space. */
253 0 : aml_register_regionspace(sc->sc_node, CHVGPIO_REGIONSPACE_BASE + uid,
254 : sc, chvgpio_opreg_handler);
255 :
256 0 : acpi_register_gpio(sc->sc_acpi, sc->sc_node);
257 0 : return;
258 :
259 : unmap:
260 0 : bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size);
261 0 : }
262 :
263 : int
264 0 : chvgpio_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
265 : {
266 0 : struct chvgpio_softc *sc = arg;
267 0 : int type = AML_CRSTYPE(crs);
268 :
269 0 : switch (type) {
270 : case LR_MEM32FIXED:
271 0 : sc->sc_addr = crs->lr_m32fixed._bas;
272 0 : sc->sc_size = crs->lr_m32fixed._len;
273 0 : break;
274 : case LR_EXTIRQ:
275 0 : sc->sc_irq = crs->lr_extirq.irq[0];
276 0 : sc->sc_irq_flags = crs->lr_extirq.flags;
277 0 : break;
278 : default:
279 0 : printf(" type 0x%x\n", type);
280 0 : break;
281 : }
282 :
283 0 : return 0;
284 : }
285 :
286 : int
287 0 : chvgpio_check_pin(struct chvgpio_softc *sc, int pin)
288 : {
289 0 : if (pin < 0)
290 0 : return EINVAL;
291 0 : if ((pin / 15) >= sc->sc_ngroups)
292 0 : return EINVAL;
293 0 : if ((pin % 15) >= sc->sc_pins[pin / 15])
294 0 : return EINVAL;
295 :
296 0 : return 0;
297 0 : }
298 :
299 : int
300 0 : chvgpio_read_pin(void *cookie, int pin)
301 : {
302 0 : struct chvgpio_softc *sc = cookie;
303 : uint32_t reg;
304 :
305 0 : KASSERT(chvgpio_check_pin(sc, pin) == 0);
306 :
307 0 : reg = chvgpio_read_pad_cfg0(sc, pin);
308 0 : return (reg & CHVGPIO_PAD_CFG0_GPIORXSTATE);
309 : }
310 :
311 : void
312 0 : chvgpio_write_pin(void *cookie, int pin, int value)
313 : {
314 0 : struct chvgpio_softc *sc = cookie;
315 : uint32_t reg;
316 :
317 0 : KASSERT(chvgpio_check_pin(sc, pin) == 0);
318 :
319 0 : reg = chvgpio_read_pad_cfg0(sc, pin);
320 0 : if (value)
321 0 : reg |= CHVGPIO_PAD_CFG0_GPIOTXSTATE;
322 : else
323 0 : reg &= ~CHVGPIO_PAD_CFG0_GPIOTXSTATE;
324 0 : chvgpio_write_pad_cfg0(sc, pin, reg);
325 0 : }
326 :
327 : void
328 0 : chvgpio_intr_establish(void *cookie, int pin, int flags,
329 : int (*func)(void *), void *arg)
330 : {
331 0 : struct chvgpio_softc *sc = cookie;
332 : uint32_t reg;
333 : int line;
334 :
335 0 : KASSERT(chvgpio_check_pin(sc, pin) == 0);
336 :
337 0 : reg = chvgpio_read_pad_cfg0(sc, pin);
338 0 : reg &= CHVGPIO_PAD_CFG0_INTSEL_MASK;
339 0 : line = reg >> CHVGPIO_PAD_CFG0_INTSEL_SHIFT;
340 :
341 0 : sc->sc_pin_ih[line].ih_func = func;
342 0 : sc->sc_pin_ih[line].ih_arg = arg;
343 :
344 0 : reg = chvgpio_read_pad_cfg1(sc, pin);
345 0 : reg &= ~CHVGPIO_PAD_CFG1_INTWAKECFG_MASK;
346 0 : reg &= ~CHVGPIO_PAD_CFG1_INVRXTX_MASK;
347 0 : switch (flags & (LR_GPIO_MODE | LR_GPIO_POLARITY)) {
348 : case LR_GPIO_LEVEL | LR_GPIO_ACTLO:
349 0 : reg |= CHVGPIO_PAD_CFG1_INVRXTX_RXDATA;
350 : /* FALLTHROUGH */
351 : case LR_GPIO_LEVEL | LR_GPIO_ACTHI:
352 0 : reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_LEVEL;
353 0 : break;
354 : case LR_GPIO_EDGE | LR_GPIO_ACTLO:
355 0 : reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_FALLING;
356 0 : break;
357 : case LR_GPIO_EDGE | LR_GPIO_ACTHI:
358 0 : reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_RISING;
359 0 : break;
360 : case LR_GPIO_EDGE | LR_GPIO_ACTBOTH:
361 0 : reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_BOTH;
362 0 : break;
363 : default:
364 0 : printf("%s: unsupported interrupt mode/polarity\n",
365 0 : sc->sc_dev.dv_xname);
366 0 : break;
367 : }
368 0 : chvgpio_write_pad_cfg1(sc, pin, reg);
369 :
370 0 : reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
371 : CHVGPIO_INTERRUPT_MASK);
372 0 : bus_space_write_4(sc->sc_memt, sc->sc_memh,
373 : CHVGPIO_INTERRUPT_MASK, reg | (1 << line));
374 0 : }
375 :
376 : int
377 0 : chvgpio_intr(void *arg)
378 : {
379 0 : struct chvgpio_softc *sc = arg;
380 : uint32_t reg;
381 : int rc = 0;
382 : int line;
383 :
384 0 : reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
385 : CHVGPIO_INTERRUPT_STATUS);
386 0 : for (line = 0; line < 16; line++) {
387 0 : if ((reg & (1 << line)) == 0)
388 : continue;
389 :
390 0 : bus_space_write_4(sc->sc_memt,sc->sc_memh,
391 : CHVGPIO_INTERRUPT_STATUS, 1 << line);
392 0 : if (sc->sc_pin_ih[line].ih_func)
393 0 : sc->sc_pin_ih[line].ih_func(sc->sc_pin_ih[line].ih_arg);
394 : rc = 1;
395 0 : }
396 :
397 0 : return rc;
398 : }
399 :
400 : int
401 0 : chvgpio_opreg_handler(void *cookie, int iodir, uint64_t address, int size,
402 : uint64_t *value)
403 : {
404 0 : struct chvgpio_softc *sc = cookie;
405 :
406 : /* Only allow 32-bit access. */
407 0 : if (size != 4 || address > sc->sc_size - size)
408 0 : return -1;
409 :
410 0 : if (iodir == ACPI_IOREAD)
411 0 : *value = bus_space_read_4(sc->sc_memt, sc->sc_memh, address);
412 : else
413 0 : bus_space_write_4(sc->sc_memt, sc->sc_memh, address, *value);
414 :
415 0 : return 0;
416 0 : }
|