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