/* Copyright (c) 2006 Jon Sevy * * Based on PXA interrupt controller, * Copyright (c) 2002 Genetec Corporation. All rights reserved. * Written by Hiroyuki Bessho for Genetec Corporation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: *This product includes software developed for the NetBSD Project by *Genetec Corporation. * 4. The name of Genetec Corporation may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * IRQ handler for the Agere Vx115 processor. */ #include __KERNEL_RCSID(0, "$NetBSD: vx115_intr.c,v 1.5 2003/07/15 00:24:55 lukem Exp $"); #include #include #include #include #include #include #include #include #include #define DEBUG_INTR #ifdef DEBUG_INTR #define DPRINTF(fmt...) printf(fmt) static void DPRINTF_MASKS(void) { int i; printf("pri hard mask soft mask\n"); for (i = 0; i < NIPL; i++) printf(" %i 0x%08x 0x%08x\n", i, vx115_pic_spl_mask[i], vx115_pic_spl_soft_mask[i]); } #else #define DPRINTF(fmt...) #define DPRINTF_MASKS() #endif struct vx115_pic_softc vx115_pic_init_sc; struct vx115_pic_softc *vx115_pic_sc; /* number of hardware IRQs */ #define NR_IRQS 32 /* Array mapping priorities to associated IRQ number */ /* The [IRQ_XXX_PRIORITY] values are defined in vx115_irq.h, and can */ /* be changed without needing to change this array initialization */ /* Note that these priority levels differ from the system priority */ /* levels (SPLs), and are used just to prioritize between simultaneous */ /* interrupts at the controller. */ static u_int32_t irq_priorities[NR_IRQS] = { 0, [IRQ_TIMER1_PRIORITY] = IRQ_TIMER1, [IRQ_DMA1_ERROR_PRIORITY] = IRQ_DMA1_ERROR, [IRQ_DMA1_PRIORITY] = IRQ_DMA1, [IRQ_DISPLAY_PRIORITY] = IRQ_DISPLAY, [IRQ_DISP_SYNC_PRIORITY] = IRQ_DISP_SYNC, [IRQ_SSP1_PRIORITY] = IRQ_SSP1, [IRQ_SSP3_PRIORITY] = IRQ_SSP3, [IRQ_UART0_WAKE_PRIORITY] = IRQ_UART0_WAKE, [IRQ_UART0_PRIORITY] = IRQ_UART0, [IRQ_SDMCC0_PRIORITY] = IRQ_SDMCC0, [IRQ_SDMCC1_PRIORITY] = IRQ_SDMCC1, [IRQ_I2C_PRIORITY] = IRQ_I2C, [IRQ_KEYPAD_PRIORITY] = IRQ_KEYPAD, [IRQ_GPIOA_PRIORITY] = IRQ_GPIOA, [IRQ_GPIOB_PRIORITY] = IRQ_GPIOB, [IRQ_USB_EXTINT_PRIORITY] = IRQ_USB_EXTINT, [IRQ_USB_OTG_PRIORITY] = IRQ_USB_OTG, [IRQ_DSP_PCU_PRIORITY] = IRQ_DSP_PCU, [IRQ_ARM7_PCU_PRIORITY] = IRQ_ARM7_PCU, [IRQ_SWI_PRIORITY] = IRQ_SWI, [IRQ_CAMERA_PRIORITY] = IRQ_CAMERA, [IRQ_EXT2_PRIORITY] = IRQ_EXT2, [IRQ_EXT3_PRIORITY] = IRQ_EXT3, [IRQ_AGPIOA_PRIORITY] = IRQ_AGPIOA, [IRQ_AGPIOB_PRIORITY] = IRQ_AGPIOB, [IRQ_EXT4_PRIORITY] = IRQ_EXT4, [IRQ_EXT5_PRIORITY] = IRQ_EXT5, [IRQ_EXT6_PRIORITY] = IRQ_EXT6, [IRQ_EXT7_PRIORITY] = IRQ_EXT7, [IRQ_RESERVED24_PRIORITY] = IRQ_RESERVED24, [IRQ_RESERVED27_PRIORITY] = IRQ_RESERVED27 }; /* * PIC autoconf glue */ static int vx115_pic_match(struct device *, struct cfdata *, void *); static void vx115_pic_attach(struct device *, struct device *, void *); CFATTACH_DECL(vx115_pic, sizeof(struct vx115_pic_softc), vx115_pic_match, vx115_pic_attach, NULL, NULL); static int vx115_pic_attached; static int stray_interrupt(void *); static void vx115_pic_init_interrupt_masks(void); /* * interrupt dispatch table. */ #ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ struct intrhand { TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ int (*ih_func)(void *); /* handler */ void *ih_arg; /* arg for handler */ }; #endif static struct { #ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ TAILQ_HEAD(,intrhand) list; #else vx115_irq_handler_t func; #endif void *cookie;/* NULL for stackframe */ /* struct evbnt ev; */ } handler[NR_IRQS]; __volatile int softint_pending; __volatile int current_spl_level; /* interrupt enable masks for each level; used for spl changes */ /* we use separate masks for the hardware and software IRQs, */ /* since the hardware IRQs use 32 bits */ int vx115_pic_spl_mask[NIPL]; int vx115_pic_spl_soft_mask[NIPL]; static int extirq_level[NR_IRQS]; static int vx115_pic_match(struct device *parent, struct cfdata *cf, void *aux) { struct vx115_attach_args *sa = aux; DPRINTF("vx115_pic_match\n"); if (vx115_pic_attached || sa->sa_addr != PIC1_BASE_PHYS) return (0); return (1); } void vx115_pic_attach(struct device *parent, struct device *self, void *aux) { struct vx115_pic_softc *sc = (struct vx115_pic_softc*)self; struct vx115_attach_args *sa = (struct vx115_attach_args*)aux; int i; DPRINTF("vx115_pic_attach\n"); /* get bus space and handle */ sc->sc_iot = sa->sa_iot; if (vx115_pic_sc == NULL) vx115_pic_sc = sc; /* map bus space and get handle */ if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh) != 0) panic("%s: Cannot map registers", self->dv_xname); vx115_pic_attached = 1; /* disable all interrupts */ vx115_write_pic(PIC_ENABLE_CLEAR, INT_REQ_SRC_CLR_ALL); /* clear all interrupts */ vx115_write_pic(PIC_SOURCE_CLEAR, INT_REQ_SRC_CLR_ALL); /* set up controller priority assignment registers */ for (i = 1; i < NR_IRQS; i++) { /* set the priority control register to the associate IRQ number */ vx115_write_pic(PIC_IPCR_1 + ((i-1)<<2), irq_priorities[i]); } /* enable all irq priority levels, but clear FRZ bit */ vx115_write_pic(PIC_PRIORITY_ENABLE_SET, PIC_IPE_ALL); vx115_write_pic(PIC_PRIORITY_ENABLE_CLEAR, PIC_IPE_E0); /* set all handlers initially to dummy handler */ for(i = 0; i < sizeof handler / sizeof handler[0]; ++i) { handler[i].func = stray_interrupt; handler[i].cookie = (void *)(intptr_t) i; extirq_level[i] = IPL_SERIAL; } vx115_pic_init_interrupt_masks(); _splraise(IPL_SERIAL); enable_interrupts(I32_bit); } /* * Invoked very early on from the board-specific initarm(), in order to * allow us to set up softc pointer, which is needed for spl functions. */ void vx115_intr_bootstrap(bus_addr_t addr, bus_size_t size) { /* set up inital softc struct; this will be used only until device formally attached */ /* assign bus tag: standard vx115 bus ops */ vx115_pic_init_sc.sc_iot = &vx115_bs_tag; /* map bus space and set handle */ if (bus_space_map(vx115_pic_init_sc.sc_iot, addr, size, 0, &vx115_pic_init_sc.sc_ioh) != 0) panic("%s: Cannot map initial interrupt softc region", "vx115_intr_bootstrap"); /* assign softc pointer to init softc struct */ vx115_pic_sc = & vx115_pic_init_sc; } static __inline void __raise(int ipl) { if (current_spl_level < ipl) vx115_setipl(ipl); } /* * Map a software interrupt queue to an interrupt priority level. */ static const int si_to_ipl[SI_NQUEUES] = { IPL_SOFT, /* SI_SOFT */ IPL_SOFTCLOCK, /* SI_SOFTCLOCK */ IPL_SOFTNET, /* SI_SOFTNET */ IPL_SOFTSERIAL, /* SI_SOFTSERIAL */ }; /* * called from irq_entry. */ void vx115_irq_dispatcher(void *arg) { struct clockframe *frame = arg; uint32_t irqbits; int irqno; int saved_spl_level; saved_spl_level = current_spl_level; while ((irqbits = vx115_read_pic(PIC_STATUS)) != 0) { /* FOR LATER: handle IRQs in priority order - the PIC does this for us */ irqno = find_first_bit(irqbits); /* raise spl if necessary to stop interrupts of lower priorities */ if (saved_spl_level < extirq_level[irqno]) vx115_setipl(extirq_level[irqno]); (*handler[irqno].func)( (handler[irqno].cookie == 0) ? frame : handler[irqno].cookie ); /* restore spl if it was changed */ if (saved_spl_level < extirq_level[irqno]) vx115_setipl(saved_spl_level); /* clear interrupt */ vx115_write_pic(PIC_SOURCE_CLEAR, (1< high. */ mask_array[IPL_SERIAL] &= mask_array[IPL_HIGH]; /* enable hardware interrupts appropriate to current spl level */ vx115_write_pic(PIC_ENABLE_SET, vx115_pic_spl_mask[current_spl_level]); /* disable hardware interrupts appropriate to current spl level */ vx115_write_pic(PIC_ENABLE_CLEAR, ~vx115_pic_spl_mask[current_spl_level]); restore_interrupts(psw); DPRINTF("vx115_update_intr_masks: irqno %i, level %i\n", irqno, level); DPRINTF_MASKS(); } static void vx115_pic_init_interrupt_masks(void) { DPRINTF("vx115_pic_init_intr_masks\n"); memset(vx115_pic_spl_mask, 0, sizeof(vx115_pic_spl_mask)); memset(vx115_pic_spl_soft_mask, 0, sizeof(vx115_pic_spl_soft_mask)); /* * IPL_NONE has soft interrupts enabled only, at least until * hardware handlers are installed. */ vx115_pic_spl_soft_mask[IPL_NONE] = SI_TO_IRQBIT(SI_SOFT) | SI_TO_IRQBIT(SI_SOFTCLOCK) | SI_TO_IRQBIT(SI_SOFTNET) | SI_TO_IRQBIT(SI_SOFTSERIAL); /* * Initialize the soft interrupt masks to block themselves. */ vx115_pic_spl_soft_mask[IPL_SOFT] = ~SI_TO_IRQBIT(SI_SOFT); vx115_pic_spl_soft_mask[IPL_SOFTCLOCK] = ~SI_TO_IRQBIT(SI_SOFTCLOCK); vx115_pic_spl_soft_mask[IPL_SOFTNET] = ~SI_TO_IRQBIT(SI_SOFTNET); vx115_pic_spl_soft_mask[IPL_SOFTSERIAL] = ~SI_TO_IRQBIT(SI_SOFTSERIAL); vx115_pic_spl_soft_mask[IPL_SOFT] &= vx115_pic_spl_soft_mask[IPL_NONE]; /* * splsoftclock() is the only interface that users of the * generic software interrupt facility have to block their * soft intrs, so splsoftclock() must also block IPL_SOFT. */ vx115_pic_spl_soft_mask[IPL_SOFTCLOCK] &= vx115_pic_spl_soft_mask[IPL_SOFT]; /* * splsoftnet() must also block splsoftclock(), since we don't * want timer-driven network events to occur while we're * processing incoming packets. */ vx115_pic_spl_soft_mask[IPL_SOFTNET] &= vx115_pic_spl_soft_mask[IPL_SOFTCLOCK]; DPRINTF_MASKS(); } void vx115_do_pending(void) { static __cpu_simple_lock_t processing = __SIMPLELOCK_UNLOCKED; int oldirqstate, spl_save; if (__cpu_simple_lock_try(&processing) == 0) return; spl_save = current_spl_level; oldirqstate = disable_interrupts(I32_bit); #define DO_SOFTINT(si,ipl) \ if ((softint_pending & vx115_pic_spl_soft_mask[current_spl_level]) & SI_TO_IRQBIT(si)) { \ softint_pending &= ~SI_TO_IRQBIT(si); \ __raise(ipl); \ restore_interrupts(oldirqstate); \ softintr_dispatch(si); \ oldirqstate = disable_interrupts(I32_bit); \ vx115_setipl(spl_save); \ } do { DO_SOFTINT(SI_SOFTSERIAL,IPL_SOFTSERIAL); DO_SOFTINT(SI_SOFTNET, IPL_SOFTNET); DO_SOFTINT(SI_SOFTCLOCK, IPL_SOFTCLOCK); DO_SOFTINT(SI_SOFT, IPL_SOFT); } while( softint_pending & vx115_pic_spl_soft_mask[current_spl_level] ); __cpu_simple_unlock(&processing); restore_interrupts(oldirqstate); } #undef splx void splx(int ipl) { vx115_splx(ipl); } #undef _splraise int _splraise(int ipl) { return vx115_splraise(ipl); } #undef _spllower int _spllower(int ipl) { return vx115_spllower(ipl); } #undef _setsoftintr void _setsoftintr(int si) { return vx115_setsoftintr(si); } void * vx115_intr_establish(int irqno, int level, int (*func)(void *), void *cookie) { int psw; if (irqno < VX115_IRQ_MIN || irqno >= VX115_IRQ_MAX) panic("intr_establish: bogus irq number %d", irqno); psw = disable_interrupts(I32_bit); handler[irqno].cookie = cookie; handler[irqno].func = func; extirq_level[irqno] = level; vx115_update_intr_masks(irqno, level); restore_interrupts(psw); return (&handler[irqno]); } /* Configure the irq's sense (level or edge) and polarity (low-high or high-low) */ int vx115_configure_irq(unsigned int irq, unsigned int sense, unsigned int polarity) { unsigned int irq_mask; unsigned int irq_enabled; unsigned int offset; /* world's most annoying register offset calculation */ offset = irq << 2; if ((1 <= irq) && (irq <= 7)) { offset += 0xA4; } else if ((8 <= irq) && (irq <= 26)) { offset += 0xE0; } else if ((27 <= irq) && (irq <= 28)) { offset += 0x58; } else if ((29 <= irq) && (irq <= 30)) { offset += 0x64; } else { /* bad irq number supplied */ return -1; } /* create mask for this irq */ irq_mask = 1 << irq; /* get current interrupt enabled state */ irq_enabled = vx115_read_pic(PIC_ENABLE_SET) & irq_mask; /* assign supplied parameters */ vx115_write_pic(offset, sense | polarity | VX115_INT_ENABLED); /* clear the interrupt in case got a false one */ vx115_write_pic(PIC_SOURCE_CLEAR, irq_mask); /* reenable the irq if enabled on entry */ vx115_write_pic(PIC_ENABLE_SET, irq_enabled); return 0; } void vx115_enable_irq(unsigned int irq) { vx115_write_pic(PIC_ENABLE_SET, 1 << irq); } void vx115_disable_irq(unsigned int irq) { vx115_write_pic(PIC_ENABLE_CLEAR, 1 << irq); }