Home | History | Annotate | Line # | Download | only in gemini
      1 /*	$NetBSD: gemini_icu.c,v 1.7 2022/03/03 06:26:29 riastradh Exp $	*/
      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.7 2022/03/03 06:26:29 riastradh 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 <sys/bus.h>
     48 
     49 #include <arm/cpu.h>
     50 #include <arm/armreg.h>
     51 #include <arm/cpufunc.h>
     52 
     53 #include <arm/pic/picvar.h>
     54 
     55 #include <arm/gemini/gemini_reg.h>
     56 #include <arm/gemini/gemini_obiovar.h>
     57 
     58 
     59 #define	INTC_READ(sc, o)		\
     60 	bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o))
     61 #define	INTC_WRITE(sc, o, v)	\
     62 	bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), v)
     63 
     64 static int geminiicu_match(device_t, cfdata_t, void *);
     65 static void geminiicu_attach(device_t, device_t, void *);
     66 
     67 static void geminiicu_unblock_irqs(struct pic_softc *, size_t, uint32_t);
     68 static void geminiicu_block_irqs(struct pic_softc *, size_t, uint32_t);
     69 static void geminiicu_establish_irq(struct pic_softc *, struct intrsource *);
     70 static void geminiicu_source_name(struct pic_softc *, int, char *, size_t);
     71 
     72 static const struct pic_ops geminiicu_picops = {
     73 	.pic_unblock_irqs = geminiicu_unblock_irqs,
     74 	.pic_block_irqs = geminiicu_block_irqs,
     75 	.pic_establish_irq = geminiicu_establish_irq,
     76 	.pic_source_name = geminiicu_source_name,
     77 };
     78 
     79 #define	PICTOSOFTC(pic)	\
     80 	((void *)((uintptr_t)(pic) - offsetof(struct geminiicu_softc, sc_pic)))
     81 
     82 static struct geminiicu_softc {
     83 	device_t sc_dev;
     84 	bus_space_tag_t sc_memt;
     85 	bus_space_handle_t sc_memh;
     86 	struct pic_softc sc_pic;
     87 	uint32_t sc_enabled_mask;
     88 	uint32_t sc_edge_mask;
     89 	uint32_t sc_edge_rising_mask;
     90 	uint32_t sc_edge_falling_mask;
     91 	uint32_t sc_level_mask;
     92 	uint32_t sc_level_hi_mask;
     93 	uint32_t sc_level_lo_mask;
     94 } geminiicu_softc = {
     95 	.sc_pic = {
     96 		.pic_ops = &geminiicu_picops,
     97 		.pic_maxsources = 32,
     98 		.pic_name = "geminiicu",
     99 	},
    100 };
    101 
    102 static const char * const sources[32] = {
    103 	"ipi(0)",	"gmac0(1)",	"gmac1(2)",	"wdt(3)",
    104 	"ide0(4)",	"ide1(5)",	"raid(6)",	"crypto(7)",
    105 	"pci(8)",	"dma(9)",	"usb0(10)",	"usb1(11)",
    106 	"flash(12)",	"tve(13)",	"timer0(14)",	"timer1(15)",
    107 	"timer2(16)",	"rtc(17)",	"uart(18)",	"lcd(19)",
    108 	"lpc(20)",	"ssp(21)",	"gpio0(22)",	"gpio1(23)",
    109 	"gpio2(24)",	"cir(25)",	"power(26)",	"irq 27",
    110 	"irq 28",	"irq 29",	"usbc0(30)",	"usbc1(31)"
    111 };
    112 
    113 static void geminiicu_source_name(struct pic_softc *pic, int irq,
    114 	char *buf, size_t len)
    115 {
    116 	KASSERT((unsigned int)irq < 32);
    117 	strlcpy(buf, sources[irq], len);
    118 }
    119 
    120 static void
    121 geminiicu_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
    122 {
    123 	struct geminiicu_softc * const sc = PICTOSOFTC(pic);
    124 	KASSERT(irqbase == 0 && (irq_mask & sc->sc_enabled_mask) == 0);
    125 	sc->sc_enabled_mask |= irq_mask;
    126 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
    127 	/*
    128 	 * If this is a level source, ack it now.  If it's still asserted
    129 	 * it'll come back.
    130 	 */
    131 	if (irq_mask & sc->sc_level_mask)
    132 		INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR,
    133 		    irq_mask & sc->sc_level_mask);
    134 }
    135 
    136 static void
    137 geminiicu_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
    138 {
    139 	struct geminiicu_softc * const sc = PICTOSOFTC(pic);
    140 	KASSERT(irqbase == 0);
    141 
    142 	sc->sc_enabled_mask &= ~irq_mask;
    143 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
    144 	/*
    145 	 * If any of the source are edge triggered, ack them now so
    146 	 * we won't lose them.
    147 	 */
    148 	if (irq_mask & sc->sc_edge_mask)
    149 		INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR,
    150 		    irq_mask & sc->sc_edge_mask);
    151 }
    152 
    153 /*
    154  * Called with interrupts disabled
    155  */
    156 static int
    157 find_pending_irqs(struct geminiicu_softc *sc)
    158 {
    159 	uint32_t pending = INTC_READ(sc, GEMINI_ICU_IRQ_STATUS);
    160 
    161 	KASSERT((sc->sc_enabled_mask & pending) == pending);
    162 
    163 	if (pending == 0)
    164 		return 0;
    165 
    166 	return pic_mark_pending_sources(&sc->sc_pic, 0, pending);
    167 }
    168 
    169 void
    170 gemini_irq_handler(void *frame)
    171 {
    172 	struct cpu_info * const ci = curcpu();
    173 	struct geminiicu_softc * const sc = &geminiicu_softc;
    174 	const int oldipl = ci->ci_cpl;
    175 	const uint32_t oldipl_mask = __BIT(oldipl);
    176 	int ipl_mask = 0;
    177 
    178 	ci->ci_data.cpu_nintr++;
    179 
    180 	KASSERT(sc->sc_enabled_mask != 0);
    181 
    182 	ipl_mask = find_pending_irqs(sc);
    183 
    184 	/*
    185 	 * Record the pending_ipls and deliver them if we can.
    186 	 */
    187 	if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
    188 		pic_do_pending_ints(I32_bit, oldipl, frame);
    189 }
    190 
    191 void
    192 geminiicu_establish_irq(struct pic_softc *pic, struct intrsource *is)
    193 {
    194 	struct geminiicu_softc * const sc = PICTOSOFTC(pic);
    195 	const uint32_t irq_mask = __BIT(is->is_irq);
    196 
    197 	KASSERT(is->is_irq < 32);
    198 
    199 	sc->sc_enabled_mask &= ~irq_mask;
    200 	/* Have to do with this interrupt disabled.  */
    201 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
    202 	INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, irq_mask);
    203 
    204 	sc->sc_edge_rising_mask &= ~irq_mask;
    205 	sc->sc_edge_falling_mask &= ~irq_mask;
    206 	sc->sc_level_lo_mask &= ~irq_mask;
    207 	sc->sc_level_hi_mask &= ~irq_mask;
    208 
    209         switch (is->is_type) {
    210         case IST_LEVEL_LOW: sc->sc_level_lo_mask |= irq_mask; break;
    211         case IST_LEVEL_HIGH: sc->sc_level_hi_mask |= irq_mask; break;
    212         case IST_EDGE_FALLING: sc->sc_edge_falling_mask |= irq_mask; break;
    213         case IST_EDGE_RISING: sc->sc_edge_rising_mask |= irq_mask; break;
    214         }
    215 
    216         sc->sc_edge_mask = sc->sc_edge_rising_mask | sc->sc_edge_falling_mask;
    217         sc->sc_level_mask = sc->sc_level_hi_mask|sc->sc_level_lo_mask;
    218 
    219 	/*
    220 	 * Set the new interrupt mode.
    221 	 */
    222 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, sc->sc_edge_mask);
    223 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL,
    224 	    sc->sc_level_lo_mask | sc->sc_edge_falling_mask);
    225 }
    226 
    227 int
    228 geminiicu_match(device_t parent, cfdata_t cf, void *aux)
    229 {
    230 	struct obio_attach_args * const oa = aux;
    231 
    232 #if defined(SL3516)
    233 	if ((oa->obio_addr == GEMINI_IC0_BASE)
    234 	||  (oa->obio_addr == GEMINI_IC1_BASE))
    235 		return 1;
    236 
    237 	return 0;
    238 #else
    239 #error unsupported GEMINI variant
    240 #endif
    241 }
    242 
    243 void
    244 geminiicu_attach(device_t parent, device_t self, void *aux)
    245 {
    246 	struct obio_attach_args * const oa = aux;
    247 	struct geminiicu_softc * const sc = &geminiicu_softc;
    248 	int error;
    249 
    250 	aprint_normal("\n");
    251 
    252 	sc->sc_memt = oa->obio_iot;
    253 
    254 	error = bus_space_map(sc->sc_memt, oa->obio_addr, 0x1000, 0,
    255 	    &sc->sc_memh);
    256 	if (error)
    257 		panic("failed to map interrupt registers: %d", error);
    258 
    259 	INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, 0);
    260 	INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 0xffffffff);
    261 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, 0);
    262 	INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 0xffffffff);
    263 
    264 	sc->sc_dev = self;
    265 	device_set_private(self, sc);
    266 
    267 	pic_add(&sc->sc_pic, 0);
    268 }
    269 
    270 CFATTACH_DECL_NEW(geminiicu,
    271     0,
    272     geminiicu_match, geminiicu_attach,
    273     NULL, NULL);
    274