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_filter_t, 83 driver_intr_t, void *, 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_filter_t *filt, driver_intr_t *intr, void *arg, 225 void **cookiep) 226 { 227 struct hrowpic_softc *sc; 228 u_long start; 229 int error; 230 231 sc = device_get_softc(picdev); 232 start = rman_get_start(res); 233 234 if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 235 flags |= INTR_EXCL; 236 237 /* 238 * We depend here on rman_activate_resource() being idempotent. 239 */ 240 error = rman_activate_resource(res); 241 if (error) 242 return (error); 243 244 error = inthand_add(device_get_nameunit(child), start, filt, intr, arg, 245 flags, cookiep); 246 247 if (!error) { 248 /* 249 * Record irq request, and enable if h/w has been probed 250 */ 251 sc->sc_irq[start] = 1; 252 if (sc->sc_memr) { 253 hrowpic_toggle_irq(sc, start, 1); 254 } 255 } 256 257 return (error); 258 } 259 260 static int 261 hrowpic_teardown_intr(device_t picdev, device_t child, struct resource *res, 262 void *ih) 263 { 264 int error; 265 266 error = rman_deactivate_resource(res); 267 if (error) 268 return (error); 269 270 error = inthand_remove(rman_get_start(res), ih); 271 272 return (error); 273 } 274 275 static int 276 hrowpic_release_intr(device_t picdev, device_t child, int rid, 277 struct resource *res) 278 { 279 int error; 280 281 if (rman_get_flags(res) & RF_ACTIVE) { 282 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 283 if (error) 284 return (error); 285 } 286 287 return (rman_release_resource(res)); 288 } 289 290 /* 291 * Interrupt interface 292 */ 293 static void 294 hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank, 295 u_int32_t val) 296 { 297 if (bank == HPIC_PRIMARY) 298 reg += HPIC_1ST_OFFSET; 299 300 bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 301 302 /* 303 * XXX Issue a read to force the write to complete 304 */ 305 bus_space_read_4(sc->sc_bt, sc->sc_bh, reg); 306 } 307 308 static u_int32_t 309 hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank) 310 { 311 if (bank == HPIC_PRIMARY) 312 reg += HPIC_1ST_OFFSET; 313 314 return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 315 } 316 317 static void 318 hrowpic_clear_all(struct hrowpic_softc *sc) 319 { 320 /* 321 * Disable all interrupt sources and clear outstanding interrupts 322 */ 323 hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0); 324 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 0xffffffff); 325 hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0); 326 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 0xffffffff); 327 } 328 329 static void 330 hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable) 331 { 332 u_int roffset; 333 u_int rbit; 334 335 KASSERT((irq > 0) && (irq < HROWPIC_IRQMAX), ("en irq out of range")); 336 337 /* 338 * Calculate prim/sec register bank for the IRQ, update soft copy, 339 * and enable the IRQ as an interrupt source 340 */ 341 roffset = HPIC_INT_TO_BANK(irq); 342 rbit = HPIC_INT_TO_REGBIT(irq); 343 344 if (enable) 345 sc->sc_softreg[roffset] |= (1 << rbit); 346 else 347 sc->sc_softreg[roffset] &= ~(1 << rbit); 348 349 hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]); 350 } 351 352 static void 353 hrowpic_intr(void) 354 { 355 int irq_lo, irq_hi; 356 int i; 357 struct hrowpic_softc *sc; 358 359 sc = hpicsoftc; 360 361 /* 362 * Loop through both interrupt sources until they are empty. 363 * XXX simplistic code, far from optimal. 364 */ 365 do { 366 irq_lo = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY); 367 if (irq_lo) { 368 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 369 irq_lo); 370 for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) { 371 if (irq_lo & (1 << i)) { 372 /* 373 * Disable IRQ and call handler 374 */ 375 hrowpic_toggle_irq(sc, i, 0); 376 intr_handle(i); 377 } 378 } 379 380 } 381 382 irq_hi = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY); 383 if (irq_hi) { 384 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 385 irq_hi); 386 for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) { 387 if (irq_hi & (1 << i)) { 388 /* 389 * Disable IRQ and call handler 390 */ 391 hrowpic_toggle_irq(sc, 392 i + HROWPIC_IRQ_REGNUM, 0); 393 intr_handle(i + HROWPIC_IRQ_REGNUM); 394 } 395 } 396 } 397 } while (irq_lo && irq_hi); 398 } 399 400 static void 401 hrowpic_ext_enable_irq(uintptr_t irq) 402 { 403 hrowpic_toggle_irq(hpicsoftc, irq, 1); 404 } 405 406 static void 407 hrowpic_ext_disable_irq(uintptr_t irq) 408 { 409 hrowpic_toggle_irq(hpicsoftc, irq, 0); 410 } 411 412 413 /* 414 * MacIO interface 415 */ 416 417 static device_method_t hrowpic_macio_methods[] = { 418 /* Device interface */ 419 DEVMETHOD(device_probe, hrowpic_macio_probe), 420 DEVMETHOD(device_attach, hrowpic_macio_attach), 421 422 { 0, 0 }, 423 }; 424 425 static driver_t hrowpic_macio_driver = { 426 "hrowpicmacio", 427 hrowpic_macio_methods, 428 0 429 }; 430 431 static devclass_t hrowpic_macio_devclass; 432 433 DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver, 434 hrowpic_macio_devclass, 0, 0); 435 436 static int 437 hrowpic_macio_probe(device_t dev) 438 { 439 const char *type = ofw_bus_get_type(dev); 440 441 /* 442 * OpenPIC cells have a type of "open-pic", so this 443 * is sufficient to identify a Heathrow cell 444 */ 445 if (strcmp(type, "interrupt-controller") != 0) 446 return (ENXIO); 447 448 /* 449 * The description was already printed out in the nexus 450 * probe, so don't do it again here 451 */ 452 device_set_desc(dev, "Heathrow MacIO interrupt cell"); 453 device_quiet(dev); 454 return (0); 455 } 456 457 static int 458 hrowpic_macio_attach(device_t dev) 459 { 460 struct hrowpic_softc *sc = hpicsoftc; 461 int rid; 462 int i; 463 464 KASSERT(sc != NULL, ("pic not nexus-probed\n")); 465 sc->sc_maciodev = dev; 466 467 rid = 0; 468 sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 469 RF_ACTIVE); 470 471 if (sc->sc_memr == NULL) { 472 device_printf(dev, "Could not alloc mem resource!\n"); 473 return (ENXIO); 474 } 475 476 sc->sc_bt = rman_get_bustag(sc->sc_memr); 477 sc->sc_bh = rman_get_bushandle(sc->sc_memr); 478 479 hrowpic_clear_all(sc); 480 481 /* 482 * Enable all IRQs that were requested before the h/w 483 * was probed 484 */ 485 for (i = 0; i < HROWPIC_IRQMAX; i++) 486 if (sc->sc_irq[i]) { 487 hrowpic_toggle_irq(sc, i, 1); 488 } 489 490 return (0); 491 } 492