1 /* 2 * Copyright (C) 2002 Benno Rice. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/bus.h> 31 #include <sys/conf.h> 32 #include <sys/kernel.h> 33 34 #include <dev/ofw/openfirm.h> 35 #include <dev/ofw/ofw_pci.h> 36 37 #include <dev/pci/pcivar.h> 38 #include <dev/pci/pcireg.h> 39 40 #include <machine/bus.h> 41 #include <machine/intr.h> 42 #include <machine/intr_machdep.h> 43 #include <machine/md_var.h> 44 #include <machine/nexusvar.h> 45 #include <machine/pio.h> 46 #include <machine/resource.h> 47 48 #include <vm/vm.h> 49 #include <vm/pmap.h> 50 51 #include <sys/rman.h> 52 53 #include <machine/openpicreg.h> 54 #include <machine/openpicvar.h> 55 56 #include "pic_if.h" 57 58 /* 59 * Device interface. 60 */ 61 static int openpic_probe(device_t); 62 static int openpic_attach(device_t); 63 64 /* 65 * PIC interface. 66 */ 67 static struct resource *openpic_allocate_intr(device_t, device_t, int *, 68 u_long, u_int); 69 static int openpic_setup_intr(device_t, device_t, 70 struct resource *, int, driver_intr_t, void *, 71 void **); 72 static int openpic_teardown_intr(device_t, device_t, 73 struct resource *, void *); 74 static int openpic_release_intr(device_t dev, device_t, int, 75 struct resource *res); 76 77 /* 78 * Local routines 79 */ 80 static u_int openpic_read(struct openpic_softc *, int); 81 static void openpic_write(struct openpic_softc *, int, u_int); 82 static int openpic_read_irq(struct openpic_softc *, int); 83 static void openpic_eoi(struct openpic_softc *, int); 84 static void openpic_enable_irq(struct openpic_softc *, int, int); 85 static void openpic_disable_irq(struct openpic_softc *, int); 86 static void openpic_set_priority(struct openpic_softc *, int, int); 87 static void openpic_intr(void); 88 static void irq_enable(int); 89 static void irq_disable(int); 90 91 /* 92 * Driver methods. 93 */ 94 static device_method_t openpic_methods[] = { 95 /* Device interface */ 96 DEVMETHOD(device_probe, openpic_probe), 97 DEVMETHOD(device_attach, openpic_attach), 98 99 /* PIC interface */ 100 DEVMETHOD(pic_allocate_intr, openpic_allocate_intr), 101 DEVMETHOD(pic_setup_intr, openpic_setup_intr), 102 DEVMETHOD(pic_teardown_intr, openpic_teardown_intr), 103 DEVMETHOD(pic_release_intr, openpic_release_intr), 104 105 { 0, 0 } 106 }; 107 108 static driver_t openpic_driver = { 109 "openpic", 110 openpic_methods, 111 sizeof(struct openpic_softc) 112 }; 113 114 static devclass_t openpic_devclass; 115 116 DRIVER_MODULE(openpic, nexus, openpic_driver, openpic_devclass, 0, 0); 117 118 static struct openpic_softc *softc; /* XXX This limits us to one openpic */ 119 120 /* 121 * Device interface 122 */ 123 124 static int 125 openpic_probe(device_t dev) 126 { 127 struct openpic_softc *sc; 128 phandle_t node, parent; 129 char *type; 130 char *compat; 131 u_int32_t reg[5], val; 132 vm_offset_t macio_base; 133 vm_offset_t opic_base; 134 135 sc = device_get_softc(dev); 136 node = nexus_get_node(dev); 137 type = nexus_get_device_type(dev); 138 compat = nexus_get_compatible(dev); 139 140 if (type == NULL) 141 return (ENXIO); 142 143 if (strcmp(type, "open-pic") != 0) 144 return (ENXIO); 145 146 if (strcmp(compat, "psim,open-pic") == 0) { 147 sc->sc_psim = 1; 148 149 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) 150 return (ENXIO); 151 152 opic_base = reg[1]; 153 } else { 154 parent = OF_parent(node); 155 if (OF_getprop(parent, "assigned-addresses", 156 reg, sizeof(reg)) < 20) 157 return (ENXIO); 158 159 macio_base = (vm_offset_t)reg[2]; 160 161 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) 162 return (ENXIO); 163 164 opic_base = macio_base + reg[0]; 165 } 166 167 sc->sc_base = (vm_offset_t)pmap_mapdev(opic_base, OPENPIC_SIZE); 168 169 val = openpic_read(sc, OPENPIC_FEATURE); 170 switch (val & OPENPIC_FEATURE_VERSION_MASK) { 171 case 1: 172 sc->sc_version = "1.0"; 173 break; 174 case 2: 175 sc->sc_version = "1.2"; 176 break; 177 case 3: 178 sc->sc_version = "1.3"; 179 break; 180 default: 181 sc->sc_version = "unknown"; 182 break; 183 } 184 185 sc->sc_ncpu = ((val & OPENPIC_FEATURE_LAST_CPU_MASK) >> 186 OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; 187 sc->sc_nirq = ((val & OPENPIC_FEATURE_LAST_IRQ_MASK) >> 188 OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; 189 190 /* 191 * PSIM seems to report 1 too many IRQs 192 */ 193 if (sc->sc_psim) 194 sc->sc_nirq--; 195 196 device_set_desc(dev, "OpenPIC interrupt controller"); 197 return (0); 198 } 199 200 static int 201 openpic_attach(device_t dev) 202 { 203 struct openpic_softc *sc; 204 u_int32_t irq, x; 205 206 sc = device_get_softc(dev); 207 softc = sc; 208 209 device_printf(dev, 210 "Version %s, supports %d CPUs and %d irqs\n", 211 sc->sc_version, sc->sc_ncpu, sc->sc_nirq); 212 213 sc->sc_rman.rm_type = RMAN_ARRAY; 214 sc->sc_rman.rm_descr = device_get_nameunit(dev); 215 216 if (rman_init(&sc->sc_rman) != 0 || 217 rman_manage_region(&sc->sc_rman, 0, sc->sc_nirq - 1) != 0) { 218 device_printf(dev, "could not set up resource management"); 219 return (ENXIO); 220 } 221 222 /* disable all interrupts */ 223 for (irq = 0; irq < sc->sc_nirq; irq++) 224 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); 225 226 openpic_set_priority(sc, 0, 15); 227 228 /* we don't need 8259 passthrough mode */ 229 x = openpic_read(sc, OPENPIC_CONFIG); 230 x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; 231 openpic_write(sc, OPENPIC_CONFIG, x); 232 233 /* send all interrupts to cpu 0 */ 234 for (irq = 0; irq < sc->sc_nirq; irq++) 235 openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0); 236 237 for (irq = 0; irq < sc->sc_nirq; irq++) { 238 x = irq; 239 x |= OPENPIC_IMASK; 240 x |= OPENPIC_POLARITY_POSITIVE; 241 x |= OPENPIC_SENSE_LEVEL; 242 x |= 8 << OPENPIC_PRIORITY_SHIFT; 243 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 244 } 245 246 /* XXX IPI */ 247 /* XXX set spurious intr vector */ 248 249 openpic_set_priority(sc, 0, 0); 250 251 /* clear all pending interrupts */ 252 for (irq = 0; irq < sc->sc_nirq; irq++) { 253 openpic_read_irq(sc, 0); 254 openpic_eoi(sc, 0); 255 } 256 257 for (irq = 0; irq < sc->sc_nirq; irq++) 258 openpic_disable_irq(sc, irq); 259 260 intr_init(openpic_intr, sc->sc_nirq, irq_enable, irq_disable); 261 262 return (0); 263 } 264 265 /* 266 * PIC interface 267 */ 268 269 static struct resource * 270 openpic_allocate_intr(device_t dev, device_t child, int *rid, u_long intr, 271 u_int flags) 272 { 273 struct openpic_softc *sc; 274 struct resource *rv; 275 int needactivate; 276 277 sc = device_get_softc(dev); 278 needactivate = flags & RF_ACTIVE; 279 flags &= ~RF_ACTIVE; 280 281 rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child); 282 if (rv == NULL) { 283 device_printf(dev, "interrupt reservation failed for %s\n", 284 device_get_nameunit(child)); 285 return (NULL); 286 } 287 288 if (needactivate) { 289 if (bus_activate_resource(child, SYS_RES_IRQ, *rid, rv) != 0) { 290 device_printf(dev, 291 "resource activation failed for %s\n", 292 device_get_nameunit(child)); 293 rman_release_resource(rv); 294 return (NULL); 295 } 296 } 297 298 return (rv); 299 } 300 301 static int 302 openpic_setup_intr(device_t dev, device_t child, struct resource *res, 303 int flags, driver_intr_t *intr, void *arg, void **cookiep) 304 { 305 struct openpic_softc *sc; 306 int error; 307 308 sc = device_get_softc(dev); 309 310 if (res == NULL) { 311 device_printf(dev, "null interrupt resource from %s\n", 312 device_get_nameunit(child)); 313 return (EINVAL); 314 } 315 316 if ((res->r_flags & RF_SHAREABLE) == 0) 317 flags |= INTR_EXCL; 318 319 /* 320 * We depend here on rman_activate_resource() being idempotent. 321 */ 322 error = rman_activate_resource(res); 323 if (error) 324 return (error); 325 326 error = inthand_add(device_get_nameunit(child), res->r_start, intr, 327 arg, flags, cookiep); 328 openpic_enable_irq(sc, res->r_start, IST_LEVEL); 329 330 return (error); 331 } 332 333 static int 334 openpic_teardown_intr(device_t dev, device_t child, struct resource *res, 335 void *ih) 336 { 337 int error; 338 339 error = rman_deactivate_resource(res); 340 if (error) 341 return (error); 342 343 error = inthand_remove(res->r_start, ih); 344 345 return (error); 346 } 347 348 static int 349 openpic_release_intr(device_t dev, device_t child, int rid, 350 struct resource *res) 351 { 352 int error; 353 354 if (rman_get_flags(res) & RF_ACTIVE) { 355 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 356 if (error) 357 return (error); 358 } 359 360 return (rman_release_resource(res)); 361 } 362 363 /* 364 * Local routines 365 */ 366 367 static u_int 368 openpic_read(struct openpic_softc *sc, int reg) 369 { 370 volatile unsigned char *addr; 371 372 addr = (unsigned char *)sc->sc_base + reg; 373 #if 0 374 printf("openpic: reading from %p (0x%08x + 0x%08x)\n", addr, 375 sc->sc_base, reg); 376 #endif 377 378 return in32rb(addr); 379 } 380 381 static void 382 openpic_write(struct openpic_softc *sc, int reg, u_int val) 383 { 384 volatile unsigned char *addr; 385 386 addr = (unsigned char *)sc->sc_base + reg; 387 #if 0 388 printf("openpic: writing to %p (0x%08x + 0x%08x)\n", addr, sc->sc_base, 389 reg); 390 #endif 391 392 out32rb(addr, val); 393 } 394 395 static int 396 openpic_read_irq(struct openpic_softc *sc, int cpu) 397 { 398 return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 399 } 400 401 static void 402 openpic_eoi(struct openpic_softc *sc, int cpu) 403 { 404 openpic_write(sc, OPENPIC_EOI(cpu), 0); 405 if (!sc->sc_psim) { 406 /* 407 * Probably not needed, since appropriate eieio/sync 408 * is done in out32rb. See Darwin src. 409 */ 410 openpic_read(sc, OPENPIC_EOI(cpu)); 411 } 412 } 413 414 static void 415 openpic_enable_irq(struct openpic_softc *sc, int irq, int type) 416 { 417 u_int x; 418 419 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 420 x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE); 421 if (type == IST_LEVEL) 422 x |= OPENPIC_SENSE_LEVEL; 423 else 424 x |= OPENPIC_SENSE_EDGE; 425 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 426 } 427 428 static void 429 openpic_disable_irq(struct openpic_softc *sc, int irq) 430 { 431 u_int x; 432 433 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 434 x |= OPENPIC_IMASK; 435 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 436 } 437 438 static void 439 openpic_set_priority(struct openpic_softc *sc, int cpu, int pri) 440 { 441 u_int x; 442 443 x = openpic_read(sc, OPENPIC_CPU_PRIORITY(cpu)); 444 x &= ~OPENPIC_CPU_PRIORITY_MASK; 445 x |= pri; 446 openpic_write(sc, OPENPIC_CPU_PRIORITY(cpu), x); 447 } 448 449 static void 450 openpic_intr(void) 451 { 452 int irq; 453 u_int32_t msr; 454 455 msr = mfmsr(); 456 457 irq = openpic_read_irq(softc, 0); 458 if (irq == 255) { 459 return; 460 } 461 462 start: 463 openpic_disable_irq(softc, irq); 464 /*mtmsr(msr | PSL_EE);*/ 465 466 /* do the interrupt thang */ 467 intr_handle(irq); 468 469 mtmsr(msr); 470 471 openpic_eoi(softc, 0); 472 473 irq = openpic_read_irq(softc, 0); 474 if (irq != 255) 475 goto start; 476 } 477 478 static void 479 irq_enable(int irq) 480 { 481 482 openpic_enable_irq(softc, irq, IST_LEVEL); 483 } 484 485 static void 486 irq_disable(int irq) 487 { 488 489 openpic_disable_irq(softc, irq); 490 } 491