jcs's openbsd hax
openbsd

Add support for GPIO interrupts.

ok mglocker@, bmercer@

kettenis 3ee3bddf 0c042fb4

+261 -10
+261 -10
sys/dev/fdt/bcmstbgpio.c
··· 1 - /* $OpenBSD: bcmstbgpio.c,v 1.1 2025/08/09 14:42:48 kettenis Exp $ */ 1 + /* $OpenBSD: bcmstbgpio.c,v 1.2 2025/09/08 19:32:18 kettenis Exp $ */ 2 2 /* 3 3 * Copyright (c) 2025 Mark Kettenis <kettenis@openbsd.org> 4 4 * ··· 18 18 #include <sys/param.h> 19 19 #include <sys/systm.h> 20 20 #include <sys/device.h> 21 + #include <sys/evcount.h> 22 + #include <sys/malloc.h> 21 23 22 24 #include <machine/intr.h> 23 25 #include <machine/bus.h> ··· 30 32 /* Registers. */ 31 33 #define GIO_DATA(_bank) (0x04 + (_bank) * 32) 32 34 #define GIO_IODIR(_bank) (0x08 + (_bank) * 32) 35 + #define GIO_EC(_bank) (0x0c + (_bank) * 32) 36 + #define GIO_EI(_bank) (0x10 + (_bank) * 32) 37 + #define GIO_MASK(_bank) (0x14 + (_bank) * 32) 38 + #define GIO_LEVEL(_bank) (0x18 + (_bank) * 32) 39 + #define GIO_STAT(_bank) (0x1c + (_bank) * 32) 33 40 34 41 #define HREAD4(sc, reg) \ 35 42 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) ··· 40 47 #define HCLR4(sc, reg, bits) \ 41 48 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 42 49 50 + struct intrhand { 51 + int (*ih_func)(void *); 52 + void *ih_arg; 53 + int ih_ipl; 54 + int ih_irq; 55 + int ih_edge; 56 + struct evcount ih_count; 57 + char *ih_name; 58 + void *ih_sc; 59 + }; 60 + 43 61 struct bcmstbgpio_softc { 44 62 struct device sc_dev; 45 63 bus_space_tag_t sc_iot; 46 64 bus_space_handle_t sc_ioh; 47 65 u_int sc_nbanks; 66 + int sc_node; 67 + 68 + void *sc_ih; 69 + int sc_ipl; 70 + struct intrhand **sc_handlers; 71 + struct interrupt_controller sc_ic; 48 72 49 73 struct gpio_controller sc_gc; 50 74 }; ··· 63 87 void bcmstbgpio_config_pin(void *, uint32_t *, int); 64 88 int bcmstbgpio_get_pin(void *, uint32_t *); 65 89 void bcmstbgpio_set_pin(void *, uint32_t *, int); 90 + void *bcmstbgpio_intr_establish_pin(void *, uint32_t *, int, 91 + struct cpu_info *, int (*)(void *), void *, char *); 92 + 93 + int bcmstbgpio_intr(void *); 94 + void *bcmstbgpio_intr_establish(void *, int *, int, struct cpu_info *, 95 + int (*)(void *), void *, char *); 96 + void bcmstbgpio_intr_enable(void *); 97 + void bcmstbgpio_intr_disable(void *); 98 + void bcmstbgpio_intr_barrier(void *); 66 99 67 100 int 68 101 bcmstbgpio_match(struct device *parent, void *match, void *aux) ··· 77 110 { 78 111 struct bcmstbgpio_softc *sc = (struct bcmstbgpio_softc *)self; 79 112 struct fdt_attach_args *faa = aux; 113 + u_int bank; 80 114 81 115 if (faa->fa_nreg < 1) { 82 116 printf(": no registers\n"); ··· 90 124 return; 91 125 } 92 126 sc->sc_nbanks = faa->fa_reg[0].size / 32; 127 + sc->sc_node = faa->fa_node; 128 + 129 + sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_NONE, 130 + bcmstbgpio_intr, sc, sc->sc_dev.dv_xname); 131 + sc->sc_ipl = IPL_NONE; 132 + 133 + printf("\n"); 134 + 135 + for (bank = 0; bank < sc->sc_nbanks; bank++) 136 + HWRITE4(sc, GIO_MASK(bank), 0); 137 + 138 + sc->sc_handlers = mallocarray(sc->sc_nbanks * 32, 139 + sizeof(struct intrhand *), M_DEVBUF, M_WAITOK | M_ZERO); 140 + 141 + sc->sc_ic.ic_node = faa->fa_node; 142 + sc->sc_ic.ic_cookie = sc; 143 + sc->sc_ic.ic_establish = bcmstbgpio_intr_establish; 144 + sc->sc_ic.ic_enable = bcmstbgpio_intr_enable; 145 + sc->sc_ic.ic_disable = bcmstbgpio_intr_enable; 146 + sc->sc_ic.ic_barrier = bcmstbgpio_intr_barrier; 147 + fdt_intr_register(&sc->sc_ic); 93 148 94 149 sc->sc_gc.gc_node = faa->fa_node; 95 150 sc->sc_gc.gc_cookie = sc; 96 151 sc->sc_gc.gc_config_pin = bcmstbgpio_config_pin; 97 152 sc->sc_gc.gc_get_pin = bcmstbgpio_get_pin; 98 153 sc->sc_gc.gc_set_pin = bcmstbgpio_set_pin; 154 + sc->sc_gc.gc_intr_establish = bcmstbgpio_intr_establish_pin; 99 155 gpio_controller_register(&sc->sc_gc); 100 - 101 - printf("\n"); 102 156 } 103 157 104 158 void 105 159 bcmstbgpio_config_pin(void *cookie, uint32_t *cells, int config) 106 160 { 107 161 struct bcmstbgpio_softc *sc = cookie; 108 - uint32_t bank = cells[0] / 32;; 109 - uint32_t pin = cells[0] % 32; 162 + u_int bank = cells[0] / 32; 163 + u_int pin = cells[0] % 32; 110 164 111 165 if (bank >= sc->sc_nbanks) 112 166 return; 113 167 114 168 if (config & GPIO_CONFIG_OUTPUT) 115 - HCLR4(sc, GIO_IODIR(bank), (1U << pin)); 169 + HCLR4(sc, GIO_IODIR(bank), 1U << pin); 116 170 else 117 - HSET4(sc, GIO_IODIR(bank), (1U << pin)); 171 + HSET4(sc, GIO_IODIR(bank), 1U << pin); 118 172 } 119 173 120 174 int 121 175 bcmstbgpio_get_pin(void *cookie, uint32_t *cells) 122 176 { 123 177 struct bcmstbgpio_softc *sc = cookie; 124 - uint32_t bank = cells[0] / 32;; 178 + uint32_t bank = cells[0] / 32; 125 179 uint32_t pin = cells[0] % 32; 126 180 uint32_t flags = cells[1]; 127 181 uint32_t reg; ··· 151 205 if (flags & GPIO_ACTIVE_LOW) 152 206 val = !val; 153 207 if (val) 154 - HSET4(sc, GIO_DATA(bank), (1U << pin)); 208 + HSET4(sc, GIO_DATA(bank), 1U << pin); 155 209 else 156 - HCLR4(sc, GIO_DATA(bank), (1U << pin)); 210 + HCLR4(sc, GIO_DATA(bank), 1U << pin); 211 + } 212 + 213 + void * 214 + bcmstbgpio_intr_establish_pin(void *cookie, uint32_t *cells, int ipl, 215 + struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 216 + { 217 + struct bcmstbgpio_softc *sc = cookie; 218 + uint32_t icells[2]; 219 + 220 + icells[0] = cells[0]; 221 + icells[1] = 3; /* both edges */ 222 + 223 + return bcmstbgpio_intr_establish(sc, icells, ipl, ci, func, arg, name); 224 + } 225 + 226 + int 227 + bcmstbgpio_intr(void *arg) 228 + { 229 + struct bcmstbgpio_softc *sc = arg; 230 + u_int bank; 231 + int handled = 0; 232 + 233 + for (bank = 0; bank < sc->sc_nbanks; bank++) { 234 + struct intrhand *ih; 235 + uint32_t mask, stat; 236 + u_int pin; 237 + int s; 238 + 239 + mask = HREAD4(sc, GIO_MASK(bank)); 240 + stat = HREAD4(sc, GIO_STAT(bank)) & mask; 241 + if (stat == 0) 242 + continue; 243 + 244 + while (stat) { 245 + pin = ffs(stat) - 1; 246 + stat &= ~(1U << pin); 247 + 248 + ih = sc->sc_handlers[bank * 32 + pin]; 249 + KASSERT(ih); 250 + 251 + if (ih->ih_edge) 252 + HWRITE4(sc, GIO_STAT(bank), 1U << pin); 253 + 254 + s = splraise(ih->ih_ipl); 255 + if (ih->ih_func(ih->ih_arg)) 256 + ih->ih_count.ec_count++; 257 + splx(s); 258 + 259 + if (!ih->ih_edge) 260 + HWRITE4(sc, GIO_STAT(bank), 1U << pin); 261 + 262 + handled = 1; 263 + } 264 + } 265 + 266 + return handled; 267 + } 268 + 269 + void 270 + bcmstbgpio_recalc_ipl(struct bcmstbgpio_softc *sc) 271 + { 272 + struct intrhand *ih; 273 + int max = IPL_NONE; 274 + int min = IPL_HIGH; 275 + int irq; 276 + 277 + for (irq = 0; irq < sc->sc_nbanks * 32; irq++) { 278 + ih = sc->sc_handlers[irq]; 279 + if (ih == NULL) 280 + continue; 281 + 282 + if (ih->ih_ipl > max) 283 + max = ih->ih_ipl; 284 + 285 + if (ih->ih_ipl < min) 286 + min = ih->ih_ipl; 287 + } 288 + 289 + if (max == IPL_NONE) 290 + min = IPL_NONE; 291 + 292 + if (sc->sc_ipl != max) { 293 + sc->sc_ipl = max; 294 + 295 + fdt_intr_disestablish(sc->sc_ih); 296 + sc->sc_ih = fdt_intr_establish(sc->sc_node, 297 + sc->sc_ipl, bcmstbgpio_intr, sc, sc->sc_dev.dv_xname); 298 + KASSERT(sc->sc_ih); 299 + } 300 + } 301 + 302 + void * 303 + bcmstbgpio_intr_establish(void *cookie, int *cells, int ipl, 304 + struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 305 + { 306 + struct bcmstbgpio_softc *sc = cookie; 307 + struct intrhand *ih; 308 + int irq = cells[0]; 309 + int type = cells[1]; 310 + u_int bank = irq / 32;; 311 + u_int pin = irq % 32; 312 + uint32_t ec, ei, level; 313 + int edge = 0; 314 + 315 + /* Interrupt handling is an optional feature. */ 316 + if (sc->sc_ih == NULL) 317 + return NULL; 318 + 319 + if (bank >= sc->sc_nbanks) 320 + return NULL; 321 + 322 + /* We don't support interrupt sharing. */ 323 + if (sc->sc_handlers[irq]) 324 + return NULL; 325 + 326 + ec = HREAD4(sc, GIO_EC(bank)) & ~(1U << pin); 327 + ei = HREAD4(sc, GIO_EI(bank)) & ~(1U << pin); 328 + level = HREAD4(sc, GIO_LEVEL(bank)) & ~(1U << pin); 329 + 330 + switch (type) { 331 + case 1: /* rising */ 332 + ec |= (1U << pin); 333 + edge = 1; 334 + break; 335 + case 2: /* falling */ 336 + edge = 1; 337 + break; 338 + case 3: /* both */ 339 + ei |= (1U << pin); 340 + edge = 1; 341 + break; 342 + case 4: /* high */ 343 + ec |= (1U << pin); 344 + level |= (1U << pin); 345 + break; 346 + case 8: /* low */ 347 + level |= (1U << pin); 348 + break; 349 + default: 350 + return NULL; 351 + } 352 + 353 + ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 354 + ih->ih_func = func; 355 + ih->ih_arg = arg; 356 + ih->ih_ipl = ipl & IPL_IRQMASK; 357 + ih->ih_irq = irq; 358 + ih->ih_edge = edge; 359 + ih->ih_name = name; 360 + ih->ih_sc = sc; 361 + 362 + if (name) 363 + evcount_attach(&ih->ih_count, name, &ih->ih_irq); 364 + 365 + sc->sc_handlers[ih->ih_irq] = ih; 366 + 367 + bcmstbgpio_recalc_ipl(sc); 368 + 369 + HWRITE4(sc, GIO_EC(bank), ec); 370 + HWRITE4(sc, GIO_EI(bank), ei); 371 + HWRITE4(sc, GIO_LEVEL(bank), level); 372 + 373 + HWRITE4(sc, GIO_STAT(bank), 1U << pin); 374 + HSET4(sc, GIO_MASK(bank), 1U << pin); 375 + 376 + return ih; 377 + } 378 + 379 + void 380 + bcmstbgpio_intr_enable(void *cookie) 381 + { 382 + struct intrhand *ih = cookie; 383 + struct bcmstbgpio_softc *sc = ih->ih_sc; 384 + uint32_t bank = ih->ih_irq / 32; 385 + uint32_t pin = ih->ih_irq % 32; 386 + 387 + HSET4(sc, GIO_MASK(bank), 1U << pin); 388 + } 389 + 390 + void 391 + bcmstbgpio_intr_disable(void *cookie) 392 + { 393 + struct intrhand *ih = cookie; 394 + struct bcmstbgpio_softc *sc = ih->ih_sc; 395 + uint32_t bank = ih->ih_irq / 32; 396 + uint32_t pin = ih->ih_irq % 32; 397 + 398 + HCLR4(sc, GIO_MASK(bank), 1U << pin); 399 + } 400 + 401 + void 402 + bcmstbgpio_intr_barrier(void *cookie) 403 + { 404 + struct intrhand *ih = cookie; 405 + struct bcmstbgpio_softc *sc = ih->ih_sc; 406 + 407 + intr_barrier(sc->sc_ih); 157 408 }