gpio_acpi: Factor out gpio_acpi_check_gpioint().
[dragonfly.git] / sys / bus / gpio / gpio_intel / gpio_cherryview.c
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Imre Vadász <imre@vdsz.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Cherryview GPIO support.
36  */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/errno.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/bus.h>
46
47 #include <sys/rman.h>
48
49 #include "opt_acpi.h"
50 #include "acpi.h"
51 #include <dev/acpica/acpivar.h>
52
53 #include "gpio_intel_var.h"
54
55 #include "gpio_if.h"
56
57 #define CHV_GPIO_REG_IS         0x300
58 #define CHV_GPIO_REG_MASK       0x380
59 #define CHV_GPIO_REG_PINS       0x4400  /* start of pin control registers */
60
61 #define CHV_GPIO_REGOFF_CTL0    0x0
62 #define CHV_GPIO_REGOFF_CTL1    0x4
63
64 #define CHV_GPIO_CTL0_RXSTATE   0x00000001u
65 #define CHV_GPIO_CTL0_TXSTATE   0x00000002u
66 #define CHV_GPIO_CTL0_GPIOCFG_MASK 0x00000700u
67 #define CHV_GPIO_CTL0_GPIOEN    0x00008000u
68 #define CHV_GPIO_CTL0_PULLUP    0x00800000u
69 #define CHV_GPIO_CTL1_INTCFG_MASK 0x00000007u
70 #define CHV_GPIO_CTL1_INVRXDATA 0x00000040u
71
72 #define CHV_GPIO_PINSIZE        0x8     /* 8 bytes for each pin */
73 #define CHV_GPIO_PINCHUNK       15      /* 15 pins at a time */
74 #define CHV_GPIO_PININC         0x400   /* every 0x400 bytes */
75
76 #define PIN_ADDRESS(x)                                  \
77     (CHV_GPIO_REG_PINS +                                \
78      ((x) / CHV_GPIO_PINCHUNK) * CHV_GPIO_PININC +      \
79      ((x) % CHV_GPIO_PINCHUNK) * CHV_GPIO_PINSIZE)
80
81 #define PIN_CTL0(x)             (PIN_ADDRESS(x) + CHV_GPIO_REGOFF_CTL0)
82 #define PIN_CTL1(x)             (PIN_ADDRESS(x) + CHV_GPIO_REGOFF_CTL1)
83
84 static void     gpio_cherryview_init(struct gpio_intel_softc *sc);
85 static void     gpio_cherryview_intr(void *arg);
86 static int      gpio_cherryview_map_intr(struct gpio_intel_softc *sc,
87                     uint16_t pin, int trigger, int polarity, int termination);
88 static void     gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc,
89                     struct pin_intr_map *map);
90 static void     gpio_cherryview_enable_intr(struct gpio_intel_softc *sc,
91                     struct pin_intr_map *map);
92 static void     gpio_cherryview_disable_intr(struct gpio_intel_softc *sc,
93                     struct pin_intr_map *map);
94 static int      gpio_cherryview_read_pin(struct gpio_intel_softc *sc,
95                     uint16_t pin);
96 static void     gpio_cherryview_write_pin(struct gpio_intel_softc *sc,
97                     uint16_t pin, int value);
98
99 static struct gpio_intel_fns gpio_cherryview_fns = {
100         .init = gpio_cherryview_init,
101         .intr = gpio_cherryview_intr,
102         .map_intr = gpio_cherryview_map_intr,
103         .unmap_intr = gpio_cherryview_unmap_intr,
104         .enable_intr = gpio_cherryview_enable_intr,
105         .disable_intr = gpio_cherryview_disable_intr,
106         .read_pin = gpio_cherryview_read_pin,
107         .write_pin = gpio_cherryview_write_pin,
108 };
109
110 /* _UID=1 */
111 static struct pinrange chv_sw_ranges[] = {
112         { 0, 7 },
113         { 15, 22 },
114         { 30, 37 },
115         { 45, 52 },
116         { 60, 67 },
117         { 75, 82 },
118         { 90, 97 },
119         { -1, -1 }
120 };
121
122 /* _UID=2 */
123 static struct pinrange chv_n_ranges[] = {
124         { 0, 8 },
125         { 15, 27 },
126         { 30, 41 },
127         { 45, 56 },
128         { 60, 72 },
129         { -1, -1 }
130 };
131
132 /* _UID=3 */
133 static struct pinrange chv_e_ranges[] = {
134         { 0, 11 },
135         { 15, 26 },
136         { -1, -1 }
137 };
138
139 /* _UID=4 */
140 static struct pinrange chv_se_ranges[] = {
141         { 0, 7 },
142         { 15, 26 },
143         { 30, 35 },
144         { 45, 52 },
145         { 60, 69 },
146         { 75, 85 },
147         { -1, -1 }
148 };
149
150 static struct lock gpio_lk;
151 LOCK_SYSINIT(chvgpiolk, &gpio_lk, "chvgpio", 0);
152
153 /*
154  * Use global GPIO register lock to workaround erratum:
155  *
156  * CHT34    Multiple Drivers That Access the GPIO Registers Concurrently May
157  *          Result in Unpredictable System Behaviour
158  */
159 static inline uint32_t
160 chvgpio_read(struct gpio_intel_softc *sc, bus_size_t offset)
161 {
162         uint32_t val;
163
164         lockmgr(&gpio_lk, LK_EXCLUSIVE);
165         val = bus_read_4(sc->mem_res, offset);
166         lockmgr(&gpio_lk, LK_RELEASE);
167         return val;
168 }
169
170 static inline void
171 chvgpio_write(struct gpio_intel_softc *sc, bus_size_t offset, uint32_t val)
172 {
173         lockmgr(&gpio_lk, LK_EXCLUSIVE);
174         bus_write_4(sc->mem_res, offset, val);
175         lockmgr(&gpio_lk, LK_RELEASE);
176 }
177
178 int
179 gpio_cherryview_matchuid(struct gpio_intel_softc *sc)
180 {
181         ACPI_HANDLE handle;
182
183         handle = acpi_get_handle(sc->dev);
184         if (acpi_MatchUid(handle, "1")) {
185                 sc->ranges = chv_sw_ranges;
186         } else if (acpi_MatchUid(handle, "2")) {
187                 sc->ranges = chv_n_ranges;
188         } else if (acpi_MatchUid(handle, "3")) {
189                 sc->ranges = chv_e_ranges;
190         } else if (acpi_MatchUid(handle, "4")) {
191                 sc->ranges = chv_se_ranges;
192         } else {
193                 return (ENXIO);
194         }
195
196         sc->fns = &gpio_cherryview_fns;
197
198         return (0);
199 }
200
201 static void
202 gpio_cherryview_init(struct gpio_intel_softc *sc)
203 {
204         /* mask and clear all interrupt lines */
205         chvgpio_write(sc, CHV_GPIO_REG_MASK, 0);
206         chvgpio_write(sc, CHV_GPIO_REG_IS, 0xffff);
207 }
208
209 static void
210 gpio_cherryview_intr(void *arg)
211 {
212         struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg;
213         struct pin_intr_map *mapping;
214         uint32_t status;
215         int i;
216
217         status = chvgpio_read(sc, CHV_GPIO_REG_IS);
218         for (i = 0; i < 16; i++) {
219                 if (status & (1U << i)) {
220                         mapping = &sc->intrmaps[i];
221                         if (!mapping->is_level) {
222                                 chvgpio_write(sc, CHV_GPIO_REG_IS,
223                                     (1U << i));
224                         }
225                         if (mapping->pin != -1 && mapping->handler != NULL)
226                                 mapping->handler(mapping->arg);
227                         if (mapping->is_level) {
228                                 chvgpio_write(sc, CHV_GPIO_REG_IS,
229                                     (1U << i));
230                         }
231                 }
232         }
233 }
234
235 /* XXX Add shared/exclusive argument. */
236 static int
237 gpio_cherryview_map_intr(struct gpio_intel_softc *sc, uint16_t pin, int trigger,
238     int polarity, int termination)
239 {
240         uint32_t reg, reg1, reg2;
241         uint32_t intcfg, new_intcfg, gpiocfg, new_gpiocfg;
242         int i;
243
244         reg1 = chvgpio_read(sc, PIN_CTL0(pin));
245         reg2 = chvgpio_read(sc, PIN_CTL1(pin));
246         device_printf(sc->dev,
247             "pin=%d trigger=%d polarity=%d ctrl0=0x%08x ctrl1=0x%08x\n",
248             pin, trigger, polarity, reg1, reg2);
249
250         new_intcfg = intcfg = reg2 & CHV_GPIO_CTL1_INTCFG_MASK;
251         new_gpiocfg = gpiocfg = reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK;
252
253         /*
254          * Sanity Checks, for now we just abort if the configuration doesn't
255          * match our expectations.
256          */
257         if (!(reg1 & CHV_GPIO_CTL0_GPIOEN)) {
258                 device_printf(sc->dev, "GPIO mode is disabled\n");
259                 return (ENXIO);
260         }
261         if (gpiocfg != 0x0 && gpiocfg != 0x200) {
262                 device_printf(sc->dev, "RX is disabled\n");
263                 if (gpiocfg == 0x100)
264                         new_gpiocfg = 0x000;
265                 else if (gpiocfg == 0x300)
266                         new_gpiocfg = 0x200;
267                 else
268                         return (ENXIO);
269         }
270         if (trigger == ACPI_LEVEL_SENSITIVE) {
271                 if (intcfg != 4) {
272                         device_printf(sc->dev,
273                             "trigger is %x, should be 4 (Level)\n", intcfg);
274                         return (ENXIO);
275                 }
276                 if (polarity == ACPI_ACTIVE_BOTH) {
277                         device_printf(sc->dev,
278                             "ACTIVE_BOTH incompatible with level trigger\n");
279                         return (ENXIO);
280                 } else if (polarity == ACPI_ACTIVE_LOW) {
281                         if (!(reg2 & CHV_GPIO_CTL1_INVRXDATA)) {
282                                 device_printf(sc->dev,
283                                     "Invert RX not enabled (needed for "
284                                     "level/low trigger/polarity)\n");
285                                 return (ENXIO);
286                         }
287                 } else {
288                         if (reg2 & CHV_GPIO_CTL1_INVRXDATA) {
289                                 device_printf(sc->dev,
290                                     "Invert RX should not be enabled for "
291                                     "level/high trigger/polarity\n");
292                                 return (ENXIO);
293                         }
294                 }
295         } else {
296                 /*
297                  * For edge-triggered interrupts it's definitely harmless to
298                  * change between rising-edge, falling-edge and both-edges
299                  * triggering.
300                  */
301                 if (polarity == ACPI_ACTIVE_HIGH && intcfg != 2) {
302                         device_printf(sc->dev,
303                             "Wrong interrupt configuration, is 0x%x should "
304                             "be 0x%x\n", intcfg, 2);
305                         if (intcfg == 1 || intcfg == 3)
306                                 new_intcfg = 2;
307                         else
308                                 return (ENXIO);
309                 } else if (polarity == ACPI_ACTIVE_LOW && intcfg != 1) {
310                         device_printf(sc->dev,
311                             "Wrong interrupt configuration, is 0x%x should "
312                             "be 0x%x\n", intcfg, 1);
313                         if (intcfg == 2 || intcfg == 3)
314                                 new_intcfg = 1;
315                         else
316                                 return (ENXIO);
317                 } else if (polarity == ACPI_ACTIVE_BOTH && intcfg != 3) {
318                         device_printf(sc->dev,
319                             "Wrong interrupt configuration, is 0x%x should "
320                             "be 0x%x\n", intcfg, 3);
321                         if (intcfg == 1 || intcfg == 2)
322                                 new_intcfg = 3;
323                         else
324                                 return (ENXIO);
325                 }
326         }
327         if (termination == ACPI_PIN_CONFIG_PULLUP &&
328             !(reg1 & CHV_GPIO_CTL0_PULLUP)) {
329                 device_printf(sc->dev,
330                     "Wrong termination, is pull-down, should be pull-up\n");
331                 return (ENXIO);
332         } else if (termination == ACPI_PIN_CONFIG_PULLDOWN &&
333             (reg1 & CHV_GPIO_CTL0_PULLUP)) {
334                 device_printf(sc->dev,
335                     "Wrong termination, is pull-up, should be pull-down\n");
336                 return (ENXIO);
337         }
338
339         /* Check if the interrupt/line configured by BIOS/UEFI is unused */
340         i = (reg1 >> 28) & 0xf;
341         if (sc->intrmaps[i].pin != -1) {
342                 device_printf(sc->dev, "Interrupt line %d already used\n", i);
343                 return (ENXIO);
344         }
345
346         if (new_intcfg != intcfg) {
347                 device_printf(sc->dev,
348                     "Switching interrupt configuration from 0x%x to 0x%x\n",
349                     intcfg, new_intcfg);
350                 reg = reg2 & ~CHV_GPIO_CTL1_INTCFG_MASK;
351                 reg |= (new_intcfg & CHV_GPIO_CTL1_INTCFG_MASK) << 0;
352                 chvgpio_write(sc, PIN_CTL1(pin), reg);
353         }
354
355         if (new_gpiocfg != gpiocfg) {
356                 device_printf(sc->dev,
357                     "Switching gpio configuration from 0x%x to 0x%x\n",
358                     gpiocfg, new_gpiocfg);
359                 reg = reg1 & ~CHV_GPIO_CTL0_GPIOCFG_MASK;
360                 reg |= (new_gpiocfg & CHV_GPIO_CTL0_GPIOCFG_MASK) << 0;
361                 chvgpio_write(sc, PIN_CTL0(pin), reg);
362         }
363
364         sc->intrmaps[i].pin = pin;
365         sc->intrmaps[i].intidx = i;
366         sc->intrmaps[i].orig_intcfg = intcfg;
367         sc->intrmaps[i].orig_gpiocfg = gpiocfg;
368
369         if (trigger == ACPI_LEVEL_SENSITIVE)
370                 sc->intrmaps[i].is_level = 1;
371         else
372                 sc->intrmaps[i].is_level = 0;
373
374         return (0);
375 }
376
377 static void
378 gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc,
379     struct pin_intr_map *map)
380 {
381         uint32_t reg, intcfg, gpiocfg;
382         uint16_t pin = map->pin;
383
384         intcfg = map->orig_intcfg;
385         intcfg &= CHV_GPIO_CTL1_INTCFG_MASK;
386
387         gpiocfg = map->orig_gpiocfg;
388         gpiocfg &= CHV_GPIO_CTL0_GPIOCFG_MASK;
389
390         map->pin = -1;
391         map->intidx = -1;
392         map->is_level = 0;
393         map->orig_intcfg = 0;
394         map->orig_gpiocfg = 0;
395
396         /* Restore interrupt configuration if needed */
397         reg = chvgpio_read(sc, PIN_CTL1(pin));
398         if ((reg & CHV_GPIO_CTL1_INTCFG_MASK) != intcfg) {
399                 reg &= ~CHV_GPIO_CTL1_INTCFG_MASK;
400                 reg |= intcfg;
401                 chvgpio_write(sc, PIN_CTL1(pin), reg);
402         }
403
404         /* Restore gpio configuration if needed */
405         reg = chvgpio_read(sc, PIN_CTL0(pin));
406         if ((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) != gpiocfg) {
407                 reg &= ~CHV_GPIO_CTL0_GPIOCFG_MASK;
408                 reg |= gpiocfg;
409                 chvgpio_write(sc, PIN_CTL0(pin), reg);
410         }
411 }
412
413 static void
414 gpio_cherryview_enable_intr(struct gpio_intel_softc *sc,
415     struct pin_intr_map *map)
416 {
417         uint32_t reg;
418
419         KKASSERT(map->intidx >= 0);
420
421         /* clear interrupt status flag */
422         chvgpio_write(sc, CHV_GPIO_REG_IS, (1U << map->intidx));
423
424         /* unmask interrupt */
425         reg = chvgpio_read(sc, CHV_GPIO_REG_MASK);
426         reg |= (1U << map->intidx);
427         chvgpio_write(sc, CHV_GPIO_REG_MASK, reg);
428 }
429
430 static void
431 gpio_cherryview_disable_intr(struct gpio_intel_softc *sc,
432     struct pin_intr_map *map)
433 {
434         uint32_t reg;
435
436         KKASSERT(map->intidx >= 0);
437
438         /* mask interrupt line */
439         reg = chvgpio_read(sc, CHV_GPIO_REG_MASK);
440         reg &= ~(1U << map->intidx);
441         chvgpio_write(sc, CHV_GPIO_REG_MASK, reg);
442 }
443
444 static int
445 gpio_cherryview_read_pin(struct gpio_intel_softc *sc, uint16_t pin)
446 {
447         uint32_t reg;
448         int val;
449
450         reg = chvgpio_read(sc, PIN_CTL0(pin));
451         /* Verify that RX is enabled */
452         KKASSERT((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x0 ||
453             (reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x200);
454
455         if (reg & CHV_GPIO_CTL0_RXSTATE)
456                 val = 1;
457         else
458                 val = 0;
459
460         return (val);
461 }
462
463 static void
464 gpio_cherryview_write_pin(struct gpio_intel_softc *sc, uint16_t pin, int value)
465 {
466         uint32_t reg1, reg2;
467
468         reg2 = chvgpio_read(sc, PIN_CTL1(pin));
469         /* Verify that interrupt is disabled */
470         KKASSERT((reg2 & CHV_GPIO_CTL1_INTCFG_MASK) == 0);
471
472         reg1 = chvgpio_read(sc, PIN_CTL0(pin));
473         /* Verify that TX is enabled */
474         KKASSERT((reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0 ||
475             (reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x100);
476
477         if (value)
478                 reg1 |= CHV_GPIO_CTL0_TXSTATE;
479         else
480                 reg1 &= ~CHV_GPIO_CTL0_TXSTATE;
481         chvgpio_write(sc, PIN_CTL0(pin), reg1);
482 }