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