xref: /netbsd-src/sys/arch/evbmips/ingenic/intr.c (revision ed6f73360e6d6723e5c3d5938fb9ba5292de65bb)
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