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