1 /* $NetBSD: intr.c,v 1.13 2017/05/21 06:49:12 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2014 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.13 2017/05/21 06:49:12 skrll Exp $");
31
32 #define __INTR_PRIVATE
33
34 #include "opt_multiprocessor.h"
35
36 #include <sys/param.h>
37 #include <sys/cpu.h>
38 #include <sys/device.h>
39 #include <sys/kernel.h>
40 #include <sys/systm.h>
41 #include <sys/timetc.h>
42 #include <sys/bitops.h>
43
44 #include <mips/locore.h>
45 #include <machine/intr.h>
46
47 #include <mips/ingenic/ingenic_var.h>
48 #include <mips/ingenic/ingenic_regs.h>
49 #include <mips/ingenic/ingenic_coreregs.h>
50
51 #include "opt_ingenic.h"
52
53 #ifdef INGENIC_INTR_DEBUG
54 #define DPRINTF printf
55 #else
56 #define DPRINTF while (0) printf
57 #endif
58
59 extern void ingenic_clockintr(struct clockframe *);
60 extern void ingenic_puts(const char *);
61 /*
62 * This is a mask of bits to clear in the SR when we go to a
63 * given hardware interrupt priority level.
64 */
65 static const struct ipl_sr_map ingenic_ipl_sr_map = {
66 .sr_bits = {
67 [IPL_NONE] = 0,
68 [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
69 [IPL_SOFTNET] = MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
70 [IPL_VM] =
71 MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
72 MIPS_INT_MASK_0 |
73 MIPS_INT_MASK_3 |
74 MIPS_INT_MASK_4 |
75 MIPS_INT_MASK_5,
76 [IPL_SCHED] =
77 MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
78 MIPS_INT_MASK_0 |
79 MIPS_INT_MASK_1 |
80 MIPS_INT_MASK_2 |
81 MIPS_INT_MASK_3 |
82 MIPS_INT_MASK_4 |
83 MIPS_INT_MASK_5,
84 [IPL_DDB] = MIPS_INT_MASK,
85 [IPL_HIGH] = MIPS_INT_MASK,
86 },
87 };
88
89 #define NINTR 64
90
91 /* some timer channels share interrupts, couldn't find any others */
92 struct intrhand {
93 struct evcnt ih_count;
94 char ih_name[16];
95 int (*ih_func)(void *);
96 void *ih_arg;
97 int ih_ipl;
98 };
99
100 struct intrhand intrs[NINTR];
101 struct evcnt clockintrs;
102
103 void ingenic_irq(int);
104
105 void
evbmips_intr_init(void)106 evbmips_intr_init(void)
107 {
108 uint32_t reg;
109 int i;
110
111 ipl_sr_map = ingenic_ipl_sr_map;
112
113 evcnt_attach_dynamic(&clockintrs,
114 EVCNT_TYPE_INTR, NULL, "timer", "intr");
115
116 /* zero all handlers */
117 for (i = 0; i < NINTR; i++) {
118 intrs[i].ih_func = NULL;
119 intrs[i].ih_arg = NULL;
120 snprintf(intrs[i].ih_name, sizeof(intrs[i].ih_name),
121 "irq %d", i);
122 evcnt_attach_dynamic(&intrs[i].ih_count, EVCNT_TYPE_INTR,
123 NULL, "INTC", intrs[i].ih_name);
124 }
125
126 /* mask all peripheral IRQs */
127 writereg(JZ_ICMR0, 0xffffffff);
128 writereg(JZ_ICMR1, 0xffffffff);
129
130 /* allow peripheral interrupts to core 0 only */
131 reg = mips_cp0_corereim_read();
132 reg &= 0xffff0000;
133 reg |= REIM_IRQ0_M | REIM_MIRQ0_M;
134 #ifdef MULTIPROCESSOR
135 reg |= REIM_MIRQ1_M;
136 #endif
137 mips_cp0_corereim_write(reg);
138
139 mips_cp0_corembox_write(1, 0); /* ping the 2nd core */
140 DPRINTF("%s %08x\n", __func__, reg);
141 }
142
143 void
evbmips_iointr(int ipl,uint32_t ipending,struct clockframe * cf)144 evbmips_iointr(int ipl, uint32_t ipending, struct clockframe *cf)
145 {
146 uint32_t id;
147 #ifdef INGENIC_INTR_DEBUG
148 char buffer[256];
149
150 #if 0
151 snprintf(buffer, 256, "pending: %08x CR %08x\n", ipending,
152 mipsNN_cp0_cause_read());
153 ingenic_puts(buffer);
154 #endif
155 #endif
156 /* see which core we're on */
157 id = mipsNN_cp0_ebase_read() & 7;
158
159 /*
160 * XXX
161 * the manual counts the softint bits as INT0 and INT1, our headers
162 * don't so everything here looks off by two
163 */
164 if (ipending & MIPS_INT_MASK_1) {
165 /*
166 * this is a mailbox interrupt / IPI
167 */
168 uint32_t reg;
169 int s = splsched();
170
171 /* read pending IPIs */
172 reg = mips_cp0_corestatus_read();
173 if (id == 0) {
174 if (reg & CS_MIRQ0_P) {
175 #ifdef MULTIPROCESSOR
176 uint32_t tag;
177 tag = mips_cp0_corembox_read(id);
178
179 ipi_process(curcpu(), tag);
180 #ifdef INGENIC_INTR_DEBUG
181 snprintf(buffer, 256,
182 "IPI for core 0, msg %08x\n", tag);
183 ingenic_puts(buffer);
184 #endif
185 #endif
186 reg &= (~CS_MIRQ0_P);
187 /* clear it */
188 mips_cp0_corestatus_write(reg);
189 }
190 } else if (id == 1) {
191 if (reg & CS_MIRQ1_P) {
192 #ifdef MULTIPROCESSOR
193 uint32_t tag;
194 tag = mips_cp0_corembox_read(id);
195 ingenic_puts("1");
196 if (tag & 0x400)
197 hardclock(cf);
198 //ipi_process(curcpu(), tag);
199 #ifdef INGENIC_INTR_DEBUG
200 snprintf(buffer, 256,
201 "IPI for core 1, msg %08x\n", tag);
202 ingenic_puts(buffer);
203 #endif
204 #endif
205 reg &= (~CS_MIRQ1_P);
206 /* clear it */
207 mips_cp0_corestatus_write(reg);
208 }
209 }
210 splx(s);
211 }
212 if (ipending & MIPS_INT_MASK_2) {
213 /* this is a timer interrupt */
214 ingenic_clockintr(cf);
215 clockintrs.ev_count++;
216 ingenic_puts("INT2\n");
217 }
218 if (ipending & MIPS_INT_MASK_0) {
219 uint32_t mask;
220 /* peripheral interrupt */
221
222 /*
223 * XXX
224 * OS timer interrupts are supposed to show up as INT2 as well
225 * but I haven't seen them there so for now we just weed them
226 * out right here.
227 * The idea is to allow peripheral interrupts on both cores but
228 * block INT0 on core1 so it would see only timer interrupts
229 * and IPIs. If that doesn't work we'll have to send an IPI to
230 * core1 for each timer tick.
231 */
232 mask = readreg(JZ_ICPR0);
233 if (mask & 0x0c000000) {
234 writereg(JZ_ICMSR0, 0x0c000000);
235 ingenic_clockintr(cf);
236 writereg(JZ_ICMCR0, 0x0c000000);
237 clockintrs.ev_count++;
238 }
239 ingenic_irq(ipl);
240 KASSERT(id == 0);
241 }
242 }
243
244 void
ingenic_irq(int ipl)245 ingenic_irq(int ipl)
246 {
247 uint32_t irql, irqh, mask, ll, hh;
248 int bit, idx, bail;
249 #ifdef INGENIC_INTR_DEBUG
250 char buffer[16];
251 #endif
252
253 irql = readreg(JZ_ICPR0);
254 irqh = readreg(JZ_ICPR1);
255 #ifdef INGENIC_INTR_DEBUG
256 if (irql != 0) {
257 snprintf(buffer, 16, " il%08x", irql);
258 ingenic_puts(buffer);
259 }
260 #endif
261 bail = 32;
262 ll = irql;
263 hh = irqh;
264 writereg(JZ_ICMSR0, ll);
265 writereg(JZ_ICMSR1, hh);
266 bit = ffs32(irql);
267 while (bit != 0) {
268 idx = bit - 1;
269 mask = 1 << idx;
270 intrs[idx].ih_count.ev_count++;
271 if (intrs[idx].ih_func != NULL) {
272 if (intrs[idx].ih_ipl == IPL_VM)
273 KERNEL_LOCK(1, NULL);
274 intrs[idx].ih_func(intrs[idx].ih_arg);
275 if (intrs[idx].ih_ipl == IPL_VM)
276 KERNEL_UNLOCK_ONE(NULL);
277 } else {
278 /* spurious interrupt, mask it */
279 writereg(JZ_ICMSR0, mask);
280 }
281 irql &= ~mask;
282 bit = ffs32(irql);
283 bail--;
284 KASSERT(bail > 0);
285 }
286
287 #ifdef INGENIC_INTR_DEBUG
288 if (irqh != 0) {
289 snprintf(buffer, 16, " ih%08x", irqh);
290 ingenic_puts(buffer);
291 }
292 #endif
293 bit = ffs32(irqh);
294 while (bit != 0) {
295 idx = bit - 1;
296 mask = 1 << idx;
297 idx += 32;
298 intrs[idx].ih_count.ev_count++;
299 if (intrs[idx].ih_func != NULL) {
300 if (intrs[idx].ih_ipl == IPL_VM)
301 KERNEL_LOCK(1, NULL);
302 intrs[idx].ih_func(intrs[idx].ih_arg);
303 if (intrs[idx].ih_ipl == IPL_VM)
304 KERNEL_UNLOCK_ONE(NULL);
305 } else {
306 /* spurious interrupt, mask it */
307 writereg(JZ_ICMSR1, mask);
308 }
309 irqh &= ~mask;
310 bit = ffs32(irqh);
311 }
312 writereg(JZ_ICMCR0, ll);
313 writereg(JZ_ICMCR1, hh);
314 }
315
316 void *
evbmips_intr_establish(int irq,int (* func)(void *),void * arg)317 evbmips_intr_establish(int irq, int (*func)(void *), void *arg)
318 {
319 int s;
320
321 if ((irq < 0) || (irq >= NINTR)) {
322 aprint_error("%s: invalid irq %d\n", __func__, irq);
323 return NULL;
324 }
325
326 s = splhigh(); /* XXX probably needs a mutex */
327 intrs[irq].ih_func = func;
328 intrs[irq].ih_arg = arg;
329 intrs[irq].ih_ipl = IPL_VM;
330
331 /* now enable the IRQ */
332 if (irq >= 32) {
333 writereg(JZ_ICMCR1, 1 << (irq - 32));
334 } else
335 writereg(JZ_ICMCR0, 1 << irq);
336
337 splx(s);
338
339 return ((void *)(irq + 1));
340 }
341
342 void
evbmips_intr_disestablish(void * cookie)343 evbmips_intr_disestablish(void *cookie)
344 {
345 int irq = ((int)cookie) - 1;
346 int s;
347
348 if ((irq < 0) || (irq >= NINTR)) {
349 aprint_error("%s: invalid irq %d\n", __func__, irq);
350 return;
351 }
352
353 s = splhigh();
354
355 /* disable the IRQ */
356 if (irq >= 32) {
357 writereg(JZ_ICMSR1, 1 << (irq - 32));
358 } else
359 writereg(JZ_ICMSR0, 1 << irq);
360
361 intrs[irq].ih_func = NULL;
362 intrs[irq].ih_arg = NULL;
363 intrs[irq].ih_ipl = 0;
364
365 splx(s);
366 }
367