gpio_intel: Handle level triggered interrupts more correctly.
[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                     void *arg, driver_intr_t *handler);
89 static void     gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc,
90                     uint16_t pin);
91 static int      gpio_cherryview_read_pin(struct gpio_intel_softc *sc,
92                     uint16_t pin);
93 static void     gpio_cherryview_write_pin(struct gpio_intel_softc *sc,
94                     uint16_t pin, int value);
95
96 static struct gpio_intel_fns gpio_cherryview_fns = {
97         .init = gpio_cherryview_init,
98         .intr = gpio_cherryview_intr,
99         .map_intr = gpio_cherryview_map_intr,
100         .unmap_intr = gpio_cherryview_unmap_intr,
101         .read_pin = gpio_cherryview_read_pin,
102         .write_pin = gpio_cherryview_write_pin,
103 };
104
105 /* _UID=1 */
106 static struct pinrange chv_sw_ranges[] = {
107         { 0, 7 },
108         { 15, 22 },
109         { 30, 37 },
110         { 45, 52 },
111         { 60, 67 },
112         { 75, 82 },
113         { 90, 97 },
114         { -1, -1 }
115 };
116
117 /* _UID=2 */
118 static struct pinrange chv_n_ranges[] = {
119         { 0, 8 },
120         { 15, 27 },
121         { 30, 41 },
122         { 45, 56 },
123         { 60, 72 },
124         { -1, -1 }
125 };
126
127 /* _UID=3 */
128 static struct pinrange chv_e_ranges[] = {
129         { 0, 11 },
130         { 15, 26 },
131         { -1, -1 }
132 };
133
134 /* _UID=4 */
135 static struct pinrange chv_se_ranges[] = {
136         { 0, 7 },
137         { 15, 26 },
138         { 30, 35 },
139         { 45, 52 },
140         { 60, 69 },
141         { 75, 85 },
142         { -1, -1 }
143 };
144
145 int
146 gpio_cherryview_matchuid(struct gpio_intel_softc *sc)
147 {
148         ACPI_HANDLE handle;
149
150         handle = acpi_get_handle(sc->dev);
151         if (acpi_MatchUid(handle, "1")) {
152                 sc->ranges = chv_sw_ranges;
153         } else if (acpi_MatchUid(handle, "2")) {
154                 sc->ranges = chv_n_ranges;
155         } else if (acpi_MatchUid(handle, "3")) {
156                 sc->ranges = chv_e_ranges;
157         } else if (acpi_MatchUid(handle, "4")) {
158                 sc->ranges = chv_se_ranges;
159         } else {
160                 return (ENXIO);
161         }
162
163         sc->fns = &gpio_cherryview_fns;
164
165         return (0);
166 }
167
168 static void
169 gpio_cherryview_init(struct gpio_intel_softc *sc)
170 {
171         /* mask and clear all interrupt lines */
172         bus_write_4(sc->mem_res, CHV_GPIO_REG_MASK, 0);
173         bus_write_4(sc->mem_res, CHV_GPIO_REG_IS, 0xffff);
174 }
175
176 static void
177 gpio_cherryview_intr(void *arg)
178 {
179         struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg;
180         struct pin_intr_map *mapping;
181         uint32_t status;
182         int i;
183
184         status = bus_read_4(sc->mem_res, CHV_GPIO_REG_IS);
185         for (i = 0; i < 16; i++) {
186                 if (status & (1U << i)) {
187                         mapping = &sc->intrmaps[i];
188                         if (!mapping->is_level) {
189                                 bus_write_4(sc->mem_res, CHV_GPIO_REG_IS,
190                                     (1U << i));
191                         }
192                         if (mapping->pin != -1 && mapping->handler != NULL)
193                                 mapping->handler(mapping->arg);
194                         if (mapping->is_level) {
195                                 bus_write_4(sc->mem_res, CHV_GPIO_REG_IS,
196                                     (1U << i));
197                         }
198                 }
199         }
200 }
201
202 /* XXX Add shared/exclusive argument. */
203 static int
204 gpio_cherryview_map_intr(struct gpio_intel_softc *sc, uint16_t pin, int trigger,
205     int polarity, int termination, void *arg, driver_intr_t *handler)
206 {
207         uint32_t reg, reg1, reg2;
208         uint32_t intcfg, new_intcfg, gpiocfg, new_gpiocfg;
209         int i;
210
211         reg1 = bus_read_4(sc->mem_res, PIN_CTL0(pin));
212         reg2 = bus_read_4(sc->mem_res, PIN_CTL1(pin));
213         device_printf(sc->dev,
214             "pin=%d trigger=%d polarity=%d ctrl0=0x%08x ctrl1=0x%08x\n",
215             pin, trigger, polarity, reg1, reg2);
216
217         new_intcfg = intcfg = reg2 & CHV_GPIO_CTL1_INTCFG_MASK;
218         new_gpiocfg = gpiocfg = reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK;
219
220         /*
221          * Sanity Checks, for now we just abort if the configuration doesn't
222          * match our expectations.
223          */
224         if (!(reg1 & CHV_GPIO_CTL0_GPIOEN)) {
225                 device_printf(sc->dev, "GPIO mode is disabled\n");
226                 return (ENXIO);
227         }
228         if (gpiocfg != 0x0 && gpiocfg != 0x200) {
229                 device_printf(sc->dev, "RX is disabled\n");
230                 if (gpiocfg == 0x100)
231                         new_gpiocfg = 0x000;
232                 else if (gpiocfg == 0x300)
233                         new_gpiocfg = 0x200;
234                 else
235                         return (ENXIO);
236         }
237         if (trigger == ACPI_LEVEL_SENSITIVE) {
238                 if (intcfg != 4) {
239                         device_printf(sc->dev,
240                             "trigger is %x, should be 4 (Level)\n", intcfg);
241                         return (ENXIO);
242                 }
243                 if (polarity == ACPI_ACTIVE_BOTH) {
244                         device_printf(sc->dev,
245                             "ACTIVE_BOTH incompatible with level trigger\n");
246                         return (ENXIO);
247                 } else if (polarity == ACPI_ACTIVE_LOW) {
248                         if (!(reg2 & CHV_GPIO_CTL1_INVRXDATA)) {
249                                 device_printf(sc->dev,
250                                     "Invert RX not enabled (needed for "
251                                     "level/low trigger/polarity)\n");
252                                 return (ENXIO);
253                         }
254                 } else {
255                         if (reg2 & CHV_GPIO_CTL1_INVRXDATA) {
256                                 device_printf(sc->dev,
257                                     "Invert RX should not be enabled for "
258                                     "level/high trigger/polarity\n");
259                                 return (ENXIO);
260                         }
261                 }
262         } else {
263                 /*
264                  * For edge-triggered interrupts it's definitely harmless to
265                  * change between rising-edge, falling-edge and both-edges
266                  * triggering.
267                  */
268                 if (polarity == ACPI_ACTIVE_HIGH && intcfg != 2) {
269                         device_printf(sc->dev,
270                             "Wrong interrupt configuration, is 0x%x should "
271                             "be 0x%x\n", intcfg, 2);
272                         if (intcfg == 1 || intcfg == 3)
273                                 new_intcfg = 2;
274                         else
275                                 return (ENXIO);
276                 } else if (polarity == ACPI_ACTIVE_LOW && intcfg != 1) {
277                         device_printf(sc->dev,
278                             "Wrong interrupt configuration, is 0x%x should "
279                             "be 0x%x\n", intcfg, 1);
280                         if (intcfg == 2 || intcfg == 3)
281                                 new_intcfg = 1;
282                         else
283                                 return (ENXIO);
284                 } else if (polarity == ACPI_ACTIVE_BOTH && intcfg != 3) {
285                         device_printf(sc->dev,
286                             "Wrong interrupt configuration, is 0x%x should "
287                             "be 0x%x\n", intcfg, 3);
288                         if (intcfg == 1 || intcfg == 2)
289                                 new_intcfg = 3;
290                         else
291                                 return (ENXIO);
292                 }
293         }
294         if (termination == ACPI_PIN_CONFIG_PULLUP &&
295             !(reg1 & CHV_GPIO_CTL0_PULLUP)) {
296                 device_printf(sc->dev,
297                     "Wrong termination, is pull-down, should be pull-up\n");
298                 return (ENXIO);
299         } else if (termination == ACPI_PIN_CONFIG_PULLDOWN &&
300             (reg1 & CHV_GPIO_CTL0_PULLUP)) {
301                 device_printf(sc->dev,
302                     "Wrong termination, is pull-up, should be pull-down\n");
303                 return (ENXIO);
304         }
305
306         /* Check if the interrupt/line configured by BIOS/UEFI is unused */
307         i = (reg1 >> 28) & 0xf;
308         if (sc->intrmaps[i].pin != -1) {
309                 device_printf(sc->dev, "Interrupt line %d already used\n", i);
310                 return (ENXIO);
311         }
312
313         if (new_intcfg != intcfg) {
314                 device_printf(sc->dev,
315                     "Switching interrupt configuration from 0x%x to 0x%x\n",
316                     intcfg, new_intcfg);
317                 reg = reg2 & ~CHV_GPIO_CTL1_INTCFG_MASK;
318                 reg |= (new_intcfg & CHV_GPIO_CTL1_INTCFG_MASK) << 0;
319                 bus_write_4(sc->mem_res, PIN_CTL1(pin), reg);
320         }
321
322         if (new_gpiocfg != gpiocfg) {
323                 device_printf(sc->dev,
324                     "Switching gpio configuration from 0x%x to 0x%x\n",
325                     gpiocfg, new_gpiocfg);
326                 reg = reg1 & ~CHV_GPIO_CTL0_GPIOCFG_MASK;
327                 reg |= (new_gpiocfg & CHV_GPIO_CTL0_GPIOCFG_MASK) << 0;
328                 bus_write_4(sc->mem_res, PIN_CTL0(pin), reg);
329         }
330
331         sc->intrmaps[i].pin = pin;
332         sc->intrmaps[i].arg = arg;
333         sc->intrmaps[i].handler = handler;
334         sc->intrmaps[i].orig_intcfg = intcfg;
335         sc->intrmaps[i].orig_gpiocfg = gpiocfg;
336
337         if (trigger == ACPI_LEVEL_SENSITIVE)
338                 sc->intrmaps[i].is_level = 1;
339         else
340                 sc->intrmaps[i].is_level = 0;
341
342         /* unmask interrupt */
343         reg = bus_read_4(sc->mem_res, CHV_GPIO_REG_MASK);
344         reg |= (1 << i);
345         bus_write_4(sc->mem_res, CHV_GPIO_REG_MASK, reg);
346
347         return (0);
348 }
349
350 static void
351 gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc, uint16_t pin)
352 {
353         uint32_t reg, intcfg, gpiocfg;
354         int i;
355
356         for (i = 0; i < 16; i++) {
357                 if (sc->intrmaps[i].pin == pin) {
358                         intcfg = sc->intrmaps[i].orig_intcfg;
359                         intcfg &= CHV_GPIO_CTL1_INTCFG_MASK;
360
361                         gpiocfg = sc->intrmaps[i].orig_gpiocfg;
362                         gpiocfg &= CHV_GPIO_CTL0_GPIOCFG_MASK;
363
364                         sc->intrmaps[i].pin = -1;
365                         sc->intrmaps[i].arg = NULL;
366                         sc->intrmaps[i].handler = NULL;
367                         sc->intrmaps[i].is_level = 0;
368                         sc->intrmaps[i].orig_intcfg = 0;
369                         sc->intrmaps[i].orig_gpiocfg = 0;
370
371                         /* mask interrupt line */
372                         reg = bus_read_4(sc->mem_res, CHV_GPIO_REG_MASK);
373                         reg &= ~(1 << i);
374                         bus_write_4(sc->mem_res, CHV_GPIO_REG_MASK, reg);
375
376                         /* Restore interrupt configuration if needed */
377                         reg = bus_read_4(sc->mem_res, PIN_CTL1(pin));
378                         if ((reg & CHV_GPIO_CTL1_INTCFG_MASK) != intcfg) {
379                                 reg &= ~CHV_GPIO_CTL1_INTCFG_MASK;
380                                 reg |= intcfg;
381                                 bus_write_4(sc->mem_res, PIN_CTL1(pin), reg);
382                         }
383
384                         /* Restore gpio configuration if needed */
385                         reg = bus_read_4(sc->mem_res, PIN_CTL0(pin));
386                         if ((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) != gpiocfg) {
387                                 reg &= ~CHV_GPIO_CTL0_GPIOCFG_MASK;
388                                 reg |= gpiocfg;
389                                 bus_write_4(sc->mem_res, PIN_CTL0(pin), reg);
390                         }
391                 }
392         }
393 }
394
395 static int
396 gpio_cherryview_read_pin(struct gpio_intel_softc *sc, uint16_t pin)
397 {
398         uint32_t reg;
399         int val;
400
401         reg = bus_read_4(sc->mem_res, PIN_CTL0(pin));
402         /* Verify that RX is enabled */
403         KKASSERT((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x0 ||
404             (reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x200);
405
406         if (reg & CHV_GPIO_CTL0_RXSTATE)
407                 val = 1;
408         else
409                 val = 0;
410
411         return (val);
412 }
413
414 static void
415 gpio_cherryview_write_pin(struct gpio_intel_softc *sc, uint16_t pin, int value)
416 {
417         uint32_t reg1, reg2;
418
419         reg2 = bus_read_4(sc->mem_res, PIN_CTL1(pin));
420         /* Verify that interrupt is disabled */
421         KKASSERT((reg2 & CHV_GPIO_CTL1_INTCFG_MASK) == 0);
422
423         reg1 = bus_read_4(sc->mem_res, PIN_CTL0(pin));
424         /* Verify that TX is enabled */
425         KKASSERT((reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0 ||
426             (reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x100);
427
428         if (value)
429                 reg1 |= CHV_GPIO_CTL0_TXSTATE;
430         else
431                 reg1 &= ~CHV_GPIO_CTL0_TXSTATE;
432         bus_write_4(sc->mem_res, PIN_CTL0(pin), reg1);
433 }