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