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