Home | History | Annotate | Line # | Download | only in gemini
gemini_icu.c revision 1.2.14.2
      1  1.2.14.2  yamt /*	$NetBSD	*/
      2  1.2.14.2  yamt 
      3  1.2.14.2  yamt /* adapted from:
      4  1.2.14.2  yamt  *	NetBSD: omap2_icu.c,v 1.4 2008/08/27 11:03:10 matt Exp
      5  1.2.14.2  yamt  */
      6  1.2.14.2  yamt 
      7  1.2.14.2  yamt /*
      8  1.2.14.2  yamt  * Define the SDP2430 specific information and then include the generic OMAP
      9  1.2.14.2  yamt  * interrupt header.
     10  1.2.14.2  yamt  */
     11  1.2.14.2  yamt 
     12  1.2.14.2  yamt /*
     13  1.2.14.2  yamt  * Redistribution and use in source and binary forms, with or without
     14  1.2.14.2  yamt  * modification, are permitted provided that the following conditions
     15  1.2.14.2  yamt  * are met:
     16  1.2.14.2  yamt  * 1. Redistributions of source code must retain this list of conditions
     17  1.2.14.2  yamt  *    and the following disclaimer.
     18  1.2.14.2  yamt  * 2. Redistributions in binary form must reproduce this list of conditions
     19  1.2.14.2  yamt  *    and the following disclaimer in the documentation and/or other materials
     20  1.2.14.2  yamt  *    provided with the distribution.
     21  1.2.14.2  yamt  *
     22  1.2.14.2  yamt  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     23  1.2.14.2  yamt  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     24  1.2.14.2  yamt  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
     25  1.2.14.2  yamt  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     26  1.2.14.2  yamt  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     27  1.2.14.2  yamt  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     28  1.2.14.2  yamt  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     29  1.2.14.2  yamt  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     30  1.2.14.2  yamt  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     31  1.2.14.2  yamt  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  1.2.14.2  yamt  */
     33  1.2.14.2  yamt #include "opt_gemini.h"
     34  1.2.14.2  yamt #include "geminiicu.h"
     35  1.2.14.2  yamt 
     36  1.2.14.2  yamt #define _INTR_PRIVATE
     37  1.2.14.2  yamt 
     38  1.2.14.2  yamt #include <sys/cdefs.h>
     39  1.2.14.2  yamt __KERNEL_RCSID(0, "$NetBSD: gemini_icu.c,v 1.2.14.2 2009/05/04 08:10:41 yamt Exp $");
     40  1.2.14.2  yamt 
     41  1.2.14.2  yamt #include <sys/param.h>
     42  1.2.14.2  yamt #include <sys/evcnt.h>
     43  1.2.14.2  yamt 
     44  1.2.14.2  yamt #include <uvm/uvm_extern.h>
     45  1.2.14.2  yamt 
     46  1.2.14.2  yamt #include <machine/intr.h>
     47  1.2.14.2  yamt #include <machine/bus.h>
     48  1.2.14.2  yamt 
     49  1.2.14.2  yamt #include <arm/cpu.h>
     50  1.2.14.2  yamt #include <arm/armreg.h>
     51  1.2.14.2  yamt #include <arm/cpufunc.h>
     52  1.2.14.2  yamt #include <arm/atomic.h>
     53  1.2.14.2  yamt 
     54  1.2.14.2  yamt #include <arm/pic/picvar.h>
     55  1.2.14.2  yamt 
     56  1.2.14.2  yamt #include <arm/gemini/gemini_reg.h>
     57  1.2.14.2  yamt #include <arm/gemini/gemini_obiovar.h>
     58  1.2.14.2  yamt 
     59  1.2.14.2  yamt 
     60  1.2.14.2  yamt #define	INTC_READ(sc, o)		\
     61  1.2.14.2  yamt 	bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o))
     62  1.2.14.2  yamt #define	INTC_WRITE(sc, o, v)	\
     63  1.2.14.2  yamt 	bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), v)
     64  1.2.14.2  yamt 
     65  1.2.14.2  yamt static int geminiicu_match(device_t, cfdata_t, void *);
     66  1.2.14.2  yamt static void geminiicu_attach(device_t, device_t, void *);
     67  1.2.14.2  yamt 
     68  1.2.14.2  yamt static void geminiicu_unblock_irqs(struct pic_softc *, size_t, uint32_t);
     69  1.2.14.2  yamt static void geminiicu_block_irqs(struct pic_softc *, size_t, uint32_t);
     70  1.2.14.2  yamt static void geminiicu_establish_irq(struct pic_softc *, struct intrsource *);
     71  1.2.14.2  yamt static void geminiicu_source_name(struct pic_softc *, int, char *, size_t);
     72  1.2.14.2  yamt 
     73  1.2.14.2  yamt static const struct pic_ops geminiicu_picops = {
     74  1.2.14.2  yamt 	.pic_unblock_irqs = geminiicu_unblock_irqs,
     75  1.2.14.2  yamt 	.pic_block_irqs = geminiicu_block_irqs,
     76  1.2.14.2  yamt 	.pic_establish_irq = geminiicu_establish_irq,
     77  1.2.14.2  yamt 	.pic_source_name = geminiicu_source_name,
     78  1.2.14.2  yamt };
     79  1.2.14.2  yamt 
     80  1.2.14.2  yamt #define	PICTOSOFTC(pic)	\
     81  1.2.14.2  yamt 	((void *)((uintptr_t)(pic) - offsetof(struct geminiicu_softc, sc_pic)))
     82  1.2.14.2  yamt 
     83  1.2.14.2  yamt static struct geminiicu_softc {
     84  1.2.14.2  yamt 	device_t sc_dev;
     85  1.2.14.2  yamt 	bus_space_tag_t sc_memt;
     86  1.2.14.2  yamt 	bus_space_handle_t sc_memh;
     87  1.2.14.2  yamt 	struct pic_softc sc_pic;
     88  1.2.14.2  yamt 	uint32_t sc_enabled_mask;
     89  1.2.14.2  yamt 	uint32_t sc_edge_mask;
     90  1.2.14.2  yamt 	uint32_t sc_edge_rising_mask;
     91  1.2.14.2  yamt 	uint32_t sc_edge_falling_mask;
     92  1.2.14.2  yamt 	uint32_t sc_level_mask;
     93  1.2.14.2  yamt 	uint32_t sc_level_hi_mask;
     94  1.2.14.2  yamt 	uint32_t sc_level_lo_mask;
     95  1.2.14.2  yamt } geminiicu_softc = {
     96  1.2.14.2  yamt 	.sc_pic = {
     97  1.2.14.2  yamt 		.pic_ops = &geminiicu_picops,
     98  1.2.14.2  yamt 		.pic_maxsources = 32,
     99  1.2.14.2  yamt 		.pic_name = "geminiicu",
    100  1.2.14.2  yamt 	},
    101  1.2.14.2  yamt };
    102  1.2.14.2  yamt 
    103  1.2.14.2  yamt static const char * const sources[32] = {
    104  1.2.14.2  yamt 	"ipi(0)",	"gmac0(1)",	"gmac1(2)",	"wdt(3)",
    105  1.2.14.2  yamt 	"ide0(4)",	"ide1(5)",	"raid(6)",	"crypto(7)",
    106  1.2.14.2  yamt 	"pci(8)",	"dma(9)",	"usb0(10)",	"usb1(11)",
    107  1.2.14.2  yamt 	"flash(12)",	"tve(13)",	"timer0(14)",	"timer1(15)",
    108  1.2.14.2  yamt 	"timer2(16)",	"rtc(17)",	"uart(18)",	"lcd(19)",
    109  1.2.14.2  yamt 	"lpc(20)",	"ssp(21)",	"gpio0(22)",	"gpio1(23)",
    110  1.2.14.2  yamt 	"gpio2(24)",	"cir(25)",	"power(26)",	"irq 27",
    111  1.2.14.2  yamt 	"irq 28",	"irq 29",	"usbc0(30)",	"usbc1(31)"
    112  1.2.14.2  yamt };
    113  1.2.14.2  yamt 
    114  1.2.14.2  yamt static void geminiicu_source_name(struct pic_softc *pic, int irq,
    115  1.2.14.2  yamt 	char *buf, size_t len)
    116  1.2.14.2  yamt {
    117  1.2.14.2  yamt 	KASSERT((unsigned int)irq < 32);
    118  1.2.14.2  yamt 	strlcpy(buf, sources[irq], len);
    119  1.2.14.2  yamt }
    120  1.2.14.2  yamt 
    121  1.2.14.2  yamt static void
    122  1.2.14.2  yamt geminiicu_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
    123  1.2.14.2  yamt {
    124  1.2.14.2  yamt 	struct geminiicu_softc * const sc = PICTOSOFTC(pic);
    125  1.2.14.2  yamt 	KASSERT(irqbase == 0 && (irq_mask & sc->sc_enabled_mask) == 0);
    126  1.2.14.2  yamt 	sc->sc_enabled_mask |= irq_mask;
    127  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
    128  1.2.14.2  yamt 	/*
    129  1.2.14.2  yamt 	 * If this is a level source, ack it now.  If it's still asserted
    130  1.2.14.2  yamt 	 * it'll come back.
    131  1.2.14.2  yamt 	 */
    132  1.2.14.2  yamt 	if (irq_mask & sc->sc_level_mask)
    133  1.2.14.2  yamt 		INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR,
    134  1.2.14.2  yamt 		    irq_mask & sc->sc_level_mask);
    135  1.2.14.2  yamt }
    136  1.2.14.2  yamt 
    137  1.2.14.2  yamt static void
    138  1.2.14.2  yamt geminiicu_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
    139  1.2.14.2  yamt {
    140  1.2.14.2  yamt 	struct geminiicu_softc * const sc = PICTOSOFTC(pic);
    141  1.2.14.2  yamt 	KASSERT(irqbase == 0);
    142  1.2.14.2  yamt 
    143  1.2.14.2  yamt 	sc->sc_enabled_mask &= ~irq_mask;
    144  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
    145  1.2.14.2  yamt 	/*
    146  1.2.14.2  yamt 	 * If any of the source are edge triggered, ack them now so
    147  1.2.14.2  yamt 	 * we won't lose them.
    148  1.2.14.2  yamt 	 */
    149  1.2.14.2  yamt 	if (irq_mask & sc->sc_edge_mask)
    150  1.2.14.2  yamt 		INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR,
    151  1.2.14.2  yamt 		    irq_mask & sc->sc_edge_mask);
    152  1.2.14.2  yamt }
    153  1.2.14.2  yamt 
    154  1.2.14.2  yamt /*
    155  1.2.14.2  yamt  * Called with interrupts disabled
    156  1.2.14.2  yamt  */
    157  1.2.14.2  yamt static int
    158  1.2.14.2  yamt find_pending_irqs(struct geminiicu_softc *sc)
    159  1.2.14.2  yamt {
    160  1.2.14.2  yamt 	uint32_t pending = INTC_READ(sc, GEMINI_ICU_IRQ_STATUS);
    161  1.2.14.2  yamt 
    162  1.2.14.2  yamt 	KASSERT((sc->sc_enabled_mask & pending) == pending);
    163  1.2.14.2  yamt 
    164  1.2.14.2  yamt 	if (pending == 0)
    165  1.2.14.2  yamt 		return 0;
    166  1.2.14.2  yamt 
    167  1.2.14.2  yamt 	return pic_mark_pending_sources(&sc->sc_pic, 0, pending);
    168  1.2.14.2  yamt }
    169  1.2.14.2  yamt 
    170  1.2.14.2  yamt void
    171  1.2.14.2  yamt gemini_irq_handler(void *frame)
    172  1.2.14.2  yamt {
    173  1.2.14.2  yamt 	struct cpu_info * const ci = curcpu();
    174  1.2.14.2  yamt 	struct geminiicu_softc * const sc = &geminiicu_softc;
    175  1.2.14.2  yamt 	const int oldipl = ci->ci_cpl;
    176  1.2.14.2  yamt 	const uint32_t oldipl_mask = __BIT(oldipl);
    177  1.2.14.2  yamt 	int ipl_mask = 0;
    178  1.2.14.2  yamt 
    179  1.2.14.2  yamt 	uvmexp.intrs++;
    180  1.2.14.2  yamt 
    181  1.2.14.2  yamt 	KASSERT(sc->sc_enabled_mask != 0);
    182  1.2.14.2  yamt 
    183  1.2.14.2  yamt 	ipl_mask = find_pending_irqs(sc);
    184  1.2.14.2  yamt 
    185  1.2.14.2  yamt 	/*
    186  1.2.14.2  yamt 	 * Record the pending_ipls and deliver them if we can.
    187  1.2.14.2  yamt 	 */
    188  1.2.14.2  yamt 	if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
    189  1.2.14.2  yamt 		pic_do_pending_ints(I32_bit, oldipl, frame);
    190  1.2.14.2  yamt }
    191  1.2.14.2  yamt 
    192  1.2.14.2  yamt void
    193  1.2.14.2  yamt geminiicu_establish_irq(struct pic_softc *pic, struct intrsource *is)
    194  1.2.14.2  yamt {
    195  1.2.14.2  yamt 	struct geminiicu_softc * const sc = PICTOSOFTC(pic);
    196  1.2.14.2  yamt 	const uint32_t irq_mask = __BIT(is->is_irq);
    197  1.2.14.2  yamt 
    198  1.2.14.2  yamt 	KASSERT(is->is_irq < 32);
    199  1.2.14.2  yamt 
    200  1.2.14.2  yamt 	sc->sc_enabled_mask &= ~irq_mask;
    201  1.2.14.2  yamt 	/* Have to do with this interrupt disabled.  */
    202  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
    203  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, irq_mask);
    204  1.2.14.2  yamt 
    205  1.2.14.2  yamt 	sc->sc_edge_rising_mask &= ~irq_mask;
    206  1.2.14.2  yamt 	sc->sc_edge_falling_mask &= ~irq_mask;
    207  1.2.14.2  yamt 	sc->sc_level_lo_mask &= ~irq_mask;
    208  1.2.14.2  yamt 	sc->sc_level_hi_mask &= ~irq_mask;
    209  1.2.14.2  yamt 
    210  1.2.14.2  yamt         switch (is->is_type) {
    211  1.2.14.2  yamt         case IST_LEVEL_LOW: sc->sc_level_lo_mask |= irq_mask; break;
    212  1.2.14.2  yamt         case IST_LEVEL_HIGH: sc->sc_level_hi_mask |= irq_mask; break;
    213  1.2.14.2  yamt         case IST_EDGE_FALLING: sc->sc_edge_falling_mask |= irq_mask; break;
    214  1.2.14.2  yamt         case IST_EDGE_RISING: sc->sc_edge_rising_mask |= irq_mask; break;
    215  1.2.14.2  yamt         }
    216  1.2.14.2  yamt 
    217  1.2.14.2  yamt         sc->sc_edge_mask = sc->sc_edge_rising_mask | sc->sc_edge_falling_mask;
    218  1.2.14.2  yamt         sc->sc_level_mask = sc->sc_level_hi_mask|sc->sc_level_lo_mask;
    219  1.2.14.2  yamt 
    220  1.2.14.2  yamt 	/*
    221  1.2.14.2  yamt 	 * Set the new interrupt mode.
    222  1.2.14.2  yamt 	 */
    223  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, sc->sc_edge_mask);
    224  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL,
    225  1.2.14.2  yamt 	    sc->sc_level_lo_mask | sc->sc_edge_falling_mask);
    226  1.2.14.2  yamt }
    227  1.2.14.2  yamt 
    228  1.2.14.2  yamt int
    229  1.2.14.2  yamt geminiicu_match(device_t parent, cfdata_t cf, void *aux)
    230  1.2.14.2  yamt {
    231  1.2.14.2  yamt 	struct obio_attach_args * const oa = aux;
    232  1.2.14.2  yamt 
    233  1.2.14.2  yamt #if defined(SL3516)
    234  1.2.14.2  yamt 	if ((oa->obio_addr == GEMINI_IC0_BASE)
    235  1.2.14.2  yamt 	||  (oa->obio_addr == GEMINI_IC1_BASE))
    236  1.2.14.2  yamt 		return 1;
    237  1.2.14.2  yamt 
    238  1.2.14.2  yamt 	return 0;
    239  1.2.14.2  yamt #else
    240  1.2.14.2  yamt #error unsupported GEMINI variant
    241  1.2.14.2  yamt #endif
    242  1.2.14.2  yamt }
    243  1.2.14.2  yamt 
    244  1.2.14.2  yamt void
    245  1.2.14.2  yamt geminiicu_attach(device_t parent, device_t self, void *aux)
    246  1.2.14.2  yamt {
    247  1.2.14.2  yamt 	struct obio_attach_args * const oa = aux;
    248  1.2.14.2  yamt 	struct geminiicu_softc * const sc = &geminiicu_softc;
    249  1.2.14.2  yamt 	int error;
    250  1.2.14.2  yamt 
    251  1.2.14.2  yamt 	aprint_normal("\n");
    252  1.2.14.2  yamt 
    253  1.2.14.2  yamt 	sc->sc_memt = oa->obio_iot;
    254  1.2.14.2  yamt 
    255  1.2.14.2  yamt 	error = bus_space_map(sc->sc_memt, oa->obio_addr, 0x1000, 0,
    256  1.2.14.2  yamt 	    &sc->sc_memh);
    257  1.2.14.2  yamt 	if (error)
    258  1.2.14.2  yamt 		panic("failed to map interrupt registers: %d", error);
    259  1.2.14.2  yamt 
    260  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, 0);
    261  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 0xffffffff);
    262  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, 0);
    263  1.2.14.2  yamt 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 0xffffffff);
    264  1.2.14.2  yamt 
    265  1.2.14.2  yamt 	sc->sc_dev = self;
    266  1.2.14.2  yamt 	self->dv_private = sc;
    267  1.2.14.2  yamt 
    268  1.2.14.2  yamt 	pic_add(&sc->sc_pic, 0);
    269  1.2.14.2  yamt }
    270  1.2.14.2  yamt 
    271  1.2.14.2  yamt CFATTACH_DECL_NEW(geminiicu,
    272  1.2.14.2  yamt     0,
    273  1.2.14.2  yamt     geminiicu_match, geminiicu_attach,
    274  1.2.14.2  yamt     NULL, NULL);
    275