1 /* $OpenBSD: aplpinctrl.c,v 1.4 2022/04/06 18:59:26 naddy Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 4 * Copyright (c) 2022 Kyle Evans <kevans@FreeBSD.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/bus.h> 22 #include <sys/gpio.h> 23 #include <sys/kernel.h> 24 #include <sys/malloc.h> 25 #include <sys/module.h> 26 #include <sys/mutex.h> 27 28 #include <machine/bus.h> 29 #include <machine/intr.h> 30 #include <machine/resource.h> 31 32 #include <dev/gpio/gpiobusvar.h> 33 #include <dev/ofw/ofw_bus.h> 34 #include <dev/ofw/ofw_bus_subr.h> 35 #include <dev/fdt/fdt_pinctrl.h> 36 37 #include "pic_if.h" 38 #include "gpio_if.h" 39 40 #define APPLE_PIN(pinmux) ((pinmux) & 0xffff) 41 #define APPLE_FUNC(pinmux) ((pinmux) >> 16) 42 43 #define GPIO_PIN(pin) ((pin) * 4) 44 #define GPIO_PIN_GROUP_MASK (7 << 16) 45 #define GPIO_PIN_INPUT_ENABLE (1 << 9) 46 #define GPIO_PIN_FUNC_MASK (3 << 5) 47 #define GPIO_PIN_FUNC_SHIFT 5 48 #define GPIO_PIN_MODE_MASK (7 << 1) 49 #define GPIO_PIN_MODE_INPUT (0 << 1) 50 #define GPIO_PIN_MODE_OUTPUT (1 << 1) 51 #define GPIO_PIN_MODE_IRQ_HI (2 << 1) 52 #define GPIO_PIN_MODE_IRQ_LO (3 << 1) 53 #define GPIO_PIN_MODE_IRQ_UP (4 << 1) 54 #define GPIO_PIN_MODE_IRQ_DN (5 << 1) 55 #define GPIO_PIN_MODE_IRQ_ANY (6 << 1) 56 #define GPIO_PIN_MODE_IRQ_OFF (7 << 1) 57 #define GPIO_PIN_DATA (1 << 0) 58 #define GPIO_IRQ(grp, pin) (0x800 + (grp) * 64 + ((pin) >> 5) * 4) 59 60 #define APPLE_PINCTRL_DEFAULT_CAPS \ 61 (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) 62 63 #define HREAD4(sc, reg) \ 64 bus_read_4((sc)->sc_res[APPLE_PINCTRL_MEMRES], reg) 65 #define HWRITE4(sc, reg, val) \ 66 bus_write_4((sc)->sc_res[APPLE_PINCTRL_MEMRES], reg, val) 67 #define HSET4(sc, reg, bits) \ 68 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 69 #define HCLR4(sc, reg, bits) \ 70 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 71 72 struct apple_pinctrl_irqsrc { 73 struct intr_irqsrc isrc; 74 int irq; 75 int type; 76 }; 77 78 enum { 79 APPLE_PINCTRL_MEMRES = 0, 80 APPLE_PINCTRL_IRQRES, 81 APPLE_PINCTRL_NRES, 82 }; 83 84 struct apple_pinctrl_softc { 85 device_t sc_dev; 86 device_t sc_busdev; 87 struct mtx sc_mtx; 88 int sc_ngpios; 89 90 void *sc_intrhand; 91 struct resource *sc_res[APPLE_PINCTRL_NRES]; 92 struct apple_pinctrl_irqsrc *sc_irqs; 93 }; 94 95 #define APPLE_PINCTRL_LOCK(sc) mtx_lock_spin(&(sc)->sc_mtx) 96 #define APPLE_PINCTRL_UNLOCK(sc) mtx_unlock_spin(&(sc)->sc_mtx) 97 #define APPLE_PINCTRL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) 98 99 static struct ofw_compat_data compat_data[] = { 100 {"apple,pinctrl", 1}, 101 {NULL, 0}, 102 }; 103 104 static struct resource_spec apple_pinctrl_res_spec[] = { 105 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 106 { SYS_RES_IRQ, 0, RF_ACTIVE }, 107 { -1, 0, 0 }, 108 }; 109 110 static int apple_pinctrl_probe(device_t dev); 111 static int apple_pinctrl_attach(device_t dev); 112 static int apple_pinctrl_detach(device_t dev); 113 114 static int apple_pinctrl_configure(device_t, phandle_t); 115 static phandle_t apple_pinctrl_get_node(device_t, device_t); 116 117 static int 118 apple_pinctrl_probe(device_t dev) 119 { 120 121 if (!ofw_bus_status_okay(dev)) 122 return (ENXIO); 123 124 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 125 return (ENXIO); 126 127 device_set_desc(dev, "Apple Pinmux Controller"); 128 return (BUS_PROBE_DEFAULT); 129 } 130 131 static int 132 apple_pinctrl_attach(device_t dev) 133 { 134 pcell_t gpio_ranges[4]; 135 phandle_t node; 136 struct apple_pinctrl_softc *sc; 137 int error; 138 139 sc = device_get_softc(dev); 140 sc->sc_dev = dev; 141 142 node = ofw_bus_get_node(dev); 143 144 if (bus_alloc_resources(dev, apple_pinctrl_res_spec, sc->sc_res) != 0) { 145 device_printf(dev, "cannot allocate device resources\n"); 146 return (ENXIO); 147 } 148 149 mtx_init(&sc->sc_mtx, "aapl gpio", "gpio", MTX_SPIN); 150 151 error = OF_getencprop(node, "gpio-ranges", gpio_ranges, 152 sizeof(gpio_ranges)); 153 if (error == -1) { 154 device_printf(dev, "failed to get gpio-ranges\n"); 155 goto error; 156 } 157 158 sc->sc_ngpios = gpio_ranges[3]; 159 if (sc->sc_ngpios == 0) { 160 device_printf(dev, "no GPIOs\n"); 161 goto error; 162 } 163 164 sc->sc_busdev = gpiobus_attach_bus(dev); 165 if (sc->sc_busdev == NULL) { 166 device_printf(dev, "failed to attach gpiobus\n"); 167 goto error; 168 } 169 170 fdt_pinctrl_register(dev, "pinmux"); 171 fdt_pinctrl_configure_tree(dev); 172 173 if (!OF_hasprop(node, "interrupt-controller")) 174 return (0); 175 176 sc->sc_irqs = mallocarray(sc->sc_ngpios, 177 sizeof(*sc->sc_irqs), M_DEVBUF, M_ZERO | M_WAITOK); 178 intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); 179 180 return (0); 181 error: 182 mtx_destroy(&sc->sc_mtx); 183 bus_release_resources(dev, apple_pinctrl_res_spec, sc->sc_res); 184 return (ENXIO); 185 } 186 187 static int 188 apple_pinctrl_detach(device_t dev) 189 { 190 191 return (EBUSY); 192 } 193 194 static void 195 apple_pinctrl_pin_configure(struct apple_pinctrl_softc *sc, uint32_t pin, 196 uint32_t flags) 197 { 198 uint32_t reg; 199 200 APPLE_PINCTRL_LOCK_ASSERT(sc); 201 202 MPASS(pin < sc->sc_ngpios); 203 204 reg = HREAD4(sc, GPIO_PIN(pin)); 205 reg &= ~GPIO_PIN_FUNC_MASK; 206 reg &= ~GPIO_PIN_MODE_MASK; 207 208 if ((flags & GPIO_PIN_PRESET_LOW) != 0) 209 reg &= ~GPIO_PIN_DATA; 210 else if ((flags & GPIO_PIN_PRESET_HIGH) != 0) 211 reg |= GPIO_PIN_DATA; 212 213 if ((flags & GPIO_PIN_INPUT) != 0) 214 reg |= GPIO_PIN_MODE_INPUT; 215 else if ((flags & GPIO_PIN_OUTPUT) != 0) 216 reg |= GPIO_PIN_MODE_OUTPUT; 217 218 HWRITE4(sc, GPIO_PIN(pin), reg); 219 } 220 221 static device_t 222 apple_pinctrl_get_bus(device_t dev) 223 { 224 struct apple_pinctrl_softc *sc; 225 226 sc = device_get_softc(dev); 227 return (sc->sc_busdev); 228 } 229 230 static int 231 apple_pinctrl_pin_max(device_t dev, int *maxpin) 232 { 233 struct apple_pinctrl_softc *sc; 234 235 sc = device_get_softc(dev); 236 *maxpin = sc->sc_ngpios - 1; 237 return (0); 238 } 239 240 static int 241 apple_pinctrl_pin_getname(device_t dev, uint32_t pin, char *name) 242 { 243 struct apple_pinctrl_softc *sc; 244 245 sc = device_get_softc(dev); 246 if (pin >= sc->sc_ngpios) 247 return (EINVAL); 248 249 snprintf(name, GPIOMAXNAME - 1, "gpio%c%d", 250 device_get_unit(dev) + 'a', pin); 251 252 return (0); 253 } 254 255 static int 256 apple_pinctrl_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 257 { 258 struct apple_pinctrl_softc *sc; 259 uint32_t reg; 260 261 sc = device_get_softc(dev); 262 if (pin >= sc->sc_ngpios) 263 return (EINVAL); 264 265 *flags = 0; 266 267 APPLE_PINCTRL_LOCK(sc); 268 269 reg = HREAD4(sc, GPIO_PIN(pin)); 270 if ((reg & GPIO_PIN_MODE_INPUT) != 0) 271 *flags |= GPIO_PIN_INPUT; 272 else if ((reg & GPIO_PIN_MODE_OUTPUT) != 0) 273 *flags |= GPIO_PIN_OUTPUT; 274 275 APPLE_PINCTRL_UNLOCK(sc); 276 277 return (0); 278 } 279 280 static int 281 apple_pinctrl_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 282 { 283 284 *caps = APPLE_PINCTRL_DEFAULT_CAPS; 285 return (0); 286 } 287 288 static int 289 apple_pinctrl_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 290 { 291 struct apple_pinctrl_softc *sc; 292 293 sc = device_get_softc(dev); 294 if (pin >= sc->sc_ngpios) 295 return (EINVAL); 296 297 APPLE_PINCTRL_LOCK(sc); 298 apple_pinctrl_pin_configure(sc, pin, flags); 299 APPLE_PINCTRL_UNLOCK(sc); 300 301 return (0); 302 } 303 304 static int 305 apple_pinctrl_pin_get(device_t dev, uint32_t pin, unsigned int *val) 306 { 307 struct apple_pinctrl_softc *sc; 308 uint32_t reg; 309 310 sc = device_get_softc(dev); 311 if (pin >= sc->sc_ngpios) 312 return (EINVAL); 313 314 APPLE_PINCTRL_LOCK(sc); 315 reg = HREAD4(sc, GPIO_PIN(pin)); 316 *val = !!(reg & GPIO_PIN_DATA); 317 APPLE_PINCTRL_UNLOCK(sc); 318 319 return (0); 320 } 321 322 static int 323 apple_pinctrl_pin_set(device_t dev, uint32_t pin, unsigned int value) 324 { 325 struct apple_pinctrl_softc *sc; 326 327 sc = device_get_softc(dev); 328 if (pin >= sc->sc_ngpios) 329 return (EINVAL); 330 331 APPLE_PINCTRL_LOCK(sc); 332 if (value) 333 HSET4(sc, GPIO_PIN(pin), GPIO_PIN_DATA); 334 else 335 HCLR4(sc, GPIO_PIN(pin), GPIO_PIN_DATA); 336 device_printf(sc->sc_dev, "set pin %d to %x\n", 337 pin, HREAD4(sc, GPIO_PIN(pin))); 338 APPLE_PINCTRL_UNLOCK(sc); 339 return (0); 340 } 341 342 343 static int 344 apple_pinctrl_pin_toggle(device_t dev, uint32_t pin) 345 { 346 struct apple_pinctrl_softc *sc; 347 uint32_t reg; 348 349 sc = device_get_softc(dev); 350 if (pin >= sc->sc_ngpios) 351 return (EINVAL); 352 353 APPLE_PINCTRL_LOCK(sc); 354 reg = HREAD4(sc, GPIO_PIN(pin)); 355 if ((reg & GPIO_PIN_DATA) == 0) 356 reg |= GPIO_PIN_DATA; 357 else 358 reg &= ~GPIO_PIN_DATA; 359 HWRITE4(sc, GPIO_PIN(pin), reg); 360 APPLE_PINCTRL_UNLOCK(sc); 361 return (0); 362 } 363 364 365 static int 366 apple_pinctrl_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, 367 uint32_t *pin_flags) 368 { 369 struct apple_pinctrl_softc *sc; 370 uint32_t pin; 371 372 sc = device_get_softc(dev); 373 if (first_pin >= sc->sc_ngpios) 374 return (EINVAL); 375 376 /* 377 * The configuration for a bank of pins is scattered among several 378 * registers; we cannot g'tee to simultaneously change the state of all 379 * the pins in the flags array. So just loop through the array 380 * configuring each pin for now. If there was a strong need, it might 381 * be possible to support some limited simultaneous config, such as 382 * adjacent groups of 8 pins that line up the same as the config regs. 383 */ 384 APPLE_PINCTRL_LOCK(sc); 385 for (pin = first_pin; pin < num_pins; ++pin) { 386 if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) 387 apple_pinctrl_pin_configure(sc, pin, pin_flags[pin]); 388 } 389 APPLE_PINCTRL_UNLOCK(sc); 390 391 return (0); 392 } 393 394 static phandle_t 395 apple_pinctrl_get_node(device_t dev, device_t bus) 396 { 397 398 /* GPIO bus */ 399 return (ofw_bus_get_node(dev)); 400 } 401 402 static int 403 apple_pinctrl_configure(device_t dev, phandle_t cfgxref) 404 { 405 struct apple_pinctrl_softc *sc; 406 pcell_t *pinmux; 407 phandle_t node; 408 ssize_t len; 409 uint32_t reg; 410 uint16_t pin, func; 411 int i; 412 413 sc = device_get_softc(dev); 414 node = OF_node_from_xref(cfgxref); 415 416 len = OF_getencprop_alloc(node, "pinmux", (void **)&pinmux); 417 if (len <= 0) 418 return (-1); 419 420 APPLE_PINCTRL_LOCK(sc); 421 for (i = 0; i < len / sizeof(pcell_t); i++) { 422 pin = APPLE_PIN(pinmux[i]); 423 func = APPLE_FUNC(pinmux[i]); 424 reg = HREAD4(sc, GPIO_PIN(pin)); 425 reg &= ~GPIO_PIN_FUNC_MASK; 426 reg |= (func << GPIO_PIN_FUNC_SHIFT) & GPIO_PIN_FUNC_MASK; 427 HWRITE4(sc, GPIO_PIN(pin), reg); 428 } 429 APPLE_PINCTRL_UNLOCK(sc); 430 431 OF_prop_free(pinmux); 432 return 0; 433 } 434 435 static device_method_t apple_pinctrl_methods[] = { 436 /* Device interface */ 437 DEVMETHOD(device_probe, apple_pinctrl_probe), 438 DEVMETHOD(device_attach, apple_pinctrl_attach), 439 DEVMETHOD(device_detach, apple_pinctrl_detach), 440 441 /* GPIO protocol */ 442 DEVMETHOD(gpio_get_bus, apple_pinctrl_get_bus), 443 DEVMETHOD(gpio_pin_max, apple_pinctrl_pin_max), 444 DEVMETHOD(gpio_pin_getname, apple_pinctrl_pin_getname), 445 DEVMETHOD(gpio_pin_getflags, apple_pinctrl_pin_getflags), 446 DEVMETHOD(gpio_pin_getcaps, apple_pinctrl_pin_getcaps), 447 DEVMETHOD(gpio_pin_setflags, apple_pinctrl_pin_setflags), 448 DEVMETHOD(gpio_pin_get, apple_pinctrl_pin_get), 449 DEVMETHOD(gpio_pin_set, apple_pinctrl_pin_set), 450 DEVMETHOD(gpio_pin_toggle, apple_pinctrl_pin_toggle), 451 DEVMETHOD(gpio_pin_config_32, apple_pinctrl_pin_config_32), 452 453 /* ofw_bus interface */ 454 DEVMETHOD(ofw_bus_get_node, apple_pinctrl_get_node), 455 456 /* fdt_pinctrl interface */ 457 DEVMETHOD(fdt_pinctrl_configure, apple_pinctrl_configure), 458 459 DEVMETHOD_END 460 }; 461 462 static driver_t apple_pinctrl_driver = { 463 "gpio", 464 apple_pinctrl_methods, 465 sizeof(struct apple_pinctrl_softc), 466 }; 467 468 EARLY_DRIVER_MODULE(apple_pinctrl, simplebus, apple_pinctrl_driver, 469 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); 470