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 u_int32_t reg[5], val; 131 vm_offset_t macio_base; 132 133 sc = device_get_softc(dev); 134 node = nexus_get_node(dev); 135 type = nexus_get_device_type(dev); 136 137 if (type == NULL) 138 return (ENXIO); 139 140 if (strcmp(type, "open-pic") != 0) 141 return (ENXIO); 142 143 parent = OF_parent(node); 144 if (OF_getprop(parent, "assigned-addresses", reg, sizeof(reg)) < 20) 145 return (ENXIO); 146 macio_base = (vm_offset_t)reg[2]; 147 148 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8) 149 return (ENXIO); 150 151 sc->sc_base = (vm_offset_t)pmap_mapdev(macio_base + reg[0], 152 OPENPIC_SIZE); 153 154 val = openpic_read(sc, OPENPIC_FEATURE); 155 switch (val & OPENPIC_FEATURE_VERSION_MASK) { 156 case 1: 157 sc->sc_version = "1.0"; 158 break; 159 case 2: 160 sc->sc_version = "1.2"; 161 break; 162 case 3: 163 sc->sc_version = "1.3"; 164 break; 165 default: 166 sc->sc_version = "unknown"; 167 break; 168 } 169 170 sc->sc_ncpu = ((val & OPENPIC_FEATURE_LAST_CPU_MASK) >> 171 OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; 172 sc->sc_nirq = ((val & OPENPIC_FEATURE_LAST_IRQ_MASK) >> 173 OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; 174 175 device_set_desc(dev, "OpenPIC interrupt controller"); 176 return (0); 177 } 178 179 static int 180 openpic_attach(device_t dev) 181 { 182 struct openpic_softc *sc; 183 u_int32_t irq, x; 184 185 sc = device_get_softc(dev); 186 softc = sc; 187 188 device_printf(dev, 189 "Version %s, supports up to %d CPUs and up to %d irqs\n", 190 sc->sc_version, sc->sc_ncpu, sc->sc_nirq); 191 192 sc->sc_rman.rm_type = RMAN_ARRAY; 193 sc->sc_rman.rm_descr = device_get_nameunit(dev); 194 195 if (rman_init(&sc->sc_rman) != 0 || 196 rman_manage_region(&sc->sc_rman, 0, sc->sc_nirq - 1) != 0) { 197 device_printf(dev, "could not set up resource management"); 198 return (ENXIO); 199 } 200 201 /* disable all interrupts */ 202 for (irq = 0; irq < 256; irq++) 203 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); 204 205 openpic_set_priority(sc, 0, 15); 206 207 /* we don't need 8259 passthrough mode */ 208 x = openpic_read(sc, OPENPIC_CONFIG); 209 x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; 210 openpic_write(sc, OPENPIC_CONFIG, x); 211 212 /* send all interrupts to cpu 0 */ 213 for (irq = 0; irq < sc->sc_nirq; irq++) 214 openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0); 215 216 for (irq = 0; irq < sc->sc_nirq; irq++) { 217 x = irq; 218 x |= OPENPIC_IMASK; 219 x |= OPENPIC_POLARITY_POSITIVE; 220 x |= OPENPIC_SENSE_LEVEL; 221 x |= 8 << OPENPIC_PRIORITY_SHIFT; 222 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 223 } 224 225 /* XXX IPI */ 226 /* XXX set spurious intr vector */ 227 228 openpic_set_priority(sc, 0, 0); 229 230 /* clear all pending interrupts */ 231 for (irq = 0; irq < 256; irq++) { 232 openpic_read_irq(sc, 0); 233 openpic_eoi(sc, 0); 234 } 235 236 for (irq = 0; irq < sc->sc_nirq; irq++) 237 openpic_disable_irq(sc, irq); 238 239 intr_init(openpic_intr, sc->sc_nirq, irq_enable, irq_disable); 240 241 return (0); 242 } 243 244 /* 245 * PIC interface 246 */ 247 248 static struct resource * 249 openpic_allocate_intr(device_t dev, device_t child, int *rid, u_long intr, 250 u_int flags) 251 { 252 struct openpic_softc *sc; 253 struct resource *rv; 254 int needactivate; 255 256 sc = device_get_softc(dev); 257 needactivate = flags & RF_ACTIVE; 258 flags &= ~RF_ACTIVE; 259 260 rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child); 261 if (rv == NULL) { 262 device_printf(dev, "interrupt reservation failed for %s\n", 263 device_get_nameunit(child)); 264 return (NULL); 265 } 266 267 if (needactivate) { 268 if (bus_activate_resource(child, SYS_RES_IRQ, *rid, rv) != 0) { 269 device_printf(dev, 270 "resource activation failed for %s\n", 271 device_get_nameunit(child)); 272 rman_release_resource(rv); 273 return (NULL); 274 } 275 } 276 277 return (rv); 278 } 279 280 static int 281 openpic_setup_intr(device_t dev, device_t child, struct resource *res, 282 int flags, driver_intr_t *intr, void *arg, void **cookiep) 283 { 284 struct openpic_softc *sc; 285 int error; 286 287 sc = device_get_softc(dev); 288 289 if (res == NULL) { 290 device_printf(dev, "null interrupt resource from %s\n", 291 device_get_nameunit(child)); 292 return (EINVAL); 293 } 294 295 if ((res->r_flags & RF_SHAREABLE) == 0) 296 flags |= INTR_EXCL; 297 298 /* 299 * We depend here on rman_activate_resource() being idempotent. 300 */ 301 error = rman_activate_resource(res); 302 if (error) 303 return (error); 304 305 error = inthand_add(device_get_nameunit(child), res->r_start, intr, 306 arg, flags, cookiep); 307 openpic_enable_irq(sc, res->r_start, IST_LEVEL); 308 309 return (error); 310 } 311 312 static int 313 openpic_teardown_intr(device_t dev, device_t child, struct resource *res, 314 void *ih) 315 { 316 int error; 317 318 error = rman_deactivate_resource(res); 319 if (error) 320 return (error); 321 322 error = inthand_remove(res->r_start, ih); 323 324 return (error); 325 } 326 327 static int 328 openpic_release_intr(device_t dev, device_t child, int rid, 329 struct resource *res) 330 { 331 int error; 332 333 if (rman_get_flags(res) & RF_ACTIVE) { 334 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 335 if (error) 336 return (error); 337 } 338 339 return (rman_release_resource(res)); 340 } 341 342 /* 343 * Local routines 344 */ 345 346 static u_int 347 openpic_read(struct openpic_softc *sc, int reg) 348 { 349 volatile unsigned char *addr; 350 351 addr = (unsigned char *)sc->sc_base + reg; 352 #if 0 353 printf("openpic: reading from %p (0x%08x + 0x%08x)\n", addr, 354 sc->sc_base, reg); 355 #endif 356 357 return in32rb(addr); 358 } 359 360 static void 361 openpic_write(struct openpic_softc *sc, int reg, u_int val) 362 { 363 volatile unsigned char *addr; 364 365 addr = (unsigned char *)sc->sc_base + reg; 366 #if 0 367 printf("openpic: writing to %p (0x%08x + 0x%08x)\n", addr, sc->sc_base, 368 reg); 369 #endif 370 371 out32rb(addr, val); 372 } 373 374 static int 375 openpic_read_irq(struct openpic_softc *sc, int cpu) 376 { 377 return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 378 } 379 380 static void 381 openpic_eoi(struct openpic_softc *sc, int cpu) 382 { 383 openpic_write(sc, OPENPIC_EOI(cpu), 0); 384 openpic_read(sc, OPENPIC_EOI(cpu)); 385 } 386 387 static void 388 openpic_enable_irq(struct openpic_softc *sc, int irq, int type) 389 { 390 u_int x; 391 392 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 393 x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE); 394 if (type == IST_LEVEL) 395 x |= OPENPIC_SENSE_LEVEL; 396 else 397 x |= OPENPIC_SENSE_EDGE; 398 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 399 } 400 401 static void 402 openpic_disable_irq(struct openpic_softc *sc, int irq) 403 { 404 u_int x; 405 406 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 407 x |= OPENPIC_IMASK; 408 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 409 } 410 411 static void 412 openpic_set_priority(struct openpic_softc *sc, int cpu, int pri) 413 { 414 u_int x; 415 416 x = openpic_read(sc, OPENPIC_CPU_PRIORITY(cpu)); 417 x &= ~OPENPIC_CPU_PRIORITY_MASK; 418 x |= pri; 419 openpic_write(sc, OPENPIC_CPU_PRIORITY(cpu), x); 420 } 421 422 static void 423 openpic_intr(void) 424 { 425 int irq; 426 u_int32_t msr; 427 428 msr = mfmsr(); 429 430 irq = openpic_read_irq(softc, 0); 431 if (irq == 255) { 432 return; 433 } 434 435 start: 436 openpic_disable_irq(softc, irq); 437 mtmsr(msr | PSL_EE); 438 439 /* do the interrupt thang */ 440 intr_handle(irq); 441 442 mtmsr(msr); 443 444 openpic_eoi(softc, 0); 445 446 irq = openpic_read_irq(softc, 0); 447 if (irq != 255) 448 goto start; 449 } 450 451 static void 452 irq_enable(int irq) 453 { 454 455 openpic_enable_irq(softc, irq, IST_LEVEL); 456 } 457 458 static void 459 irq_disable(int irq) 460 { 461 462 openpic_disable_irq(softc, irq); 463 } 464