xref: /netbsd-src/sys/arch/arm/gemini/gemini_icu.c (revision 2e6fd77a5cd68ee6bbe1c1a0a63b1cb4300e0b4a)
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 
geminiicu_source_name(struct pic_softc * pic,int irq,char * buf,size_t len)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
geminiicu_unblock_irqs(struct pic_softc * pic,size_t irqbase,uint32_t irq_mask)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
geminiicu_block_irqs(struct pic_softc * pic,size_t irqbase,uint32_t irq_mask)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
find_pending_irqs(struct geminiicu_softc * sc)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
gemini_irq_handler(void * frame)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
geminiicu_establish_irq(struct pic_softc * pic,struct intrsource * is)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
geminiicu_match(device_t parent,cfdata_t cf,void * aux)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
geminiicu_attach(device_t parent,device_t self,void * aux)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