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); 187 sc->sc_nirq = ((val & OPENPIC_FEATURE_LAST_IRQ_MASK) >> 188 OPENPIC_FEATURE_LAST_IRQ_SHIFT); 189 190 device_set_desc(dev, "OpenPIC interrupt controller"); 191 return (0); 192 } 193 194 static int 195 openpic_attach(device_t dev) 196 { 197 struct openpic_softc *sc; 198 u_int32_t irq, x; 199 200 sc = device_get_softc(dev); 201 softc = sc; 202 203 device_printf(dev, 204 "Version %s, supports up to %d CPUs and up to %d irqs\n", 205 sc->sc_version, sc->sc_ncpu+1, sc->sc_nirq+1); 206 207 sc->sc_rman.rm_type = RMAN_ARRAY; 208 sc->sc_rman.rm_descr = device_get_nameunit(dev); 209 210 if (rman_init(&sc->sc_rman) != 0 || 211 rman_manage_region(&sc->sc_rman, 0, sc->sc_nirq - 1) != 0) { 212 device_printf(dev, "could not set up resource management"); 213 return (ENXIO); 214 } 215 216 /* disable all interrupts */ 217 for (irq = 0; irq < sc->sc_nirq; irq++) 218 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); 219 220 openpic_set_priority(sc, 0, 15); 221 222 /* we don't need 8259 passthrough mode */ 223 x = openpic_read(sc, OPENPIC_CONFIG); 224 x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; 225 openpic_write(sc, OPENPIC_CONFIG, x); 226 227 /* send all interrupts to cpu 0 */ 228 for (irq = 0; irq < sc->sc_nirq; irq++) 229 openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0); 230 231 for (irq = 0; irq < sc->sc_nirq; irq++) { 232 x = irq; 233 x |= OPENPIC_IMASK; 234 x |= OPENPIC_POLARITY_POSITIVE; 235 x |= OPENPIC_SENSE_LEVEL; 236 x |= 8 << OPENPIC_PRIORITY_SHIFT; 237 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 238 } 239 240 /* XXX IPI */ 241 /* XXX set spurious intr vector */ 242 243 openpic_set_priority(sc, 0, 0); 244 245 /* clear all pending interrupts */ 246 for (irq = 0; irq < sc->sc_nirq; irq++) { 247 openpic_read_irq(sc, 0); 248 openpic_eoi(sc, 0); 249 } 250 251 for (irq = 0; irq < sc->sc_nirq; irq++) 252 openpic_disable_irq(sc, irq); 253 254 intr_init(openpic_intr, sc->sc_nirq, irq_enable, irq_disable); 255 256 return (0); 257 } 258 259 /* 260 * PIC interface 261 */ 262 263 static struct resource * 264 openpic_allocate_intr(device_t dev, device_t child, int *rid, u_long intr, 265 u_int flags) 266 { 267 struct openpic_softc *sc; 268 struct resource *rv; 269 int needactivate; 270 271 sc = device_get_softc(dev); 272 needactivate = flags & RF_ACTIVE; 273 flags &= ~RF_ACTIVE; 274 275 rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child); 276 if (rv == NULL) { 277 device_printf(dev, "interrupt reservation failed for %s\n", 278 device_get_nameunit(child)); 279 return (NULL); 280 } 281 282 if (needactivate) { 283 if (bus_activate_resource(child, SYS_RES_IRQ, *rid, rv) != 0) { 284 device_printf(dev, 285 "resource activation failed for %s\n", 286 device_get_nameunit(child)); 287 rman_release_resource(rv); 288 return (NULL); 289 } 290 } 291 292 return (rv); 293 } 294 295 static int 296 openpic_setup_intr(device_t dev, device_t child, struct resource *res, 297 int flags, driver_intr_t *intr, void *arg, void **cookiep) 298 { 299 struct openpic_softc *sc; 300 int error; 301 302 sc = device_get_softc(dev); 303 304 if (res == NULL) { 305 device_printf(dev, "null interrupt resource from %s\n", 306 device_get_nameunit(child)); 307 return (EINVAL); 308 } 309 310 if ((res->r_flags & RF_SHAREABLE) == 0) 311 flags |= INTR_EXCL; 312 313 /* 314 * We depend here on rman_activate_resource() being idempotent. 315 */ 316 error = rman_activate_resource(res); 317 if (error) 318 return (error); 319 320 error = inthand_add(device_get_nameunit(child), res->r_start, intr, 321 arg, flags, cookiep); 322 openpic_enable_irq(sc, res->r_start, IST_LEVEL); 323 324 return (error); 325 } 326 327 static int 328 openpic_teardown_intr(device_t dev, device_t child, struct resource *res, 329 void *ih) 330 { 331 int error; 332 333 error = rman_deactivate_resource(res); 334 if (error) 335 return (error); 336 337 error = inthand_remove(res->r_start, ih); 338 339 return (error); 340 } 341 342 static int 343 openpic_release_intr(device_t dev, device_t child, int rid, 344 struct resource *res) 345 { 346 int error; 347 348 if (rman_get_flags(res) & RF_ACTIVE) { 349 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 350 if (error) 351 return (error); 352 } 353 354 return (rman_release_resource(res)); 355 } 356 357 /* 358 * Local routines 359 */ 360 361 static u_int 362 openpic_read(struct openpic_softc *sc, int reg) 363 { 364 volatile unsigned char *addr; 365 366 addr = (unsigned char *)sc->sc_base + reg; 367 #if 0 368 printf("openpic: reading from %p (0x%08x + 0x%08x)\n", addr, 369 sc->sc_base, reg); 370 #endif 371 372 return in32rb(addr); 373 } 374 375 static void 376 openpic_write(struct openpic_softc *sc, int reg, u_int val) 377 { 378 volatile unsigned char *addr; 379 380 addr = (unsigned char *)sc->sc_base + reg; 381 #if 0 382 printf("openpic: writing to %p (0x%08x + 0x%08x)\n", addr, sc->sc_base, 383 reg); 384 #endif 385 386 out32rb(addr, val); 387 } 388 389 static int 390 openpic_read_irq(struct openpic_softc *sc, int cpu) 391 { 392 return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 393 } 394 395 static void 396 openpic_eoi(struct openpic_softc *sc, int cpu) 397 { 398 openpic_write(sc, OPENPIC_EOI(cpu), 0); 399 if (!sc->sc_psim) { 400 /* 401 * Probably not needed, since appropriate eieio/sync 402 * is done in out32rb. See Darwin src. 403 */ 404 openpic_read(sc, OPENPIC_EOI(cpu)); 405 } 406 } 407 408 static void 409 openpic_enable_irq(struct openpic_softc *sc, int irq, int type) 410 { 411 u_int x; 412 413 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 414 x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE); 415 if (type == IST_LEVEL) 416 x |= OPENPIC_SENSE_LEVEL; 417 else 418 x |= OPENPIC_SENSE_EDGE; 419 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 420 } 421 422 static void 423 openpic_disable_irq(struct openpic_softc *sc, int irq) 424 { 425 u_int x; 426 427 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 428 x |= OPENPIC_IMASK; 429 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 430 } 431 432 static void 433 openpic_set_priority(struct openpic_softc *sc, int cpu, int pri) 434 { 435 u_int x; 436 437 x = openpic_read(sc, OPENPIC_CPU_PRIORITY(cpu)); 438 x &= ~OPENPIC_CPU_PRIORITY_MASK; 439 x |= pri; 440 openpic_write(sc, OPENPIC_CPU_PRIORITY(cpu), x); 441 } 442 443 static void 444 openpic_intr(void) 445 { 446 int irq; 447 u_int32_t msr; 448 449 msr = mfmsr(); 450 451 irq = openpic_read_irq(softc, 0); 452 if (irq == 255) { 453 return; 454 } 455 456 start: 457 openpic_disable_irq(softc, irq); 458 /*mtmsr(msr | PSL_EE);*/ 459 460 /* do the interrupt thang */ 461 intr_handle(irq); 462 463 mtmsr(msr); 464 465 openpic_eoi(softc, 0); 466 467 irq = openpic_read_irq(softc, 0); 468 if (irq != 255) 469 goto start; 470 } 471 472 static void 473 irq_enable(int irq) 474 { 475 476 openpic_enable_irq(softc, irq, IST_LEVEL); 477 } 478 479 static void 480 irq_disable(int irq) 481 { 482 483 openpic_disable_irq(softc, irq); 484 } 485