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/bus.h> 45 46 #include <sys/rman.h> 47 48 #include "opt_acpi.h" 49 #include "acpi.h" 50 #include <dev/acpica/acpivar.h> 51 52 #include "gpio_intel_var.h" 53 54 #include "gpio_if.h" 55 56 #define CHV_GPIO_REG_IS 0x300 57 #define CHV_GPIO_REG_MASK 0x380 58 #define CHV_GPIO_REG_PINS 0x4400 /* start of pin control registers */ 59 60 #define CHV_GPIO_REGOFF_CTL0 0x0 61 #define CHV_GPIO_REGOFF_CTL1 0x4 62 63 #define CHV_GPIO_CTL0_RXSTATE 0x00000001u 64 #define CHV_GPIO_CTL0_TXSTATE 0x00000002u 65 #define CHV_GPIO_CTL0_GPIOCFG_MASK 0x00000700u 66 #define CHV_GPIO_CTL0_GPIOEN 0x00008000u 67 #define CHV_GPIO_CTL0_PULLUP 0x00800000u 68 #define CHV_GPIO_CTL1_INTCFG_MASK 0x00000007u 69 #define CHV_GPIO_CTL1_INVRXDATA 0x00000040u 70 71 #define CHV_GPIO_PINSIZE 0x8 /* 8 bytes for each pin */ 72 #define CHV_GPIO_PINCHUNK 15 /* 15 pins at a time */ 73 #define CHV_GPIO_PININC 0x400 /* every 0x400 bytes */ 74 75 #define PIN_ADDRESS(x) \ 76 (CHV_GPIO_REG_PINS + \ 77 ((x) / CHV_GPIO_PINCHUNK) * CHV_GPIO_PININC + \ 78 ((x) % CHV_GPIO_PINCHUNK) * CHV_GPIO_PINSIZE) 79 80 #define PIN_CTL0(x) (PIN_ADDRESS(x) + CHV_GPIO_REGOFF_CTL0) 81 #define PIN_CTL1(x) (PIN_ADDRESS(x) + CHV_GPIO_REGOFF_CTL1) 82 83 static void gpio_cherryview_init(struct gpio_intel_softc *sc); 84 static void gpio_cherryview_intr(void *arg); 85 static int gpio_cherryview_map_intr(struct gpio_intel_softc *sc, 86 uint16_t pin, int trigger, int polarity, int termination); 87 static void gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc, 88 struct pin_intr_map *map); 89 static void gpio_cherryview_enable_intr(struct gpio_intel_softc *sc, 90 struct pin_intr_map *map); 91 static void gpio_cherryview_disable_intr(struct gpio_intel_softc *sc, 92 struct pin_intr_map *map); 93 static int gpio_cherryview_check_io_pin(struct gpio_intel_softc *sc, 94 uint16_t pin, int flags); 95 static int gpio_cherryview_read_pin(struct gpio_intel_softc *sc, 96 uint16_t pin); 97 static void gpio_cherryview_write_pin(struct gpio_intel_softc *sc, 98 uint16_t pin, int value); 99 100 static struct gpio_intel_fns gpio_cherryview_fns = { 101 .init = gpio_cherryview_init, 102 .intr = gpio_cherryview_intr, 103 .map_intr = gpio_cherryview_map_intr, 104 .unmap_intr = gpio_cherryview_unmap_intr, 105 .enable_intr = gpio_cherryview_enable_intr, 106 .disable_intr = gpio_cherryview_disable_intr, 107 .check_io_pin = gpio_cherryview_check_io_pin, 108 .read_pin = gpio_cherryview_read_pin, 109 .write_pin = gpio_cherryview_write_pin, 110 }; 111 112 /* _UID=1 */ 113 static struct pinrange chv_sw_ranges[] = { 114 { 0, 7 }, 115 { 15, 22 }, 116 { 30, 37 }, 117 { 45, 52 }, 118 { 60, 67 }, 119 { 75, 82 }, 120 { 90, 97 }, 121 { -1, -1 } 122 }; 123 124 /* _UID=2 */ 125 static struct pinrange chv_n_ranges[] = { 126 { 0, 8 }, 127 { 15, 27 }, 128 { 30, 41 }, 129 { 45, 56 }, 130 { 60, 72 }, 131 { -1, -1 } 132 }; 133 134 /* _UID=3 */ 135 static struct pinrange chv_e_ranges[] = { 136 { 0, 11 }, 137 { 15, 26 }, 138 { -1, -1 } 139 }; 140 141 /* _UID=4 */ 142 static struct pinrange chv_se_ranges[] = { 143 { 0, 7 }, 144 { 15, 26 }, 145 { 30, 35 }, 146 { 45, 52 }, 147 { 60, 69 }, 148 { 75, 85 }, 149 { -1, -1 } 150 }; 151 152 static struct lock gpio_lk; 153 LOCK_SYSINIT(chvgpiolk, &gpio_lk, "chvgpio", 0); 154 155 /* 156 * Use global GPIO register lock to workaround erratum: 157 * 158 * CHT34 Multiple Drivers That Access the GPIO Registers Concurrently May 159 * Result in Unpredictable System Behaviour 160 */ 161 static inline uint32_t 162 chvgpio_read(struct gpio_intel_softc *sc, bus_size_t offset) 163 { 164 uint32_t val; 165 166 lockmgr(&gpio_lk, LK_EXCLUSIVE); 167 val = bus_read_4(sc->mem_res, offset); 168 lockmgr(&gpio_lk, LK_RELEASE); 169 return val; 170 } 171 172 static inline void 173 chvgpio_write(struct gpio_intel_softc *sc, bus_size_t offset, uint32_t val) 174 { 175 lockmgr(&gpio_lk, LK_EXCLUSIVE); 176 bus_write_4(sc->mem_res, offset, val); 177 lockmgr(&gpio_lk, LK_RELEASE); 178 } 179 180 int 181 gpio_cherryview_matchuid(struct gpio_intel_softc *sc) 182 { 183 ACPI_HANDLE handle; 184 185 handle = acpi_get_handle(sc->dev); 186 if (acpi_MatchUid(handle, "1")) { 187 sc->ranges = chv_sw_ranges; 188 } else if (acpi_MatchUid(handle, "2")) { 189 sc->ranges = chv_n_ranges; 190 } else if (acpi_MatchUid(handle, "3")) { 191 sc->ranges = chv_e_ranges; 192 } else if (acpi_MatchUid(handle, "4")) { 193 sc->ranges = chv_se_ranges; 194 } else { 195 return (ENXIO); 196 } 197 198 sc->fns = &gpio_cherryview_fns; 199 200 return (0); 201 } 202 203 static void 204 gpio_cherryview_init(struct gpio_intel_softc *sc) 205 { 206 /* mask and clear all interrupt lines */ 207 chvgpio_write(sc, CHV_GPIO_REG_MASK, 0); 208 chvgpio_write(sc, CHV_GPIO_REG_IS, 0xffff); 209 } 210 211 static void 212 gpio_cherryview_intr(void *arg) 213 { 214 struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg; 215 struct pin_intr_map *mapping; 216 uint32_t status; 217 int i; 218 219 status = chvgpio_read(sc, CHV_GPIO_REG_IS); 220 KKASSERT(NELEM(sc->intrmaps) >= 16); 221 for (i = 0; i < 16; i++) { 222 if (status & (1U << i)) { 223 mapping = &sc->intrmaps[i]; 224 if (!mapping->is_level) { 225 chvgpio_write(sc, CHV_GPIO_REG_IS, 226 (1U << i)); 227 } 228 if (mapping->pin != -1 && mapping->handler != NULL) 229 mapping->handler(mapping->arg); 230 if (mapping->is_level) { 231 chvgpio_write(sc, CHV_GPIO_REG_IS, 232 (1U << i)); 233 } 234 } 235 } 236 } 237 238 /* XXX Add shared/exclusive argument. */ 239 static int 240 gpio_cherryview_map_intr(struct gpio_intel_softc *sc, uint16_t pin, int trigger, 241 int polarity, int termination) 242 { 243 uint32_t reg, reg1, reg2; 244 uint32_t intcfg, new_intcfg, gpiocfg, new_gpiocfg; 245 int i; 246 247 reg1 = chvgpio_read(sc, PIN_CTL0(pin)); 248 reg2 = chvgpio_read(sc, PIN_CTL1(pin)); 249 device_printf(sc->dev, 250 "pin=%d trigger=%d polarity=%d ctrl0=0x%08x ctrl1=0x%08x\n", 251 pin, trigger, polarity, reg1, reg2); 252 253 new_intcfg = intcfg = reg2 & CHV_GPIO_CTL1_INTCFG_MASK; 254 new_gpiocfg = gpiocfg = reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK; 255 256 /* 257 * Sanity Checks, for now we just abort if the configuration doesn't 258 * match our expectations. 259 */ 260 if (!(reg1 & CHV_GPIO_CTL0_GPIOEN)) { 261 device_printf(sc->dev, "GPIO mode is disabled\n"); 262 return (ENXIO); 263 } 264 if (gpiocfg != 0x0 && gpiocfg != 0x200) { 265 device_printf(sc->dev, "RX is disabled\n"); 266 if (gpiocfg == 0x100) 267 new_gpiocfg = 0x000; 268 else if (gpiocfg == 0x300) 269 new_gpiocfg = 0x200; 270 else 271 return (ENXIO); 272 } 273 if (trigger == ACPI_LEVEL_SENSITIVE) { 274 if (intcfg != 4) { 275 device_printf(sc->dev, 276 "trigger is %x, should be 4 (Level)\n", intcfg); 277 return (ENXIO); 278 } 279 if (polarity == ACPI_ACTIVE_BOTH) { 280 device_printf(sc->dev, 281 "ACTIVE_BOTH incompatible with level trigger\n"); 282 return (ENXIO); 283 } else if (polarity == ACPI_ACTIVE_LOW) { 284 if (!(reg2 & CHV_GPIO_CTL1_INVRXDATA)) { 285 device_printf(sc->dev, 286 "Invert RX not enabled (needed for " 287 "level/low trigger/polarity)\n"); 288 return (ENXIO); 289 } 290 } else { 291 if (reg2 & CHV_GPIO_CTL1_INVRXDATA) { 292 device_printf(sc->dev, 293 "Invert RX should not be enabled for " 294 "level/high trigger/polarity\n"); 295 return (ENXIO); 296 } 297 } 298 } else { 299 /* 300 * For edge-triggered interrupts it's definitely harmless to 301 * change between rising-edge, falling-edge and both-edges 302 * triggering. 303 */ 304 if (polarity == ACPI_ACTIVE_HIGH && intcfg != 2) { 305 device_printf(sc->dev, 306 "Wrong interrupt configuration, is 0x%x should " 307 "be 0x%x\n", intcfg, 2); 308 if (intcfg == 1 || intcfg == 3) 309 new_intcfg = 2; 310 else 311 return (ENXIO); 312 } else if (polarity == ACPI_ACTIVE_LOW && intcfg != 1) { 313 device_printf(sc->dev, 314 "Wrong interrupt configuration, is 0x%x should " 315 "be 0x%x\n", intcfg, 1); 316 if (intcfg == 2 || intcfg == 3) 317 new_intcfg = 1; 318 else 319 return (ENXIO); 320 } else if (polarity == ACPI_ACTIVE_BOTH && intcfg != 3) { 321 device_printf(sc->dev, 322 "Wrong interrupt configuration, is 0x%x should " 323 "be 0x%x\n", intcfg, 3); 324 if (intcfg == 1 || intcfg == 2) 325 new_intcfg = 3; 326 else 327 return (ENXIO); 328 } 329 } 330 if (termination == ACPI_PIN_CONFIG_PULLUP && 331 !(reg1 & CHV_GPIO_CTL0_PULLUP)) { 332 device_printf(sc->dev, 333 "Wrong termination, is pull-down, should be pull-up\n"); 334 return (ENXIO); 335 } else if (termination == ACPI_PIN_CONFIG_PULLDOWN && 336 (reg1 & CHV_GPIO_CTL0_PULLUP)) { 337 device_printf(sc->dev, 338 "Wrong termination, is pull-up, should be pull-down\n"); 339 return (ENXIO); 340 } 341 342 /* Check if the interrupt/line configured by BIOS/UEFI is unused */ 343 i = (reg1 >> 28) & 0xf; 344 if (sc->intrmaps[i].pin != -1) { 345 device_printf(sc->dev, "Interrupt line %d already used\n", i); 346 return (ENXIO); 347 } 348 349 if (new_intcfg != intcfg) { 350 device_printf(sc->dev, 351 "Switching interrupt configuration from 0x%x to 0x%x\n", 352 intcfg, new_intcfg); 353 reg = reg2 & ~CHV_GPIO_CTL1_INTCFG_MASK; 354 reg |= (new_intcfg & CHV_GPIO_CTL1_INTCFG_MASK) << 0; 355 chvgpio_write(sc, PIN_CTL1(pin), reg); 356 } 357 358 if (new_gpiocfg != gpiocfg) { 359 device_printf(sc->dev, 360 "Switching gpio configuration from 0x%x to 0x%x\n", 361 gpiocfg, new_gpiocfg); 362 reg = reg1 & ~CHV_GPIO_CTL0_GPIOCFG_MASK; 363 reg |= (new_gpiocfg & CHV_GPIO_CTL0_GPIOCFG_MASK) << 0; 364 chvgpio_write(sc, PIN_CTL0(pin), reg); 365 } 366 367 sc->intrmaps[i].pin = pin; 368 sc->intrmaps[i].intidx = i; 369 sc->intrmaps[i].orig_intcfg = intcfg; 370 sc->intrmaps[i].orig_gpiocfg = gpiocfg; 371 372 if (trigger == ACPI_LEVEL_SENSITIVE) 373 sc->intrmaps[i].is_level = 1; 374 else 375 sc->intrmaps[i].is_level = 0; 376 377 return (0); 378 } 379 380 static void 381 gpio_cherryview_unmap_intr(struct gpio_intel_softc *sc, 382 struct pin_intr_map *map) 383 { 384 uint32_t reg, intcfg, gpiocfg; 385 uint16_t pin = map->pin; 386 387 intcfg = map->orig_intcfg; 388 intcfg &= CHV_GPIO_CTL1_INTCFG_MASK; 389 390 gpiocfg = map->orig_gpiocfg; 391 gpiocfg &= CHV_GPIO_CTL0_GPIOCFG_MASK; 392 393 map->pin = -1; 394 map->intidx = -1; 395 map->is_level = 0; 396 map->orig_intcfg = 0; 397 map->orig_gpiocfg = 0; 398 399 /* Restore interrupt configuration if needed */ 400 reg = chvgpio_read(sc, PIN_CTL1(pin)); 401 if ((reg & CHV_GPIO_CTL1_INTCFG_MASK) != intcfg) { 402 reg &= ~CHV_GPIO_CTL1_INTCFG_MASK; 403 reg |= intcfg; 404 chvgpio_write(sc, PIN_CTL1(pin), reg); 405 } 406 407 /* Restore gpio configuration if needed */ 408 reg = chvgpio_read(sc, PIN_CTL0(pin)); 409 if ((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) != gpiocfg) { 410 reg &= ~CHV_GPIO_CTL0_GPIOCFG_MASK; 411 reg |= gpiocfg; 412 chvgpio_write(sc, PIN_CTL0(pin), reg); 413 } 414 } 415 416 static void 417 gpio_cherryview_enable_intr(struct gpio_intel_softc *sc, 418 struct pin_intr_map *map) 419 { 420 uint32_t reg; 421 422 KKASSERT(map->intidx >= 0); 423 424 /* clear interrupt status flag */ 425 chvgpio_write(sc, CHV_GPIO_REG_IS, (1U << map->intidx)); 426 427 /* unmask interrupt */ 428 reg = chvgpio_read(sc, CHV_GPIO_REG_MASK); 429 reg |= (1U << map->intidx); 430 chvgpio_write(sc, CHV_GPIO_REG_MASK, reg); 431 } 432 433 static void 434 gpio_cherryview_disable_intr(struct gpio_intel_softc *sc, 435 struct pin_intr_map *map) 436 { 437 uint32_t reg; 438 439 KKASSERT(map->intidx >= 0); 440 441 /* mask interrupt line */ 442 reg = chvgpio_read(sc, CHV_GPIO_REG_MASK); 443 reg &= ~(1U << map->intidx); 444 chvgpio_write(sc, CHV_GPIO_REG_MASK, reg); 445 } 446 447 static int 448 gpio_cherryview_check_io_pin(struct gpio_intel_softc *sc, uint16_t pin, 449 int flags) 450 { 451 uint32_t reg1, reg2; 452 453 reg1 = chvgpio_read(sc, PIN_CTL0(pin)); 454 if (flags & (1U << 0)) { 455 /* Verify that RX is enabled */ 456 if ((reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) != 0 && 457 (reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) != 0x200) { 458 return (0); 459 } 460 } 461 reg2 = chvgpio_read(sc, PIN_CTL1(pin)); 462 if (flags & (1U << 1)) { 463 /* Verify that interrupt is disabled */ 464 if ((reg2 & CHV_GPIO_CTL1_INTCFG_MASK) != 0) 465 return (0); 466 /* Verify that TX is enabled */ 467 if ((reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) != 0 && 468 (reg1 & CHV_GPIO_CTL0_GPIOCFG_MASK) != 0x100) { 469 return (0); 470 } 471 } 472 473 return (1); 474 } 475 476 static int 477 gpio_cherryview_read_pin(struct gpio_intel_softc *sc, uint16_t pin) 478 { 479 uint32_t reg; 480 int val; 481 482 reg = chvgpio_read(sc, PIN_CTL0(pin)); 483 /* Verify that RX is enabled */ 484 KKASSERT((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x0 || 485 (reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x200); 486 487 if (reg & CHV_GPIO_CTL0_RXSTATE) 488 val = 1; 489 else 490 val = 0; 491 492 return (val); 493 } 494 495 static void 496 gpio_cherryview_write_pin(struct gpio_intel_softc *sc, uint16_t pin, int value) 497 { 498 uint32_t reg; 499 500 reg = chvgpio_read(sc, PIN_CTL0(pin)); 501 /* Verify that TX is enabled */ 502 KKASSERT((reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0 || 503 (reg & CHV_GPIO_CTL0_GPIOCFG_MASK) == 0x100); 504 505 if (value) 506 reg |= CHV_GPIO_CTL0_TXSTATE; 507 else 508 reg &= ~CHV_GPIO_CTL0_TXSTATE; 509 chvgpio_write(sc, PIN_CTL0(pin), reg); 510 } 511