Annotation of sys/arch/arm/xscale/i80321_intr.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: i80321_intr.c,v 1.11 2007/05/19 15:47:16 miod Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/systm.h>
! 21: #include <sys/malloc.h>
! 22: #include <sys/evcount.h>
! 23:
! 24: #include <uvm/uvm.h> /* uvmexp */
! 25:
! 26: #include <machine/intr.h>
! 27:
! 28: #include <arm/cpufunc.h>
! 29: #include <arm/xscale/i80321reg.h>
! 30: #include <arm/xscale/i80321var.h>
! 31:
! 32: /*
! 33: * autoconf glue
! 34: */
! 35: int i80321intc_match(struct device *, void *, void *);
! 36: void i80321intc_attach(struct device *, struct device *, void *);
! 37:
! 38: /* internal functions */
! 39: static void i80321intc_write_intctl(uint32_t mask);
! 40: void i80321intc_write_steer(uint32_t mask);
! 41: uint32_t i80321intc_read_intsrc(void);
! 42: void i80321intc_calc_mask(void);
! 43: void i80321intc_init(void);
! 44: void i80321intc_intr_init(void);
! 45: static void i80321intc_setipl(int new);
! 46: void i80321intc_do_pending(void);
! 47:
! 48: uint32_t i80321intc_imask[NIPL];
! 49: uint32_t i80321intc_smask[NIPL];
! 50:
! 51: #define SI_TO_IRQBIT(x) (1 << (x))
! 52:
! 53: __volatile int current_ipl_level;
! 54: __volatile int softint_pending;
! 55:
! 56: struct cfattach i80321intc_ca = {
! 57: sizeof(struct device), i80321intc_match, i80321intc_attach
! 58: };
! 59:
! 60: struct cfdriver i80321intc_cd = {
! 61: NULL, "i80321intc", DV_DULL
! 62: };
! 63:
! 64: int i80321intc_attached = 0;
! 65:
! 66: int
! 67: i80321intc_match(struct device *parent, void *v, void *aux)
! 68: {
! 69: if (i80321intc_attached == 0)
! 70: return 1;
! 71:
! 72: i80321intc_attached = 1;
! 73: return 0;
! 74: }
! 75:
! 76: void
! 77: i80321intc_attach(struct device *parent, struct device *self, void *args)
! 78: {
! 79: i80321intc_init();
! 80: }
! 81:
! 82: static inline void
! 83: i80321intc_write_intctl(uint32_t mask)
! 84: {
! 85: __asm__ volatile ("mcr p6, 0, %0, c0, c0, 0" : : "r" (mask));
! 86: }
! 87:
! 88: void
! 89: i80321intc_write_steer(uint32_t mask)
! 90: {
! 91: __asm__ volatile ("mcr p6, 0, %0, c4, c0, 0" : : "r" (mask));
! 92: }
! 93:
! 94: uint32_t
! 95: i80321intc_read_intsrc(void)
! 96: {
! 97: uint32_t mask;
! 98: __asm__ volatile ("mrc p6, 0, %0, c8, c0, 0" : "=r" (mask));
! 99: return mask;
! 100: }
! 101:
! 102: static inline void
! 103: i80321intc_setipl(int new)
! 104: {
! 105: int psw;
! 106:
! 107: psw = disable_interrupts(I32_bit);
! 108: current_ipl_level = new;
! 109: i80321intc_write_intctl(i80321intc_imask[new]);
! 110: restore_interrupts(psw);
! 111: }
! 112:
! 113:
! 114: struct intrq i80321_handler[NIRQ];
! 115:
! 116: /*
! 117: * Recompute the irq mask bits.
! 118: * Must be called with interrupts disabled.
! 119: */
! 120: void
! 121: i80321intc_calc_mask(void)
! 122: {
! 123: int irq;
! 124: struct intrhand *ih;
! 125: int i;
! 126:
! 127: for (irq = 0; irq < NIRQ; irq++) {
! 128: int i;
! 129: int max = IPL_NONE;
! 130: int min = IPL_HIGH;
! 131: TAILQ_FOREACH(ih, &i80321_handler[irq].iq_list, ih_list) {
! 132: if (ih->ih_ipl > max)
! 133: max = ih->ih_ipl;
! 134:
! 135: if (ih->ih_ipl < min)
! 136: min = ih->ih_ipl;
! 137: }
! 138:
! 139: i80321_handler[irq].iq_irq = max;
! 140:
! 141: if (max == IPL_NONE)
! 142: min = IPL_NONE; /* interrupt not enabled */
! 143: #if 0
! 144: printf("irq %d: min %x max %x\n", irq, min, max);
! 145: #endif
! 146:
! 147: /* Enable interrupts at lower levels */
! 148: for (i = 0; i < min; i++)
! 149: i80321intc_imask[i] |= (1 << irq);
! 150: /* Disable interrupts at upper levels */
! 151: for (;i <= IPL_HIGH; i++)
! 152: i80321intc_imask[i] &= ~(1 << irq);
! 153: }
! 154: /* initialize soft interrupt mask */
! 155: for (i = IPL_NONE; i <= IPL_HIGH; i++) {
! 156: i80321intc_smask[i] = 0;
! 157: if (i < IPL_SOFT)
! 158: i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFT);
! 159: if (i < IPL_SOFTCLOCK)
! 160: i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFTCLOCK);
! 161: if (i < IPL_SOFTNET)
! 162: i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFTNET);
! 163: if (i < IPL_SOFTSERIAL)
! 164: i80321intc_smask[i] |= SI_TO_IRQBIT(SI_SOFTSERIAL);
! 165: #if 0
! 166: printf("mask[%d]: %x %x\n", i, i80321intc_smask[i],
! 167: i80321intc_imask[i]);
! 168: #endif
! 169: }
! 170:
! 171: i80321intc_setipl(current_ipl_level);
! 172: }
! 173:
! 174: void
! 175: i80321intc_do_pending(void)
! 176: {
! 177: static int processing = 0;
! 178: int oldirqstate, spl_save;
! 179:
! 180: oldirqstate = disable_interrupts(I32_bit);
! 181:
! 182: spl_save = current_ipl_level;
! 183:
! 184: if (processing == 1) {
! 185: restore_interrupts(oldirqstate);
! 186: return;
! 187: }
! 188:
! 189: #define DO_SOFTINT(si, ipl) \
! 190: if ((softint_pending & i80321intc_smask[current_ipl_level]) & \
! 191: SI_TO_IRQBIT(si)) { \
! 192: softint_pending &= ~SI_TO_IRQBIT(si); \
! 193: if (current_ipl_level < ipl) \
! 194: i80321intc_setipl(ipl); \
! 195: restore_interrupts(oldirqstate); \
! 196: softintr_dispatch(si); \
! 197: oldirqstate = disable_interrupts(I32_bit); \
! 198: i80321intc_setipl(spl_save); \
! 199: }
! 200:
! 201: do {
! 202: DO_SOFTINT(SI_SOFTSERIAL, IPL_SOFTSERIAL);
! 203: DO_SOFTINT(SI_SOFTNET, IPL_SOFTNET);
! 204: DO_SOFTINT(SI_SOFTCLOCK, IPL_SOFTCLOCK);
! 205: DO_SOFTINT(SI_SOFT, IPL_SOFT);
! 206: } while (softint_pending & i80321intc_smask[current_ipl_level]);
! 207:
! 208:
! 209: processing = 0;
! 210: restore_interrupts(oldirqstate);
! 211: }
! 212:
! 213: void
! 214: splx(int new)
! 215: {
! 216: i80321intc_setipl(new);
! 217:
! 218: if (softint_pending & i80321intc_smask[current_ipl_level])
! 219: i80321intc_do_pending();
! 220: }
! 221:
! 222: int
! 223: _spllower(int new)
! 224: {
! 225: int old = current_ipl_level;
! 226: splx(new);
! 227: return (old);
! 228: }
! 229:
! 230: int
! 231: _splraise(int new)
! 232: {
! 233: int old;
! 234: old = current_ipl_level;
! 235:
! 236: /*
! 237: * setipl must always be called because there is a race window
! 238: * where the variable is updated before the mask is set
! 239: * an interrupt occurs in that window without the mask always
! 240: * being set, the hardware might not get updated on the next
! 241: * splraise completely messing up spl protection.
! 242: */
! 243: if (old > new)
! 244: new = old;
! 245:
! 246: i80321intc_setipl(new);
! 247:
! 248: return (old);
! 249: }
! 250:
! 251: void
! 252: _setsoftintr(int si)
! 253: {
! 254: int oldirqstate;
! 255:
! 256: oldirqstate = disable_interrupts(I32_bit);
! 257: softint_pending |= SI_TO_IRQBIT(si);
! 258: restore_interrupts(oldirqstate);
! 259:
! 260: /* Process unmasked pending soft interrupts. */
! 261: if (softint_pending & i80321intc_smask[current_ipl_level])
! 262: i80321intc_do_pending();
! 263: }
! 264:
! 265: /*
! 266: * i80321_icu_init:
! 267: *
! 268: * Initialize the i80321 ICU. Called early in bootstrap
! 269: * to make sure the ICU is in a pristine state.
! 270: */
! 271: void
! 272: i80321intc_intr_init(void)
! 273: {
! 274: i80321intc_write_intctl(0);
! 275:
! 276: i80321intc_write_steer(0);
! 277: }
! 278:
! 279: /*
! 280: * i80321_intr_init:
! 281: *
! 282: * Initialize the rest of the interrupt subsystem, making it
! 283: * ready to handle interrupts from devices.
! 284: */
! 285: void
! 286: i80321intc_init(void)
! 287: {
! 288: struct intrq *iq;
! 289: int i;
! 290:
! 291: for (i = 0; i < NIRQ; i++) {
! 292: iq = &i80321_handler[i];
! 293: TAILQ_INIT(&iq->iq_list);
! 294: }
! 295:
! 296: i80321intc_calc_mask();
! 297:
! 298: /* Enable IRQs (don't yet use FIQs). */
! 299: enable_interrupts(I32_bit);
! 300: }
! 301:
! 302: void *
! 303: i80321_intr_establish(int irq, int ipl, int (*func)(void *), void *arg,
! 304: char *name)
! 305: {
! 306: struct intrq *iq;
! 307: struct intrhand *ih;
! 308: uint32_t oldirqstate;
! 309:
! 310: if (irq < 0 || irq > NIRQ)
! 311: panic("i80321_intr_establish: IRQ %d out of range", irq);
! 312:
! 313: ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
! 314: if (ih == NULL)
! 315: return (NULL);
! 316:
! 317: ih->ih_func = func;
! 318: ih->ih_arg = arg;
! 319: ih->ih_ipl = ipl;
! 320: ih->ih_name = name;
! 321: ih->ih_irq = irq;
! 322:
! 323: iq = &i80321_handler[irq];
! 324:
! 325: if (name != NULL)
! 326: evcount_attach(&ih->ih_count, name, (void *)&ih->ih_irq,
! 327: &evcount_intr);
! 328:
! 329: /* All IOP321 interrupts are level-triggered. */
! 330: iq->iq_ist = IST_LEVEL;
! 331:
! 332: oldirqstate = disable_interrupts(I32_bit);
! 333:
! 334: TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
! 335:
! 336: i80321intc_calc_mask();
! 337:
! 338: restore_interrupts(oldirqstate);
! 339:
! 340: return (ih);
! 341: }
! 342:
! 343:
! 344: void
! 345: i80321_intr_disestablish(void *cookie)
! 346: {
! 347: struct intrhand *ih = cookie;
! 348: struct intrq *iq = &i80321_handler[ih->ih_irq];
! 349: int oldirqstate;
! 350:
! 351: oldirqstate = disable_interrupts(I32_bit);
! 352:
! 353: TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
! 354: if (ih->ih_name != NULL)
! 355: evcount_detach(&ih->ih_count);
! 356:
! 357: i80321intc_calc_mask();
! 358:
! 359: restore_interrupts(oldirqstate);
! 360: }
! 361:
! 362: void
! 363: i80321_irq_handler(void *arg)
! 364: {
! 365: struct clockframe *frame = arg;
! 366: uint32_t hwpend;
! 367: int irq;
! 368: int saved_spl_level;
! 369: struct intrhand *ih;
! 370:
! 371: saved_spl_level = current_ipl_level;
! 372:
! 373: /* get pending IRQs */
! 374: hwpend = i80321intc_read_intsrc();
! 375:
! 376: while ((irq = find_first_bit(hwpend)) >= 0) {
! 377: /* XXX: Should we handle IRQs in priority order? */
! 378:
! 379: /* raise spl to stop interrupts of lower priorities */
! 380: if (saved_spl_level < i80321_handler[irq].iq_irq)
! 381: i80321intc_setipl(i80321_handler[irq].iq_irq);
! 382:
! 383: /* Enable interrupt */
! 384: enable_interrupts(I32_bit);
! 385: TAILQ_FOREACH(ih, &i80321_handler[irq].iq_list, ih_list) {
! 386: if ((ih->ih_func)( ih->ih_arg == 0
! 387: ? frame : ih->ih_arg))
! 388: ih->ih_count.ec_count++;
! 389: }
! 390: /* Disable interrupt */
! 391: disable_interrupts(I32_bit);
! 392: hwpend &= ~(1<<irq);
! 393: }
! 394: uvmexp.intrs++;
! 395:
! 396: /* restore spl to that was when this interrupt happen */
! 397: i80321intc_setipl(saved_spl_level);
! 398:
! 399: if(softint_pending & i80321intc_smask[current_ipl_level])
! 400: i80321intc_do_pending();
! 401: }
! 402:
! 403: #ifdef DIAGNOSTIC
! 404: void
! 405: i80321_splassert_check(int wantipl, const char *func)
! 406: {
! 407: int oldipl = current_ipl_level;
! 408:
! 409: if (oldipl < wantipl) {
! 410: splassert_fail(wantipl, oldipl, func);
! 411: /*
! 412: * If the splassert_ctl is set to not panic, raise the ipl
! 413: * in a feeble attempt to reduce damage.
! 414: */
! 415: i80321intc_setipl(wantipl);
! 416: }
! 417: }
! 418: #endif
CVSweb