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 <machine/bus.h> 35 #include <machine/intr.h> 36 #include <machine/intr_machdep.h> 37 #include <machine/md_var.h> 38 #include <machine/pio.h> 39 #include <machine/resource.h> 40 41 #include <vm/vm.h> 42 #include <vm/pmap.h> 43 44 #include <sys/rman.h> 45 46 #include <machine/openpicreg.h> 47 #include <machine/openpicvar.h> 48 49 #include "pic_if.h" 50 51 /* 52 * Local routines 53 */ 54 static u_int openpic_read(struct openpic_softc *, int); 55 static void openpic_write(struct openpic_softc *, int, u_int); 56 static int openpic_read_irq(struct openpic_softc *, int); 57 static void openpic_eoi(struct openpic_softc *, int); 58 static void openpic_enable_irq(struct openpic_softc *, int, int); 59 static void openpic_disable_irq(struct openpic_softc *, int); 60 static void openpic_set_priority(struct openpic_softc *, int, int); 61 static void openpic_intr(void); 62 static void openpic_ext_enable_irq(uintptr_t); 63 static void openpic_ext_disable_irq(uintptr_t); 64 65 /* XXX This limits us to one openpic */ 66 static struct openpic_softc *openpic_softc; 67 68 /* 69 * Called at nexus-probe time to allow interrupts to be enabled by 70 * devices that are probed before the OpenPIC h/w is probed. 71 */ 72 int 73 openpic_early_attach(device_t dev) 74 { 75 struct openpic_softc *sc; 76 77 sc = device_get_softc(dev); 78 openpic_softc = sc; 79 80 sc->sc_rman.rm_type = RMAN_ARRAY; 81 sc->sc_rman.rm_descr = device_get_nameunit(dev); 82 83 if (rman_init(&sc->sc_rman) != 0 || 84 rman_manage_region(&sc->sc_rman, 0, OPENPIC_IRQMAX-1) != 0) { 85 device_printf(dev, "could not set up resource management"); 86 return (ENXIO); 87 } 88 89 intr_init(openpic_intr, OPENPIC_IRQMAX, openpic_ext_enable_irq, 90 openpic_ext_disable_irq); 91 92 sc->sc_early_done = 1; 93 94 return (0); 95 } 96 97 int 98 openpic_attach(device_t dev) 99 { 100 struct openpic_softc *sc; 101 u_int irq; 102 u_int32_t x; 103 104 sc = device_get_softc(dev); 105 sc->sc_hwprobed = 1; 106 107 if (!sc->sc_early_done) 108 openpic_early_attach(dev); 109 110 x = openpic_read(sc, OPENPIC_FEATURE); 111 switch (x & OPENPIC_FEATURE_VERSION_MASK) { 112 case 1: 113 sc->sc_version = "1.0"; 114 break; 115 case 2: 116 sc->sc_version = "1.2"; 117 break; 118 case 3: 119 sc->sc_version = "1.3"; 120 break; 121 default: 122 sc->sc_version = "unknown"; 123 break; 124 } 125 126 sc->sc_ncpu = ((x & OPENPIC_FEATURE_LAST_CPU_MASK) >> 127 OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; 128 sc->sc_nirq = ((x & OPENPIC_FEATURE_LAST_IRQ_MASK) >> 129 OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; 130 131 /* 132 * PSIM seems to report 1 too many IRQs 133 */ 134 if (sc->sc_psim) 135 sc->sc_nirq--; 136 137 if (bootverbose) 138 device_printf(dev, 139 "Version %s, supports %d CPUs and %d irqs\n", 140 sc->sc_version, sc->sc_ncpu, sc->sc_nirq); 141 142 /* disable all interrupts */ 143 for (irq = 0; irq < sc->sc_nirq; irq++) 144 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); 145 146 openpic_set_priority(sc, 0, 15); 147 148 /* we don't need 8259 passthrough mode */ 149 x = openpic_read(sc, OPENPIC_CONFIG); 150 x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; 151 openpic_write(sc, OPENPIC_CONFIG, x); 152 153 /* send all interrupts to cpu 0 */ 154 for (irq = 0; irq < sc->sc_nirq; irq++) 155 openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0); 156 157 for (irq = 0; irq < sc->sc_nirq; irq++) { 158 x = irq; 159 x |= OPENPIC_IMASK; 160 x |= OPENPIC_POLARITY_POSITIVE; 161 x |= OPENPIC_SENSE_LEVEL; 162 x |= 8 << OPENPIC_PRIORITY_SHIFT; 163 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 164 } 165 166 /* XXX IPI */ 167 /* XXX set spurious intr vector */ 168 169 openpic_set_priority(sc, 0, 0); 170 171 /* clear all pending interrupts */ 172 for (irq = 0; irq < sc->sc_nirq; irq++) { 173 openpic_read_irq(sc, 0); 174 openpic_eoi(sc, 0); 175 } 176 177 /* enable pre-h/w reserved irqs, disable all others */ 178 for (irq = 0; irq < sc->sc_nirq; irq++) 179 if (sc->sc_irqrsv[irq]) 180 openpic_enable_irq(sc, irq, IST_LEVEL); 181 else 182 openpic_disable_irq(sc, irq); 183 184 return (0); 185 } 186 187 /* 188 * PIC interface 189 */ 190 191 struct resource * 192 openpic_allocate_intr(device_t dev, device_t child, int *rid, u_long intr, 193 u_int flags) 194 { 195 struct openpic_softc *sc; 196 struct resource *rv; 197 int needactivate; 198 199 sc = device_get_softc(dev); 200 needactivate = flags & RF_ACTIVE; 201 flags &= ~RF_ACTIVE; 202 203 if (sc->sc_hwprobed && (intr > sc->sc_nirq)) { 204 device_printf(dev, "interrupt reservation %ld out of range\n", 205 intr); 206 return (NULL); 207 } 208 209 rv = rman_reserve_resource(&sc->sc_rman, intr, intr, 1, flags, child); 210 if (rv == NULL) { 211 device_printf(dev, "interrupt reservation failed for %s\n", 212 device_get_nameunit(child)); 213 return (NULL); 214 } 215 rman_set_rid(rv, *rid); 216 if (needactivate) { 217 if (bus_activate_resource(child, SYS_RES_IRQ, *rid, rv) != 0) { 218 device_printf(dev, 219 "resource activation failed for %s\n", 220 device_get_nameunit(child)); 221 rman_release_resource(rv); 222 return (NULL); 223 } 224 } 225 226 return (rv); 227 } 228 229 int 230 openpic_setup_intr(device_t dev, device_t child, struct resource *res, 231 int flags, driver_intr_t *intr, void *arg, void **cookiep) 232 { 233 struct openpic_softc *sc; 234 u_long start; 235 int error; 236 237 sc = device_get_softc(dev); 238 start = rman_get_start(res); 239 240 if (res == NULL) { 241 device_printf(dev, "null interrupt resource from %s\n", 242 device_get_nameunit(child)); 243 return (EINVAL); 244 } 245 246 if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 247 flags |= INTR_EXCL; 248 249 /* 250 * We depend here on rman_activate_resource() being idempotent. 251 */ 252 error = rman_activate_resource(res); 253 if (error) 254 return (error); 255 256 error = inthand_add(device_get_nameunit(child), start, intr, arg, 257 flags, cookiep); 258 259 if (sc->sc_hwprobed) 260 openpic_enable_irq(sc, start, IST_LEVEL); 261 else 262 sc->sc_irqrsv[start] = 1; 263 264 return (error); 265 } 266 267 int 268 openpic_teardown_intr(device_t dev, device_t child, struct resource *res, 269 void *ih) 270 { 271 int error; 272 273 error = rman_deactivate_resource(res); 274 if (error) 275 return (error); 276 277 error = inthand_remove(rman_get_start(res), ih); 278 279 return (error); 280 } 281 282 int 283 openpic_release_intr(device_t dev, device_t child, int rid, 284 struct resource *res) 285 { 286 int error; 287 288 if (rman_get_flags(res) & RF_ACTIVE) { 289 error = bus_deactivate_resource(child, SYS_RES_IRQ, rid, res); 290 if (error) 291 return (error); 292 } 293 294 return (rman_release_resource(res)); 295 } 296 297 /* 298 * Local routines 299 */ 300 301 static u_int 302 openpic_read(struct openpic_softc *sc, int reg) 303 { 304 return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); 305 } 306 307 static void 308 openpic_write(struct openpic_softc *sc, int reg, u_int val) 309 { 310 bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); 311 } 312 313 static int 314 openpic_read_irq(struct openpic_softc *sc, int cpu) 315 { 316 return openpic_read(sc, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 317 } 318 319 static void 320 openpic_eoi(struct openpic_softc *sc, int cpu) 321 { 322 openpic_write(sc, OPENPIC_EOI(cpu), 0); 323 } 324 325 static void 326 openpic_enable_irq(struct openpic_softc *sc, int irq, int type) 327 { 328 u_int x; 329 330 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 331 x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE); 332 if (type == IST_LEVEL) 333 x |= OPENPIC_SENSE_LEVEL; 334 else 335 x |= OPENPIC_SENSE_EDGE; 336 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 337 } 338 339 static void 340 openpic_disable_irq(struct openpic_softc *sc, int irq) 341 { 342 u_int x; 343 344 x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); 345 x |= OPENPIC_IMASK; 346 openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); 347 } 348 349 static void 350 openpic_set_priority(struct openpic_softc *sc, int cpu, int pri) 351 { 352 u_int x; 353 354 x = openpic_read(sc, OPENPIC_CPU_PRIORITY(cpu)); 355 x &= ~OPENPIC_CPU_PRIORITY_MASK; 356 x |= pri; 357 openpic_write(sc, OPENPIC_CPU_PRIORITY(cpu), x); 358 } 359 360 static void 361 openpic_intr(void) 362 { 363 struct openpic_softc *sc; 364 int irq; 365 u_int32_t msr; 366 367 sc = openpic_softc; 368 msr = mfmsr(); 369 370 irq = openpic_read_irq(sc, 0); 371 if (irq == 255) { 372 return; 373 } 374 375 start: 376 openpic_disable_irq(sc, irq); 377 /*mtmsr(msr | PSL_EE);*/ 378 379 /* do the interrupt thang */ 380 intr_handle(irq); 381 382 mtmsr(msr); 383 384 openpic_eoi(sc, 0); 385 386 irq = openpic_read_irq(sc, 0); 387 if (irq != 255) 388 goto start; 389 } 390 391 static void 392 openpic_ext_enable_irq(uintptr_t irq) 393 { 394 if (!openpic_softc->sc_hwprobed) 395 return; 396 397 openpic_enable_irq(openpic_softc, irq, IST_LEVEL); 398 } 399 400 static void 401 openpic_ext_disable_irq(uintptr_t irq) 402 { 403 if (!openpic_softc->sc_hwprobed) 404 return; 405 406 openpic_disable_irq(openpic_softc, irq); 407 } 408