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