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