openpic.c revision 99723
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: head/sys/powerpc/powerpc/openpic.c 99723 2002-07-10 09:46:24Z benno $ 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 */ 61static int openpic_probe(device_t); 62static int openpic_attach(device_t); 63 64/* 65 * PIC interface. 66 */ 67static struct resource *openpic_allocate_intr(device_t, device_t, int *, 68 u_long, u_int); 69static int openpic_setup_intr(device_t, device_t, 70 struct resource *, int, driver_intr_t, void *, 71 void **); 72static int openpic_teardown_intr(device_t, device_t, 73 struct resource *, void *); 74static int openpic_release_intr(device_t dev, device_t, int, 75 struct resource *res); 76 77/* 78 * Local routines 79 */ 80static u_int openpic_read(struct openpic_softc *, int); 81static void openpic_write(struct openpic_softc *, int, u_int); 82static int openpic_read_irq(struct openpic_softc *, int); 83static void openpic_eoi(struct openpic_softc *, int); 84static void openpic_enable_irq(struct openpic_softc *, int, int); 85static void openpic_disable_irq(struct openpic_softc *, int); 86static void openpic_set_priority(struct openpic_softc *, int, int); 87static void openpic_intr(void); 88static void irq_enable(int); 89static void irq_disable(int); 90 91/* 92 * Driver methods. 93 */ 94static 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 108static driver_t openpic_driver = { 109 "openpic", 110 openpic_methods, 111 sizeof(struct openpic_softc) 112}; 113 114static devclass_t openpic_devclass; 115 116DRIVER_MODULE(openpic, nexus, openpic_driver, openpic_devclass, 0, 0); 117 118static struct openpic_softc *softc; /* XXX This limits us to one openpic */ 119 120/* 121 * Device interface 122 */ 123 124static int 125openpic_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 179static int 180openpic_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 248static struct resource * 249openpic_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 280static int 281openpic_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 312static int 313openpic_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 327static int 328openpic_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 346static u_int 347openpic_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 360static void 361openpic_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 374static int 375openpic_read_irq(struct openpic_softc *sc, int cpu) 376{ 377 return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 378} 379 380static void 381openpic_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 387static void 388openpic_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 401static void 402openpic_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 411static void 412openpic_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 422static void 423openpic_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 435start: 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 451static void 452irq_enable(int irq) 453{ 454 455 openpic_enable_irq(softc, irq, IST_LEVEL); 456} 457 458static void 459irq_disable(int irq) 460{ 461 462 openpic_disable_irq(softc, irq); 463} 464