[BACK]Return to i80321_intr.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / xscale

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