2 * Copyright (c) 2016 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Imre Vadász <imre@vdsz.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 * Cherryview GPIO support.
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>
44 #include <sys/mutex.h>
51 #include <dev/acpica/acpivar.h>
53 #include "gpio_intel_var.h"
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 */
61 #define CHV_GPIO_REGOFF_CTL0 0x0
62 #define CHV_GPIO_REGOFF_CTL1 0x4
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
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 */
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)
81 #define PIN_CTL0(x) (PIN_ADDRESS(x) + CHV_GPIO_REGOFF_CTL0)
82 #define PIN_CTL1(x) (PIN_ADDRESS(x) + CHV_GPIO_REGOFF_CTL1)
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,
96 static void gpio_cherryview_write_pin(struct gpio_intel_softc *sc,
97 uint16_t pin, int value);
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,
111 static struct pinrange chv_sw_ranges[] = {
123 static struct pinrange chv_n_ranges[] = {
133 static struct pinrange chv_e_ranges[] = {
140 static struct pinrange chv_se_ranges[] = {
150 static struct lock gpio_lk;
151 LOCK_SYSINIT(chvgpiolk, &gpio_lk, "chvgpio", 0);
154 * Use global GPIO register lock to workaround erratum:
156 * CHT34 Multiple Drivers That Access the GPIO Registers Concurrently May
157 * Result in Unpredictable System Behaviour
159 static inline uint32_t
160 chvgpio_read(struct gpio_intel_softc *sc, bus_size_t offset)
164 lockmgr(&gpio_lk, LK_EXCLUSIVE);
165 val = bus_read_4(sc->mem_res, offset);
166 lockmgr(&gpio_lk, LK_RELEASE);
171 chvgpio_write(struct gpio_intel_softc *sc, bus_size_t offset, uint32_t val)
173 lockmgr(&gpio_lk, LK_EXCLUSIVE);
174 bus_write_4(sc->mem_res, offset, val);
175 lockmgr(&gpio_lk, LK_RELEASE);
179 gpio_cherryview_matchuid(struct gpio_intel_softc *sc)
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;
196 sc->fns = &gpio_cherryview_fns;
202 gpio_cherryview_init(struct gpio_intel_softc *sc)
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);
210 gpio_cherryview_intr(void *arg)
212 struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg;
213 struct pin_intr_map *mapping;
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,
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,
235 /* XXX Add shared/exclusive argument. */
237 gpio_cherryview_map_intr(struct gpio_intel_softc *sc, uint16_t pin, int trigger,
238 int polarity, int termination)
240 uint32_t reg, reg1, reg2;
241 uint32_t intcfg, new_intcfg, gpiocfg, new_gpiocfg;
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);
250 new_intcfg = intcfg = reg2 & CHV_GPIO_CTL1_INTCFG_MASK;
251 new_gpiocfg = gpiocfg = reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK;
254 * Sanity Checks, for now we just abort if the configuration doesn't
255 * match our expectations.
257 if (!(reg1 & CHV_GPIO_CTL0_GPIOEN)) {
258 device_printf(sc->dev, "GPIO mode is disabled\n");
261 if (gpiocfg != 0x0 && gpiocfg != 0x200) {
262 device_printf(sc->dev, "RX is disabled\n");
263 if (gpiocfg == 0x100)
265 else if (gpiocfg == 0x300)
270 if (trigger == ACPI_LEVEL_SENSITIVE) {
272 device_printf(sc->dev,
273 "trigger is %x, should be 4 (Level)\n", intcfg);
276 if (polarity == ACPI_ACTIVE_BOTH) {
277 device_printf(sc->dev,
278 "ACTIVE_BOTH incompatible with level trigger\n");
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");
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");
297 * For edge-triggered interrupts it's definitely harmless to
298 * change between rising-edge, falling-edge and both-edges
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)
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)
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)
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");
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");
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);
346 if (new_intcfg != intcfg) {
347 device_printf(sc->dev,
348 "Switching interrupt configuration from 0x%x to 0x%x\n",
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);
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);
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;
369 if (trigger == ACPI_LEVEL_SENSITIVE)
370 sc->intrmaps[i].is_level = 1;
372 sc->intrmaps[i].is_level = 0;
378 gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc,
379 struct pin_intr_map *map)
381 uint32_t reg, intcfg, gpiocfg;
382 uint16_t pin = map->pin;
384 intcfg = map->orig_intcfg;
385 intcfg &= CHV_GPIO_CTL1_INTCFG_MASK;
387 gpiocfg = map->orig_gpiocfg;
388 gpiocfg &= CHV_GPIO_CTL0_GPIOCFG_MASK;
393 map->orig_intcfg = 0;
394 map->orig_gpiocfg = 0;
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;
401 chvgpio_write(sc, PIN_CTL1(pin), reg);
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;
409 chvgpio_write(sc, PIN_CTL0(pin), reg);
414 gpio_cherryview_enable_intr(struct gpio_intel_softc *sc,
415 struct pin_intr_map *map)
419 KKASSERT(map->intidx >= 0);
421 /* clear interrupt status flag */
422 chvgpio_write(sc, CHV_GPIO_REG_IS, (1U << map->intidx));
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);
431 gpio_cherryview_disable_intr(struct gpio_intel_softc *sc,
432 struct pin_intr_map *map)
436 KKASSERT(map->intidx >= 0);
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);
445 gpio_cherryview_read_pin(struct gpio_intel_softc *sc, uint16_t pin)
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);
455 if (reg & CHV_GPIO_CTL0_RXSTATE)
464 gpio_cherryview_write_pin(struct gpio_intel_softc *sc, uint16_t pin, int value)
468 reg2 = chvgpio_read(sc, PIN_CTL1(pin));
469 /* Verify that interrupt is disabled */
470 KKASSERT((reg2 & CHV_GPIO_CTL1_INTCFG_MASK) == 0);
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);
478 reg1 |= CHV_GPIO_CTL0_TXSTATE;
480 reg1 &= ~CHV_GPIO_CTL0_TXSTATE;
481 chvgpio_write(sc, PIN_CTL0(pin), reg1);