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 fdt_pinctrl_register(dev, "pinmux"); 165 fdt_pinctrl_configure_tree(dev); 166 167 if (OF_hasprop(node, "interrupt-controller")) { 168 sc->sc_irqs = mallocarray(sc->sc_ngpios, 169 sizeof(*sc->sc_irqs), M_DEVBUF, M_ZERO | M_WAITOK); 170 intr_pic_register(dev, 171 OF_xref_from_node(ofw_bus_get_node(dev))); 172 } 173 174 sc->sc_busdev = gpiobus_add_bus(dev); 175 if (sc->sc_busdev == NULL) { 176 device_printf(dev, "failed to attach gpiobus\n"); 177 goto error; 178 } 179 180 bus_attach_children(dev); 181 return (0); 182 error: 183 mtx_destroy(&sc->sc_mtx); 184 bus_release_resources(dev, apple_pinctrl_res_spec, sc->sc_res); 185 return (ENXIO); 186 } 187 188 static int 189 apple_pinctrl_detach(device_t dev) 190 { 191 192 return (EBUSY); 193 } 194 195 static void 196 apple_pinctrl_pin_configure(struct apple_pinctrl_softc *sc, uint32_t pin, 197 uint32_t flags) 198 { 199 uint32_t reg; 200 201 APPLE_PINCTRL_LOCK_ASSERT(sc); 202 203 MPASS(pin < sc->sc_ngpios); 204 205 reg = HREAD4(sc, GPIO_PIN(pin)); 206 reg &= ~GPIO_PIN_FUNC_MASK; 207 reg &= ~GPIO_PIN_MODE_MASK; 208 209 if ((flags & GPIO_PIN_PRESET_LOW) != 0) 210 reg &= ~GPIO_PIN_DATA; 211 else if ((flags & GPIO_PIN_PRESET_HIGH) != 0) 212 reg |= GPIO_PIN_DATA; 213 214 if ((flags & GPIO_PIN_INPUT) != 0) 215 reg |= GPIO_PIN_MODE_INPUT; 216 else if ((flags & GPIO_PIN_OUTPUT) != 0) 217 reg |= GPIO_PIN_MODE_OUTPUT; 218 219 HWRITE4(sc, GPIO_PIN(pin), reg); 220 } 221 222 static device_t 223 apple_pinctrl_get_bus(device_t dev) 224 { 225 struct apple_pinctrl_softc *sc; 226 227 sc = device_get_softc(dev); 228 return (sc->sc_busdev); 229 } 230 231 static int 232 apple_pinctrl_pin_max(device_t dev, int *maxpin) 233 { 234 struct apple_pinctrl_softc *sc; 235 236 sc = device_get_softc(dev); 237 *maxpin = sc->sc_ngpios - 1; 238 return (0); 239 } 240 241 static int 242 apple_pinctrl_pin_getname(device_t dev, uint32_t pin, char *name) 243 { 244 struct apple_pinctrl_softc *sc; 245 246 sc = device_get_softc(dev); 247 if (pin >= sc->sc_ngpios) 248 return (EINVAL); 249 250 snprintf(name, GPIOMAXNAME - 1, "gpio%c%d", 251 device_get_unit(dev) + 'a', pin); 252 253 return (0); 254 } 255 256 static int 257 apple_pinctrl_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 258 { 259 struct apple_pinctrl_softc *sc; 260 uint32_t reg; 261 262 sc = device_get_softc(dev); 263 if (pin >= sc->sc_ngpios) 264 return (EINVAL); 265 266 *flags = 0; 267 268 APPLE_PINCTRL_LOCK(sc); 269 270 reg = HREAD4(sc, GPIO_PIN(pin)); 271 if ((reg & GPIO_PIN_MODE_INPUT) != 0) 272 *flags |= GPIO_PIN_INPUT; 273 else if ((reg & GPIO_PIN_MODE_OUTPUT) != 0) 274 *flags |= GPIO_PIN_OUTPUT; 275 276 APPLE_PINCTRL_UNLOCK(sc); 277 278 return (0); 279 } 280 281 static int 282 apple_pinctrl_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 283 { 284 285 *caps = APPLE_PINCTRL_DEFAULT_CAPS; 286 return (0); 287 } 288 289 static int 290 apple_pinctrl_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 291 { 292 struct apple_pinctrl_softc *sc; 293 294 sc = device_get_softc(dev); 295 if (pin >= sc->sc_ngpios) 296 return (EINVAL); 297 298 APPLE_PINCTRL_LOCK(sc); 299 apple_pinctrl_pin_configure(sc, pin, flags); 300 APPLE_PINCTRL_UNLOCK(sc); 301 302 return (0); 303 } 304 305 static int 306 apple_pinctrl_pin_get(device_t dev, uint32_t pin, unsigned int *val) 307 { 308 struct apple_pinctrl_softc *sc; 309 uint32_t reg; 310 311 sc = device_get_softc(dev); 312 if (pin >= sc->sc_ngpios) 313 return (EINVAL); 314 315 APPLE_PINCTRL_LOCK(sc); 316 reg = HREAD4(sc, GPIO_PIN(pin)); 317 *val = !!(reg & GPIO_PIN_DATA); 318 APPLE_PINCTRL_UNLOCK(sc); 319 320 return (0); 321 } 322 323 static int 324 apple_pinctrl_pin_set(device_t dev, uint32_t pin, unsigned int value) 325 { 326 struct apple_pinctrl_softc *sc; 327 328 sc = device_get_softc(dev); 329 if (pin >= sc->sc_ngpios) 330 return (EINVAL); 331 332 APPLE_PINCTRL_LOCK(sc); 333 if (value) 334 HSET4(sc, GPIO_PIN(pin), GPIO_PIN_DATA); 335 else 336 HCLR4(sc, GPIO_PIN(pin), GPIO_PIN_DATA); 337 device_printf(sc->sc_dev, "set pin %d to %x\n", 338 pin, HREAD4(sc, GPIO_PIN(pin))); 339 APPLE_PINCTRL_UNLOCK(sc); 340 return (0); 341 } 342 343 344 static int 345 apple_pinctrl_pin_toggle(device_t dev, uint32_t pin) 346 { 347 struct apple_pinctrl_softc *sc; 348 uint32_t reg; 349 350 sc = device_get_softc(dev); 351 if (pin >= sc->sc_ngpios) 352 return (EINVAL); 353 354 APPLE_PINCTRL_LOCK(sc); 355 reg = HREAD4(sc, GPIO_PIN(pin)); 356 if ((reg & GPIO_PIN_DATA) == 0) 357 reg |= GPIO_PIN_DATA; 358 else 359 reg &= ~GPIO_PIN_DATA; 360 HWRITE4(sc, GPIO_PIN(pin), reg); 361 APPLE_PINCTRL_UNLOCK(sc); 362 return (0); 363 } 364 365 366 static int 367 apple_pinctrl_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, 368 uint32_t *pin_flags) 369 { 370 struct apple_pinctrl_softc *sc; 371 uint32_t pin; 372 373 sc = device_get_softc(dev); 374 if (first_pin >= sc->sc_ngpios) 375 return (EINVAL); 376 377 /* 378 * The configuration for a bank of pins is scattered among several 379 * registers; we cannot g'tee to simultaneously change the state of all 380 * the pins in the flags array. So just loop through the array 381 * configuring each pin for now. If there was a strong need, it might 382 * be possible to support some limited simultaneous config, such as 383 * adjacent groups of 8 pins that line up the same as the config regs. 384 */ 385 APPLE_PINCTRL_LOCK(sc); 386 for (pin = first_pin; pin < num_pins; ++pin) { 387 if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) 388 apple_pinctrl_pin_configure(sc, pin, pin_flags[pin]); 389 } 390 APPLE_PINCTRL_UNLOCK(sc); 391 392 return (0); 393 } 394 395 static phandle_t 396 apple_pinctrl_get_node(device_t dev, device_t bus) 397 { 398 399 /* GPIO bus */ 400 return (ofw_bus_get_node(dev)); 401 } 402 403 static int 404 apple_pinctrl_configure(device_t dev, phandle_t cfgxref) 405 { 406 struct apple_pinctrl_softc *sc; 407 pcell_t *pinmux; 408 phandle_t node; 409 ssize_t len; 410 uint32_t reg; 411 uint16_t pin, func; 412 int i; 413 414 sc = device_get_softc(dev); 415 node = OF_node_from_xref(cfgxref); 416 417 len = OF_getencprop_alloc(node, "pinmux", (void **)&pinmux); 418 if (len <= 0) 419 return (-1); 420 421 APPLE_PINCTRL_LOCK(sc); 422 for (i = 0; i < len / sizeof(pcell_t); i++) { 423 pin = APPLE_PIN(pinmux[i]); 424 func = APPLE_FUNC(pinmux[i]); 425 reg = HREAD4(sc, GPIO_PIN(pin)); 426 reg &= ~GPIO_PIN_FUNC_MASK; 427 reg |= (func << GPIO_PIN_FUNC_SHIFT) & GPIO_PIN_FUNC_MASK; 428 HWRITE4(sc, GPIO_PIN(pin), reg); 429 } 430 APPLE_PINCTRL_UNLOCK(sc); 431 432 OF_prop_free(pinmux); 433 return 0; 434 } 435 436 static device_method_t apple_pinctrl_methods[] = { 437 /* Device interface */ 438 DEVMETHOD(device_probe, apple_pinctrl_probe), 439 DEVMETHOD(device_attach, apple_pinctrl_attach), 440 DEVMETHOD(device_detach, apple_pinctrl_detach), 441 442 /* GPIO protocol */ 443 DEVMETHOD(gpio_get_bus, apple_pinctrl_get_bus), 444 DEVMETHOD(gpio_pin_max, apple_pinctrl_pin_max), 445 DEVMETHOD(gpio_pin_getname, apple_pinctrl_pin_getname), 446 DEVMETHOD(gpio_pin_getflags, apple_pinctrl_pin_getflags), 447 DEVMETHOD(gpio_pin_getcaps, apple_pinctrl_pin_getcaps), 448 DEVMETHOD(gpio_pin_setflags, apple_pinctrl_pin_setflags), 449 DEVMETHOD(gpio_pin_get, apple_pinctrl_pin_get), 450 DEVMETHOD(gpio_pin_set, apple_pinctrl_pin_set), 451 DEVMETHOD(gpio_pin_toggle, apple_pinctrl_pin_toggle), 452 DEVMETHOD(gpio_pin_config_32, apple_pinctrl_pin_config_32), 453 454 /* ofw_bus interface */ 455 DEVMETHOD(ofw_bus_get_node, apple_pinctrl_get_node), 456 457 /* fdt_pinctrl interface */ 458 DEVMETHOD(fdt_pinctrl_configure, apple_pinctrl_configure), 459 460 DEVMETHOD_END 461 }; 462 463 static driver_t apple_pinctrl_driver = { 464 "gpio", 465 apple_pinctrl_methods, 466 sizeof(struct apple_pinctrl_softc), 467 }; 468 469 EARLY_DRIVER_MODULE(apple_pinctrl, simplebus, apple_pinctrl_driver, 470 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); 471