becc_icu.c revision 1.13 1 1.13 skrll /* $NetBSD: becc_icu.c,v 1.13 2012/08/02 15:56:07 skrll Exp $ */
2 1.1 thorpej
3 1.1 thorpej /*
4 1.1 thorpej * Copyright (c) 2002 Wasabi Systems, Inc.
5 1.1 thorpej * All rights reserved.
6 1.1 thorpej *
7 1.1 thorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 1.1 thorpej *
9 1.1 thorpej * Redistribution and use in source and binary forms, with or without
10 1.1 thorpej * modification, are permitted provided that the following conditions
11 1.1 thorpej * are met:
12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright
13 1.1 thorpej * notice, this list of conditions and the following disclaimer.
14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the
16 1.1 thorpej * documentation and/or other materials provided with the distribution.
17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software
18 1.1 thorpej * must display the following acknowledgement:
19 1.1 thorpej * This product includes software developed for the NetBSD Project by
20 1.1 thorpej * Wasabi Systems, Inc.
21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 1.1 thorpej * or promote products derived from this software without specific prior
23 1.1 thorpej * written permission.
24 1.1 thorpej *
25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE.
36 1.1 thorpej */
37 1.1 thorpej
38 1.1 thorpej /*
39 1.1 thorpej * Interrupt support for the ADI Engineering Big Endian Companion Chip.
40 1.1 thorpej */
41 1.3 lukem
42 1.3 lukem #include <sys/cdefs.h>
43 1.13 skrll __KERNEL_RCSID(0, "$NetBSD: becc_icu.c,v 1.13 2012/08/02 15:56:07 skrll Exp $");
44 1.1 thorpej
45 1.1 thorpej #ifndef EVBARM_SPL_NOINLINE
46 1.1 thorpej #define EVBARM_SPL_NOINLINE
47 1.1 thorpej #endif
48 1.1 thorpej
49 1.1 thorpej #include <sys/param.h>
50 1.1 thorpej #include <sys/systm.h>
51 1.1 thorpej #include <sys/malloc.h>
52 1.8 ad #include <sys/bus.h>
53 1.8 ad #include <sys/intr.h>
54 1.1 thorpej
55 1.1 thorpej #include <uvm/uvm_extern.h>
56 1.1 thorpej
57 1.1 thorpej #include <arm/cpufunc.h>
58 1.1 thorpej
59 1.1 thorpej #include <arm/xscale/beccreg.h>
60 1.1 thorpej #include <arm/xscale/beccvar.h>
61 1.1 thorpej
62 1.1 thorpej #include <arm/xscale/i80200reg.h>
63 1.1 thorpej #include <arm/xscale/i80200var.h>
64 1.1 thorpej
65 1.1 thorpej /* Interrupt handler queues. */
66 1.1 thorpej struct intrq intrq[NIRQ];
67 1.1 thorpej
68 1.1 thorpej /* Interrupts to mask at each level. */
69 1.1 thorpej uint32_t becc_imask[NIPL];
70 1.1 thorpej
71 1.1 thorpej /* Interrupts pending. */
72 1.6 perry volatile uint32_t becc_ipending;
73 1.6 perry volatile uint32_t becc_sipending;
74 1.1 thorpej
75 1.1 thorpej /* Software copy of the IRQs we have enabled. */
76 1.6 perry volatile uint32_t intr_enabled;
77 1.1 thorpej
78 1.1 thorpej /* Mask if interrupts steered to FIQs. */
79 1.1 thorpej uint32_t intr_steer;
80 1.1 thorpej
81 1.1 thorpej /*
82 1.1 thorpej * Interrupt bit names.
83 1.1 thorpej * XXX Some of these are BRH-centric.
84 1.1 thorpej */
85 1.11 matt const char * const becc_irqnames[] = {
86 1.1 thorpej "soft",
87 1.1 thorpej "timer A",
88 1.1 thorpej "timer B",
89 1.1 thorpej "irq 3",
90 1.1 thorpej "irq 4",
91 1.1 thorpej "irq 5",
92 1.1 thorpej "irq 6",
93 1.1 thorpej "diagerr",
94 1.1 thorpej "DMA EOT",
95 1.1 thorpej "DMA PERR",
96 1.1 thorpej "DMA TABT",
97 1.1 thorpej "DMA MABT",
98 1.1 thorpej "irq 12",
99 1.1 thorpej "irq 13",
100 1.1 thorpej "irq 14",
101 1.1 thorpej "irq 15",
102 1.1 thorpej "PCI PERR",
103 1.1 thorpej "irq 17",
104 1.1 thorpej "irq 18",
105 1.1 thorpej "PCI SERR",
106 1.1 thorpej "PCI OAPE",
107 1.1 thorpej "PCI OATA",
108 1.1 thorpej "PCI OAMA",
109 1.1 thorpej "irq 23",
110 1.1 thorpej "irq 24",
111 1.1 thorpej "irq 25",
112 1.1 thorpej "irq 26", /* PCI INTA */
113 1.1 thorpej "irq 27", /* PCI INTB */
114 1.1 thorpej "irq 28", /* PCI INTC */
115 1.1 thorpej "irq 29", /* PCI INTD */
116 1.1 thorpej "pushbutton",
117 1.1 thorpej "irq 31",
118 1.1 thorpej };
119 1.1 thorpej
120 1.13 skrll void becc_intr_dispatch(struct trapframe *frame);
121 1.1 thorpej
122 1.6 perry static inline uint32_t
123 1.1 thorpej becc_icsr_read(void)
124 1.1 thorpej {
125 1.1 thorpej uint32_t icsr;
126 1.1 thorpej
127 1.1 thorpej icsr = BECC_CSR_READ(BECC_ICSR);
128 1.1 thorpej
129 1.1 thorpej /*
130 1.1 thorpej * The ICSR register shows bits that are active even if they are
131 1.1 thorpej * masked in ICMR, so we have to mask them off with the interrupts
132 1.1 thorpej * we consider enabled.
133 1.1 thorpej */
134 1.1 thorpej return (icsr & intr_enabled);
135 1.1 thorpej }
136 1.1 thorpej
137 1.6 perry static inline void
138 1.1 thorpej becc_set_intrsteer(void)
139 1.1 thorpej {
140 1.1 thorpej
141 1.1 thorpej BECC_CSR_WRITE(BECC_ICSTR, intr_steer & ICU_VALID_MASK);
142 1.1 thorpej (void) BECC_CSR_READ(BECC_ICSTR);
143 1.1 thorpej }
144 1.1 thorpej
145 1.6 perry static inline void
146 1.1 thorpej becc_enable_irq(int irq)
147 1.1 thorpej {
148 1.1 thorpej
149 1.1 thorpej intr_enabled |= (1U << irq);
150 1.1 thorpej becc_set_intrmask();
151 1.1 thorpej }
152 1.1 thorpej
153 1.6 perry static inline void
154 1.1 thorpej becc_disable_irq(int irq)
155 1.1 thorpej {
156 1.1 thorpej
157 1.1 thorpej intr_enabled &= ~(1U << irq);
158 1.1 thorpej becc_set_intrmask();
159 1.1 thorpej }
160 1.1 thorpej
161 1.1 thorpej /*
162 1.1 thorpej * NOTE: This routine must be called with interrupts disabled in the CPSR.
163 1.1 thorpej */
164 1.1 thorpej static void
165 1.1 thorpej becc_intr_calculate_masks(void)
166 1.1 thorpej {
167 1.1 thorpej struct intrq *iq;
168 1.1 thorpej struct intrhand *ih;
169 1.1 thorpej int irq, ipl;
170 1.1 thorpej
171 1.1 thorpej /* First, figure out which IPLs each IRQ has. */
172 1.1 thorpej for (irq = 0; irq < NIRQ; irq++) {
173 1.1 thorpej int levels = 0;
174 1.1 thorpej iq = &intrq[irq];
175 1.1 thorpej becc_disable_irq(irq);
176 1.1 thorpej for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
177 1.1 thorpej ih = TAILQ_NEXT(ih, ih_list))
178 1.1 thorpej levels |= (1U << ih->ih_ipl);
179 1.1 thorpej iq->iq_levels = levels;
180 1.1 thorpej }
181 1.1 thorpej
182 1.1 thorpej /* Next, figure out which IRQs are used by each IPL. */
183 1.1 thorpej for (ipl = 0; ipl < NIPL; ipl++) {
184 1.1 thorpej int irqs = 0;
185 1.1 thorpej for (irq = 0; irq < NIRQ; irq++) {
186 1.1 thorpej if (intrq[irq].iq_levels & (1U << ipl))
187 1.1 thorpej irqs |= (1U << irq);
188 1.1 thorpej }
189 1.1 thorpej becc_imask[ipl] = irqs;
190 1.1 thorpej }
191 1.1 thorpej
192 1.1 thorpej becc_imask[IPL_NONE] = 0;
193 1.1 thorpej
194 1.1 thorpej /*
195 1.8 ad * Enforce a hierarchy that gives "slow" device (or devices with
196 1.8 ad * limited input buffer space/"real-time" requirements) a better
197 1.8 ad * chance at not dropping data.
198 1.1 thorpej */
199 1.8 ad becc_imask[IPL_VM] |= becc_imask[IPL_SOFTSERIAL];
200 1.8 ad becc_imask[IPL_SCHED] |= becc_imask[IPL_VM];
201 1.8 ad becc_imask[IPL_HIGH] |= becc_imask[IPL_SCHED];
202 1.1 thorpej
203 1.1 thorpej /*
204 1.1 thorpej * Now compute which IRQs must be blocked when servicing any
205 1.1 thorpej * given IRQ.
206 1.1 thorpej */
207 1.1 thorpej for (irq = 0; irq < NIRQ; irq++) {
208 1.1 thorpej int irqs = (1U << irq);
209 1.1 thorpej iq = &intrq[irq];
210 1.1 thorpej if (TAILQ_FIRST(&iq->iq_list) != NULL)
211 1.1 thorpej becc_enable_irq(irq);
212 1.1 thorpej for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
213 1.1 thorpej ih = TAILQ_NEXT(ih, ih_list))
214 1.1 thorpej irqs |= becc_imask[ih->ih_ipl];
215 1.1 thorpej iq->iq_mask = irqs;
216 1.1 thorpej }
217 1.1 thorpej }
218 1.1 thorpej
219 1.1 thorpej void
220 1.1 thorpej splx(int new)
221 1.1 thorpej {
222 1.1 thorpej becc_splx(new);
223 1.1 thorpej }
224 1.1 thorpej
225 1.1 thorpej int
226 1.1 thorpej _spllower(int ipl)
227 1.1 thorpej {
228 1.1 thorpej return (becc_spllower(ipl));
229 1.1 thorpej }
230 1.1 thorpej
231 1.1 thorpej int
232 1.1 thorpej _splraise(int ipl)
233 1.1 thorpej {
234 1.1 thorpej return (becc_splraise(ipl));
235 1.1 thorpej }
236 1.1 thorpej
237 1.1 thorpej /*
238 1.1 thorpej * becc_icu_init:
239 1.1 thorpej *
240 1.1 thorpej * Initialize the BECC ICU. Called early in bootstrap
241 1.1 thorpej * to make sure the ICU is in a pristine state.
242 1.1 thorpej */
243 1.1 thorpej void
244 1.1 thorpej becc_icu_init(void)
245 1.1 thorpej {
246 1.1 thorpej
247 1.1 thorpej intr_enabled = 0; /* All interrupts disabled */
248 1.1 thorpej becc_set_intrmask();
249 1.1 thorpej
250 1.1 thorpej intr_steer = 0; /* All interrupts steered to IRQ */
251 1.1 thorpej becc_set_intrsteer();
252 1.1 thorpej
253 1.1 thorpej i80200_extirq_dispatch = becc_intr_dispatch;
254 1.1 thorpej
255 1.1 thorpej i80200_intr_enable(INTCTL_IM);
256 1.1 thorpej }
257 1.1 thorpej
258 1.1 thorpej /*
259 1.1 thorpej * becc_intr_init:
260 1.1 thorpej *
261 1.1 thorpej * Initialize the rest of the interrupt subsystem, making it
262 1.1 thorpej * ready to handle interrupts from devices.
263 1.1 thorpej */
264 1.1 thorpej void
265 1.1 thorpej becc_intr_init(void)
266 1.1 thorpej {
267 1.1 thorpej struct intrq *iq;
268 1.1 thorpej int i;
269 1.1 thorpej
270 1.1 thorpej intr_enabled = 0;
271 1.1 thorpej
272 1.1 thorpej for (i = 0; i < NIRQ; i++) {
273 1.1 thorpej iq = &intrq[i];
274 1.1 thorpej TAILQ_INIT(&iq->iq_list);
275 1.1 thorpej
276 1.1 thorpej evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
277 1.1 thorpej NULL, "becc", becc_irqnames[i]);
278 1.1 thorpej }
279 1.1 thorpej
280 1.1 thorpej becc_intr_calculate_masks();
281 1.1 thorpej
282 1.1 thorpej /* Enable IRQs (don't yet use FIQs). */
283 1.1 thorpej enable_interrupts(I32_bit);
284 1.1 thorpej }
285 1.1 thorpej
286 1.1 thorpej void *
287 1.1 thorpej becc_intr_establish(int irq, int ipl, int (*func)(void *), void *arg)
288 1.1 thorpej {
289 1.1 thorpej struct intrq *iq;
290 1.1 thorpej struct intrhand *ih;
291 1.1 thorpej uint32_t oldirqstate;
292 1.1 thorpej
293 1.1 thorpej if (irq < 0 || irq > NIRQ)
294 1.1 thorpej panic("becc_intr_establish: IRQ %d out of range", irq);
295 1.1 thorpej
296 1.1 thorpej ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
297 1.1 thorpej if (ih == NULL)
298 1.1 thorpej return (NULL);
299 1.1 thorpej
300 1.1 thorpej ih->ih_func = func;
301 1.1 thorpej ih->ih_arg = arg;
302 1.1 thorpej ih->ih_ipl = ipl;
303 1.1 thorpej ih->ih_irq = irq;
304 1.1 thorpej
305 1.1 thorpej iq = &intrq[irq];
306 1.1 thorpej
307 1.1 thorpej /* All BECC interrupts are level-triggered. */
308 1.1 thorpej iq->iq_ist = IST_LEVEL;
309 1.1 thorpej
310 1.1 thorpej oldirqstate = disable_interrupts(I32_bit);
311 1.1 thorpej
312 1.1 thorpej TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
313 1.1 thorpej
314 1.1 thorpej becc_intr_calculate_masks();
315 1.1 thorpej
316 1.1 thorpej restore_interrupts(oldirqstate);
317 1.1 thorpej
318 1.1 thorpej return (ih);
319 1.1 thorpej }
320 1.1 thorpej
321 1.1 thorpej void
322 1.1 thorpej becc_intr_disestablish(void *cookie)
323 1.1 thorpej {
324 1.1 thorpej struct intrhand *ih = cookie;
325 1.1 thorpej struct intrq *iq = &intrq[ih->ih_irq];
326 1.1 thorpej uint32_t oldirqstate;
327 1.1 thorpej
328 1.1 thorpej oldirqstate = disable_interrupts(I32_bit);
329 1.1 thorpej
330 1.1 thorpej TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
331 1.1 thorpej
332 1.1 thorpej becc_intr_calculate_masks();
333 1.1 thorpej
334 1.1 thorpej restore_interrupts(oldirqstate);
335 1.1 thorpej }
336 1.1 thorpej
337 1.1 thorpej void
338 1.13 skrll becc_intr_dispatch(struct trapframe *frame)
339 1.1 thorpej {
340 1.1 thorpej struct intrq *iq;
341 1.1 thorpej struct intrhand *ih;
342 1.11 matt uint32_t oldirqstate, irq, ibit, hwpend;
343 1.11 matt struct cpu_info * const ci = curcpu();
344 1.11 matt const int ppl = ci->ci_cpl;
345 1.11 matt const uint32_t imask = becc_imask[ppl];
346 1.1 thorpej
347 1.1 thorpej hwpend = becc_icsr_read();
348 1.1 thorpej
349 1.1 thorpej /*
350 1.1 thorpej * Disable all the interrupts that are pending. We will
351 1.1 thorpej * reenable them once they are processed and not masked.
352 1.1 thorpej */
353 1.1 thorpej intr_enabled &= ~hwpend;
354 1.1 thorpej becc_set_intrmask();
355 1.1 thorpej
356 1.1 thorpej while (hwpend != 0) {
357 1.1 thorpej irq = ffs(hwpend) - 1;
358 1.1 thorpej ibit = (1U << irq);
359 1.1 thorpej
360 1.1 thorpej hwpend &= ~ibit;
361 1.1 thorpej
362 1.11 matt if (imask & ibit) {
363 1.1 thorpej /*
364 1.1 thorpej * IRQ is masked; mark it as pending and check
365 1.1 thorpej * the next one. Note: the IRQ is already disabled.
366 1.1 thorpej */
367 1.1 thorpej becc_ipending |= ibit;
368 1.1 thorpej continue;
369 1.1 thorpej }
370 1.1 thorpej
371 1.1 thorpej becc_ipending &= ~ibit;
372 1.1 thorpej
373 1.1 thorpej iq = &intrq[irq];
374 1.1 thorpej iq->iq_ev.ev_count++;
375 1.12 matt ci->ci_data.cpu_nintr++;
376 1.11 matt TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
377 1.11 matt ci->ci_cpl = ih->ih_ipl;
378 1.11 matt oldirqstate = enable_interrupts(I32_bit);
379 1.1 thorpej (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
380 1.11 matt restore_interrupts(oldirqstate);
381 1.1 thorpej }
382 1.1 thorpej
383 1.11 matt ci->ci_cpl = ppl;
384 1.1 thorpej
385 1.1 thorpej /* Re-enable this interrupt now that's it's cleared. */
386 1.1 thorpej intr_enabled |= ibit;
387 1.1 thorpej becc_set_intrmask();
388 1.1 thorpej }
389 1.1 thorpej
390 1.11 matt if (becc_ipending & ~imask) {
391 1.11 matt intr_enabled |= (becc_ipending & ~imask);
392 1.1 thorpej becc_set_intrmask();
393 1.1 thorpej }
394 1.11 matt
395 1.11 matt #ifdef __HAVE_FAST_SOFTINTS
396 1.11 matt cpu_dosoftints();
397 1.11 matt #endif
398 1.1 thorpej }
399