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