hrowpic.c revision 133862
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: head/sys/powerpc/powermac/hrowpic.c 133862 2004-08-16 15:45:27Z marius $ 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 */ 72static void hrowpic_identify(driver_t *, device_t); 73static int hrowpic_probe(device_t); 74static int hrowpic_attach(device_t); 75 76/* 77 * PIC interface. 78 */ 79static struct resource *hrowpic_allocate_intr(device_t, device_t, int *, 80 u_long, u_int); 81static int hrowpic_setup_intr(device_t, device_t, 82 struct resource *, int, driver_intr_t, void *, 83 void **); 84static int hrowpic_teardown_intr(device_t, device_t, 85 struct resource *, void *); 86static int hrowpic_release_intr(device_t dev, device_t, int, 87 struct resource *res); 88 89/* 90 * MacIO interface 91 */ 92static int hrowpic_macio_probe(device_t); 93static int hrowpic_macio_attach(device_t); 94 95/* 96 * Local routines 97 */ 98static void hrowpic_intr(void); 99static void hrowpic_ext_enable_irq(uintptr_t); 100static void hrowpic_ext_disable_irq(uintptr_t); 101static void hrowpic_toggle_irq(struct hrowpic_softc *sc, int, int); 102 103/* 104 * Interrupt controller softc. There should only be one. 105 */ 106static struct hrowpic_softc *hpicsoftc; 107 108/* 109 * Driver methods. 110 */ 111static 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 126static driver_t hrowpic_driver = { 127 "hrowpic", 128 hrowpic_methods, 129 sizeof(struct hrowpic_softc) 130}; 131 132static devclass_t hrowpic_devclass; 133 134DRIVER_MODULE(hrowpic, nexus, hrowpic_driver, hrowpic_devclass, 0, 0); 135 136static void 137hrowpic_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 156static int 157hrowpic_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 170static int 171hrowpic_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 */ 199static struct resource * 200hrowpic_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 221static int 222hrowpic_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 258static int 259hrowpic_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 273static int 274hrowpic_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 */ 291static void 292hrowpic_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 306static u_int32_t 307hrowpic_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 315static void 316hrowpic_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 327static void 328hrowpic_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 350static void 351hrowpic_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 398static void 399hrowpic_ext_enable_irq(uintptr_t irq) 400{ 401 hrowpic_toggle_irq(hpicsoftc, irq, 1); 402} 403 404static void 405hrowpic_ext_disable_irq(uintptr_t irq) 406{ 407 hrowpic_toggle_irq(hpicsoftc, irq, 0); 408} 409 410 411/* 412 * MacIO interface 413 */ 414 415static 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 423static driver_t hrowpic_macio_driver = { 424 "hrowpicmacio", 425 hrowpic_macio_methods, 426 0 427}; 428 429static devclass_t hrowpic_macio_devclass; 430 431DRIVER_MODULE(hrowpicmacio, macio, hrowpic_macio_driver, 432 hrowpic_macio_devclass, 0, 0); 433 434static int 435hrowpic_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 455static int 456hrowpic_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