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