1 /*- 2 * Copyright 2003 by Peter Grehan. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 /* 31 * A driver for the PIC found in the Heathrow/Paddington MacIO chips. 32 * This was superseded by an OpenPIC in the Keylargo and beyond 33 * MacIO versions. 34 * 35 * The device is initially located in the Open Firmware device tree 36 * in the earliest stage of the nexus probe. However, no device registers 37 * are touched until the actual h/w is probed later on during the 38 * MacIO probe. At that point, any interrupt sources that were allocated 39 * prior to this are activated. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/module.h> 45 #include <sys/bus.h> 46 #include <sys/conf.h> 47 #include <sys/kernel.h> 48 49 #include <dev/ofw/ofw_bus.h> 50 #include <dev/ofw/openfirm.h> 51 52 #include <machine/bus.h> 53 #include <machine/intr.h> 54 #include <machine/intr_machdep.h> 55 #include <machine/md_var.h> 56 #include <machine/nexusvar.h> 57 #include <machine/pio.h> 58 #include <machine/resource.h> 59 60 #include <vm/vm.h> 61 #include <vm/pmap.h> 62 63 #include <sys/rman.h> 64 65 #include <powerpc/powermac/hrowpicvar.h> 66 67 #include "pic_if.h" 68 69 /* 70 * Device interface. 71 */ 72 static void hrowpic_identify(driver_t *, device_t); 73 static int hrowpic_probe(device_t); 74 static int hrowpic_attach(device_t); 75 76 /* 77 * PIC interface. 78 */ 79 static struct resource *hrowpic_allocate_intr(device_t, device_t, int *, 80 u_long, u_int); 81 static int hrowpic_setup_intr(device_t, device_t, 82 struct resource *, int, driver_intr_t, void *, 83 void **); 84 static int hrowpic_teardown_intr(device_t, device_t, 85 struct resource *, void *); 86 static int hrowpic_release_intr(device_t dev, device_t, int, 87 struct resource *res); 88 89 /* 90 * MacIO interface 91 */ 92 static int hrowpic_macio_probe(device_t); 93 static int hrowpic_macio_attach(device_t); 94 95 /* 96 * Local routines 97 */ 98 static void hrowpic_intr(void); 99 static void hrowpic_ext_enable_irq(uintptr_t); 100 static void hrowpic_ext_disable_irq(uintptr_t); 101 static void hrowpic_toggle_irq(struct hrowpic_softc *sc, int, int); 102 103 /* 104 * Interrupt controller softc. There should only be one. 105 */ 106 static struct hrowpic_softc *hpicsoftc; 107 108 /* 109 * Driver methods. 110 */ 111 static device_method_t hrowpic_methods[] = { 112 /* Device interface */ 113 DEVMETHOD(device_identify, hrowpic_identify), 114 DEVMETHOD(device_probe, hrowpic_probe), 115 DEVMETHOD(device_attach, hrowpic_attach), 116 117 /* PIC interface */ 118 DEVMETHOD(pic_allocate_intr, hrowpic_allocate_intr), 119 DEVMETHOD(pic_setup_intr, hrowpic_setup_intr), 120 DEVMETHOD(pic_teardown_intr, hrowpic_teardown_intr), 121 DEVMETHOD(pic_release_intr, hrowpic_release_intr), 122 123 { 0, 0 } 124 }; 125 126 static driver_t hrowpic_driver = { 127 "hrowpic", 128 hrowpic_methods, 129 sizeof(struct hrowpic_softc) 130 }; 131 132 static devclass_t hrowpic_devclass; 133 134 DRIVER_MODULE(hrowpic, nexus, hrowpic_driver, hrowpic_devclass, 0, 0); 135 136 static void 137 hrowpic_identify(driver_t *driver, device_t parent) 138 { 139 phandle_t chosen, pic; 140 char type[40]; 141 142 chosen = OF_finddevice("/chosen"); 143 if (chosen == -1) 144 return; 145 146 if (OF_getprop(chosen, "interrupt-controller", &pic, 4) != 4) 147 return; 148 149 OF_getprop(pic, "compatible", type, sizeof(type)); 150 if (strcmp(type, "heathrow")) 151 return; 152 153 BUS_ADD_CHILD(parent, 0, "hrowpic", 0); 154 } 155 156 static int 157 hrowpic_probe(device_t dev) 158 { 159 char *name; 160 161 name = nexus_get_name(dev); 162 163 if (strcmp(name, "hrowpic")) 164 return (ENXIO); 165 166 device_set_desc(dev, "Heathrow interrupt controller"); 167 return (0); 168 } 169 170 static int 171 hrowpic_attach(device_t dev) 172 { 173 struct hrowpic_softc *sc; 174 175 sc = device_get_softc(dev); 176 177 sc->sc_rman.rm_type = RMAN_ARRAY; 178 sc->sc_rman.rm_descr = device_get_nameunit(dev); 179 180 if (rman_init(&sc->sc_rman) != 0 || 181 rman_manage_region(&sc->sc_rman, 0, HROWPIC_IRQMAX-1) != 0) { 182 device_printf(dev, "could not set up resource management"); 183 return (ENXIO); 184 } 185 186 nexus_install_intcntlr(dev); 187 intr_init(hrowpic_intr, HROWPIC_IRQMAX, hrowpic_ext_enable_irq, 188 hrowpic_ext_disable_irq); 189 190 KASSERT(hpicsoftc == NULL, ("hrowpic: h/w already probed")); 191 hpicsoftc = sc; 192 193 return (0); 194 } 195 196 /* 197 * PIC interface 198 */ 199 static struct resource * 200 hrowpic_allocate_intr(device_t picdev, device_t child, int *rid, u_long intr, 201 u_int flags) 202 { 203 struct hrowpic_softc *sc; 204 struct resource *rv; 205 int needactivate; 206 207 sc = device_get_softc(picdev); 208 needactivate = flags & RF_ACTIVE; 209 flags &= ~RF_ACTIVE; 210 211 rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child); 212 if (rv == NULL) { 213 device_printf(picdev, "interrupt reservation failed for %s\n", 214 device_get_nameunit(child)); 215 return (NULL); 216 } 217 rman_set_rid(rv, *rid); 218 219 return (rv); 220 } 221 222 static int 223 hrowpic_setup_intr(device_t picdev, device_t child, struct resource *res, 224 int flags, driver_intr_t *intr, void *arg, void **cookiep) 225 { 226 struct hrowpic_softc *sc; 227 u_long start; 228 int error; 229 230 sc = device_get_softc(picdev); 231 start = rman_get_start(res); 232 233 if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 234 flags |= INTR_EXCL; 235 236 /* 237 * We depend here on rman_activate_resource() being idempotent. 238 */ 239 error = rman_activate_resource(res); 240 if (error) 241 return (error); 242 243 error = inthand_add(device_get_nameunit(child), start, intr, arg, 244 flags, cookiep); 245 246 if (!error) { 247 /* 248 * Record irq request, and enable if h/w has been probed 249 */ 250 sc->sc_irq[start] = 1; 251 if (sc->sc_memr) { 252 hrowpic_toggle_irq(sc, start, 1); 253 } 254 } 255 256 return (error); 257 } 258 259 static int 260 hrowpic_teardown_intr(device_t picdev, device_t child, struct resource *res, 261 void *ih) 262 { 263 int error; 264 265 error = rman_deactivate_resource(res); 266 if (error) 267 return (error); 268 269 error = inthand_remove(rman_get_start(res), ih); 270 271 return (error); 272 } 273 274 static int 275 hrowpic_release_intr(device_t picdev, device_t child, int rid, 276 struct resource *res) 277 { 278 int error; 279 280 if (rman_get_flags(res) & RF_ACTIVE) { 281 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 282 if (error) 283 return (error); 284 } 285 286 return (rman_release_resource(res)); 287 } 288 289 /* 290 * Interrupt interface 291 */ 292 static void 293 hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank, 294 u_int32_t val) 295 { 296 if (bank == HPIC_PRIMARY) 297 reg += HPIC_1ST_OFFSET; 298 299 bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 300 301 /* 302 * XXX Issue a read to force the write to complete 303 */ 304 bus_space_read_4(sc->sc_bt, sc->sc_bh, reg); 305 } 306 307 static u_int32_t 308 hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank) 309 { 310 if (bank == HPIC_PRIMARY) 311 reg += HPIC_1ST_OFFSET; 312 313 return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 314 } 315 316 static void 317 hrowpic_clear_all(struct hrowpic_softc *sc) 318 { 319 /* 320 * Disable all interrupt sources and clear outstanding interrupts 321 */ 322 hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0); 323 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 0xffffffff); 324 hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0); 325 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 0xffffffff); 326 } 327 328 static void 329 hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable) 330 { 331 u_int roffset; 332 u_int rbit; 333 334 KASSERT((irq > 0) && (irq < HROWPIC_IRQMAX), ("en irq out of range")); 335 336 /* 337 * Calculate prim/sec register bank for the IRQ, update soft copy, 338 * and enable the IRQ as an interrupt source 339 */ 340 roffset = HPIC_INT_TO_BANK(irq); 341 rbit = HPIC_INT_TO_REGBIT(irq); 342 343 if (enable) 344 sc->sc_softreg[roffset] |= (1 << rbit); 345 else 346 sc->sc_softreg[roffset] &= ~(1 << rbit); 347 348 hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]); 349 } 350 351 static void 352 hrowpic_intr(void) 353 { 354 int irq_lo, irq_hi; 355 int i; 356 struct hrowpic_softc *sc; 357 358 sc = hpicsoftc; 359 360 /* 361 * Loop through both interrupt sources until they are empty. 362 * XXX simplistic code, far from optimal. 363 */ 364 do { 365 irq_lo = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY); 366 if (irq_lo) { 367 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 368 irq_lo); 369 for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) { 370 if (irq_lo & (1 << i)) { 371 /* 372 * Disable IRQ and call handler 373 */ 374 hrowpic_toggle_irq(sc, i, 0); 375 intr_handle(i); 376 } 377 } 378 379 } 380 381 irq_hi = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY); 382 if (irq_hi) { 383 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 384 irq_hi); 385 for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) { 386 if (irq_hi & (1 << i)) { 387 /* 388 * Disable IRQ and call handler 389 */ 390 hrowpic_toggle_irq(sc, 391 i + HROWPIC_IRQ_REGNUM, 0); 392 intr_handle(i + HROWPIC_IRQ_REGNUM); 393 } 394 } 395 } 396 } while (irq_lo && irq_hi); 397 } 398 399 static void 400 hrowpic_ext_enable_irq(uintptr_t irq) 401 { 402 hrowpic_toggle_irq(hpicsoftc, irq, 1); 403 } 404 405 static void 406 hrowpic_ext_disable_irq(uintptr_t irq) 407 { 408 hrowpic_toggle_irq(hpicsoftc, irq, 0); 409 } 410 411 412 /* 413 * MacIO interface 414 */ 415 416 static device_method_t hrowpic_macio_methods[] = { 417 /* Device interface */ 418 DEVMETHOD(device_probe, hrowpic_macio_probe), 419 DEVMETHOD(device_attach, hrowpic_macio_attach), 420 421 { 0, 0 }, 422 }; 423 424 static driver_t hrowpic_macio_driver = { 425 "hrowpicmacio", 426 hrowpic_macio_methods, 427 0 428 }; 429 430 static devclass_t hrowpic_macio_devclass; 431 432 DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver, 433 hrowpic_macio_devclass, 0, 0); 434 435 static int 436 hrowpic_macio_probe(device_t dev) 437 { 438 const char *type = ofw_bus_get_type(dev); 439 440 /* 441 * OpenPIC cells have a type of "open-pic", so this 442 * is sufficient to identify a Heathrow cell 443 */ 444 if (strcmp(type, "interrupt-controller") != 0) 445 return (ENXIO); 446 447 /* 448 * The description was already printed out in the nexus 449 * probe, so don't do it again here 450 */ 451 device_set_desc(dev, "Heathrow MacIO interrupt cell"); 452 device_quiet(dev); 453 return (0); 454 } 455 456 static int 457 hrowpic_macio_attach(device_t dev) 458 { 459 struct hrowpic_softc *sc = hpicsoftc; 460 int rid; 461 int i; 462 463 KASSERT(sc != NULL, ("pic not nexus-probed\n")); 464 sc->sc_maciodev = dev; 465 466 rid = 0; 467 sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 468 RF_ACTIVE); 469 470 if (sc->sc_memr == NULL) { 471 device_printf(dev, "Could not alloc mem resource!\n"); 472 return (ENXIO); 473 } 474 475 sc->sc_bt = rman_get_bustag(sc->sc_memr); 476 sc->sc_bh = rman_get_bushandle(sc->sc_memr); 477 478 hrowpic_clear_all(sc); 479 480 /* 481 * Enable all IRQs that were requested before the h/w 482 * was probed 483 */ 484 for (i = 0; i < HROWPIC_IRQMAX; i++) 485 if (sc->sc_irq[i]) { 486 hrowpic_toggle_irq(sc, i, 1); 487 } 488 489 return (0); 490 } 491