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