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