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