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 OpenFirmware 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 #define __RMAN_RESOURCE_VISIBLE 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/module.h> 46 #include <sys/bus.h> 47 #include <sys/conf.h> 48 #include <sys/kernel.h> 49 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/maciovar.h> 66 #include <powerpc/powermac/hrowpicvar.h> 67 68 #include "pic_if.h" 69 70 /* 71 * Device interface. 72 */ 73 static void hrowpic_identify(driver_t *, device_t); 74 static int hrowpic_probe(device_t); 75 static int hrowpic_attach(device_t); 76 77 /* 78 * PIC interface. 79 */ 80 static struct resource *hrowpic_allocate_intr(device_t, device_t, int *, 81 u_long, u_int); 82 static int hrowpic_setup_intr(device_t, device_t, 83 struct resource *, int, driver_intr_t, void *, 84 void **); 85 static int hrowpic_teardown_intr(device_t, device_t, 86 struct resource *, void *); 87 static int hrowpic_release_intr(device_t dev, device_t, int, 88 struct resource *res); 89 90 /* 91 * MacIO interface 92 */ 93 static int hrowpic_macio_probe(device_t); 94 static int hrowpic_macio_attach(device_t); 95 96 /* 97 * Local routines 98 */ 99 static void hrowpic_intr(void); 100 static void hrowpic_ext_enable_irq(uintptr_t); 101 static void hrowpic_ext_disable_irq(uintptr_t); 102 static void hrowpic_toggle_irq(struct hrowpic_softc *sc, int, int); 103 104 /* 105 * Interrupt controller softc. There should only be one. 106 */ 107 static struct hrowpic_softc *hpicsoftc; 108 109 /* 110 * Driver methods. 111 */ 112 static device_method_t hrowpic_methods[] = { 113 /* Device interface */ 114 DEVMETHOD(device_identify, hrowpic_identify), 115 DEVMETHOD(device_probe, hrowpic_probe), 116 DEVMETHOD(device_attach, hrowpic_attach), 117 118 /* PIC interface */ 119 DEVMETHOD(pic_allocate_intr, hrowpic_allocate_intr), 120 DEVMETHOD(pic_setup_intr, hrowpic_setup_intr), 121 DEVMETHOD(pic_teardown_intr, hrowpic_teardown_intr), 122 DEVMETHOD(pic_release_intr, hrowpic_release_intr), 123 124 { 0, 0 } 125 }; 126 127 static driver_t hrowpic_driver = { 128 "hrowpic", 129 hrowpic_methods, 130 sizeof(struct hrowpic_softc) 131 }; 132 133 static devclass_t hrowpic_devclass; 134 135 DRIVER_MODULE(hrowpic, nexus, hrowpic_driver, hrowpic_devclass, 0, 0); 136 137 static void 138 hrowpic_identify(driver_t *driver, device_t parent) 139 { 140 phandle_t chosen, pic; 141 char type[40]; 142 143 chosen = OF_finddevice("/chosen"); 144 if (chosen == -1) 145 return; 146 147 if (OF_getprop(chosen, "interrupt-controller", &pic, 4) != 4) 148 return; 149 150 OF_getprop(pic, "compatible", type, sizeof(type)); 151 if (strcmp(type, "heathrow")) 152 return; 153 154 BUS_ADD_CHILD(parent, 0, "hrowpic", 0); 155 } 156 157 static int 158 hrowpic_probe(device_t dev) 159 { 160 char *name; 161 162 name = nexus_get_name(dev); 163 164 if (strcmp(name, "hrowpic")) 165 return (ENXIO); 166 167 device_set_desc(dev, "Heathrow interrupt controller"); 168 return (0); 169 } 170 171 static int 172 hrowpic_attach(device_t dev) 173 { 174 struct hrowpic_softc *sc; 175 176 sc = device_get_softc(dev); 177 178 sc->sc_rman.rm_type = RMAN_ARRAY; 179 sc->sc_rman.rm_descr = device_get_nameunit(dev); 180 181 if (rman_init(&sc->sc_rman) != 0 || 182 rman_manage_region(&sc->sc_rman, 0, HROWPIC_IRQMAX-1) != 0) { 183 device_printf(dev, "could not set up resource management"); 184 return (ENXIO); 185 } 186 187 nexus_install_intcntlr(dev); 188 intr_init(hrowpic_intr, HROWPIC_IRQMAX, hrowpic_ext_enable_irq, 189 hrowpic_ext_disable_irq); 190 191 KASSERT(hpicsoftc == NULL, ("hrowpic: h/w already probed")); 192 hpicsoftc = sc; 193 194 return (0); 195 } 196 197 /* 198 * PIC interface 199 */ 200 static struct resource * 201 hrowpic_allocate_intr(device_t picdev, device_t child, int *rid, u_long intr, 202 u_int flags) 203 { 204 struct hrowpic_softc *sc; 205 struct resource *rv; 206 int needactivate; 207 208 sc = device_get_softc(picdev); 209 needactivate = flags & RF_ACTIVE; 210 flags &= ~RF_ACTIVE; 211 212 rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child); 213 if (rv == NULL) { 214 device_printf(picdev, "interrupt reservation failed for %s\n", 215 device_get_nameunit(child)); 216 return (NULL); 217 } 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 int error; 228 229 sc = device_get_softc(picdev); 230 231 if ((res->r_flags & RF_SHAREABLE) == 0) 232 flags |= INTR_EXCL; 233 234 /* 235 * We depend here on rman_activate_resource() being idempotent. 236 */ 237 error = rman_activate_resource(res); 238 if (error) 239 return (error); 240 241 error = inthand_add(device_get_nameunit(child), res->r_start, intr, 242 arg, flags, cookiep); 243 244 if (!error) { 245 /* 246 * Record irq request, and enable if h/w has been probed 247 */ 248 sc->sc_irq[res->r_start] = 1; 249 if (sc->sc_memr) { 250 hrowpic_toggle_irq(sc, res->r_start, 1); 251 } 252 } 253 254 return (error); 255 } 256 257 static int 258 hrowpic_teardown_intr(device_t picdev, device_t child, struct resource *res, 259 void *ih) 260 { 261 int error; 262 263 error = rman_deactivate_resource(res); 264 if (error) 265 return (error); 266 267 error = inthand_remove(res->r_start, ih); 268 269 return (error); 270 } 271 272 static int 273 hrowpic_release_intr(device_t picdev, device_t child, int rid, 274 struct resource *res) 275 { 276 int error; 277 278 if (rman_get_flags(res) & RF_ACTIVE) { 279 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 280 if (error) 281 return (error); 282 } 283 284 return (rman_release_resource(res)); 285 } 286 287 /* 288 * Interrupt interface 289 */ 290 static void 291 hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank, 292 u_int32_t val) 293 { 294 if (bank == HPIC_PRIMARY) 295 reg += HPIC_1ST_OFFSET; 296 297 bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 298 299 /* 300 * XXX Issue a read to force the write to complete 301 */ 302 bus_space_read_4(sc->sc_bt, sc->sc_bh, reg); 303 } 304 305 static u_int32_t 306 hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank) 307 { 308 if (bank == HPIC_PRIMARY) 309 reg += HPIC_1ST_OFFSET; 310 311 return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 312 } 313 314 static void 315 hrowpic_clear_all(struct hrowpic_softc *sc) 316 { 317 /* 318 * Disable all interrupt sources and clear outstanding interrupts 319 */ 320 hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0); 321 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 0xffffffff); 322 hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0); 323 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 0xffffffff); 324 } 325 326 static void 327 hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable) 328 { 329 u_int roffset; 330 u_int rbit; 331 332 KASSERT((irq > 0) && (irq < HROWPIC_IRQMAX), ("en irq out of range")); 333 334 /* 335 * Calculate prim/sec register bank for the IRQ, update soft copy, 336 * and enable the IRQ as an interrupt source 337 */ 338 roffset = HPIC_INT_TO_BANK(irq); 339 rbit = HPIC_INT_TO_REGBIT(irq); 340 341 if (enable) 342 sc->sc_softreg[roffset] |= (1 << rbit); 343 else 344 sc->sc_softreg[roffset] &= ~(1 << rbit); 345 346 hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]); 347 } 348 349 static void 350 hrowpic_intr(void) 351 { 352 int irq_lo, irq_hi; 353 int i; 354 struct hrowpic_softc *sc; 355 356 sc = hpicsoftc; 357 358 /* 359 * Loop through both interrupt sources until they are empty. 360 * XXX simplistic code, far from optimal. 361 */ 362 do { 363 irq_lo = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY); 364 if (irq_lo) { 365 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 366 irq_lo); 367 for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) { 368 if (irq_lo & (1 << i)) { 369 /* 370 * Disable IRQ and call handler 371 */ 372 hrowpic_toggle_irq(sc, i, 0); 373 intr_handle(i); 374 } 375 } 376 377 } 378 379 irq_hi = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY); 380 if (irq_hi) { 381 hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 382 irq_hi); 383 for (i = 0; i < HROWPIC_IRQ_REGNUM; i++) { 384 if (irq_hi & (1 << i)) { 385 /* 386 * Disable IRQ and call handler 387 */ 388 hrowpic_toggle_irq(sc, 389 i + HROWPIC_IRQ_REGNUM, 0); 390 intr_handle(i + HROWPIC_IRQ_REGNUM); 391 } 392 } 393 } 394 } while (irq_lo && irq_hi); 395 } 396 397 static void 398 hrowpic_ext_enable_irq(uintptr_t irq) 399 { 400 hrowpic_toggle_irq(hpicsoftc, irq, 1); 401 } 402 403 static void 404 hrowpic_ext_disable_irq(uintptr_t irq) 405 { 406 hrowpic_toggle_irq(hpicsoftc, irq, 0); 407 } 408 409 410 /* 411 * MacIO interface 412 */ 413 414 static device_method_t hrowpic_macio_methods[] = { 415 /* Device interface */ 416 DEVMETHOD(device_probe, hrowpic_macio_probe), 417 DEVMETHOD(device_attach, hrowpic_macio_attach), 418 419 { 0, 0 }, 420 }; 421 422 static driver_t hrowpic_macio_driver = { 423 "hrowpicmacio", 424 hrowpic_macio_methods, 425 0 426 }; 427 428 static devclass_t hrowpic_macio_devclass; 429 430 DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver, 431 hrowpic_macio_devclass, 0, 0); 432 433 static int 434 hrowpic_macio_probe(device_t dev) 435 { 436 char *type = macio_get_devtype(dev); 437 438 /* 439 * OpenPIC cells have a type of "open-pic", so this 440 * is sufficient to identify a Heathrow cell 441 */ 442 if (strcmp(type, "interrupt-controller") != 0) 443 return (ENXIO); 444 445 /* 446 * The description was already printed out in the nexus 447 * probe, so don't do it again here 448 */ 449 device_set_desc(dev, "Heathrow MacIO interrupt cell"); 450 device_quiet(dev); 451 return (0); 452 } 453 454 static int 455 hrowpic_macio_attach(device_t dev) 456 { 457 struct hrowpic_softc *sc = hpicsoftc; 458 int rid; 459 int i; 460 461 KASSERT(sc != NULL, ("pic not nexus-probed\n")); 462 sc->sc_maciodev = dev; 463 464 rid = 0; 465 sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 466 RF_ACTIVE); 467 468 if (sc->sc_memr == NULL) { 469 device_printf(dev, "Could not alloc mem resource!\n"); 470 return (ENXIO); 471 } 472 473 sc->sc_bt = rman_get_bustag(sc->sc_memr); 474 sc->sc_bh = rman_get_bushandle(sc->sc_memr); 475 476 hrowpic_clear_all(sc); 477 478 /* 479 * Enable all IRQs that were requested before the h/w 480 * was probed 481 */ 482 for (i = 0; i < HROWPIC_IRQMAX; i++) 483 if (sc->sc_irq[i]) { 484 hrowpic_toggle_irq(sc, i, 1); 485 } 486 487 return (0); 488 } 489