xref: /openbsd/sys/arch/armv7/sunxi/sxiintc.c (revision 4bdff4be)
1 /*	$OpenBSD: sxiintc.c,v 1.11 2022/01/03 03:06:50 jsg Exp $	*/
2 /*
3  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4  * Copyright (c) 2013 Artturi Alm
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/queue.h>
22 #include <sys/malloc.h>
23 #include <sys/device.h>
24 #include <sys/evcount.h>
25 
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 
29 #include <armv7/sunxi/sxiintc.h>
30 
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/fdt.h>
33 
34 #ifdef DEBUG_INTC
35 #define DPRINTF(x)	do { if (sxiintcdebug) printf x; } while (0)
36 #define DPRINTFN(n,x)	do { if (sxiintcdebug>(n)) printf x; } while (0)
37 int	sxiintcdebug = 10;
38 char *ipl_strtbl[NIPL] = {
39 	"IPL_NONE",
40 	"IPL_SOFT",
41 	"IPL_SOFTCLOCK",
42 	"IPL_SOFTNET",
43 	"IPL_SOFTTTY",
44 	"IPL_BIO|IPL_USB",
45 	"IPL_NET",
46 	"IPL_TTY",
47 	"IPL_VM",
48 	"IPL_AUDIO",
49 	"IPL_CLOCK",
50 	"IPL_STATCLOCK",
51 	"IPL_SCHED|IPL_HIGH"
52 };
53 #else
54 #define DPRINTF(x)
55 #define DPRINTFN(n,x)
56 #endif
57 
58 #define NIRQ			96
59 #define NBANKS			3
60 #define NIRQPRIOREGS		5
61 
62 /* registers */
63 #define INTC_VECTOR_REG		0x00
64 #define INTC_BASE_ADR_REG	0x04
65 #define INTC_PROTECTION_REG	0x08
66 #define INTC_NMI_CTRL_REG	0x0c
67 
68 #define INTC_IRQ_PENDING_REG0	0x10
69 #define INTC_IRQ_PENDING_REG1	0x14
70 #define INTC_IRQ_PENDING_REG2	0x18
71 
72 #define INTC_SELECT_REG0	0x30
73 #define INTC_SELECT_REG1	0x34
74 #define INTC_SELECT_REG2	0x38
75 
76 #define INTC_ENABLE_REG0	0x40
77 #define INTC_ENABLE_REG1	0x44
78 #define INTC_ENABLE_REG2	0x48
79 
80 #define INTC_MASK_REG0		0x50
81 #define INTC_MASK_REG1		0x54
82 #define INTC_MASK_REG2		0x58
83 
84 #define INTC_RESP_REG0		0x60
85 #define INTC_RESP_REG1		0x64
86 #define INTC_RESP_REG2		0x68
87 
88 #define INTC_PRIO_REG0		0x80
89 #define INTC_PRIO_REG1		0x84
90 #define INTC_PRIO_REG2		0x88
91 #define INTC_PRIO_REG3		0x8c
92 #define INTC_PRIO_REG4		0x90
93 
94 #define INTC_IRQ_PENDING_REG(_b)	(0x10 + ((_b) * 4))
95 #define INTC_FIQ_PENDING_REG(_b)	(0x20 + ((_b) * 4))
96 #define INTC_SELECT_REG(_b)		(0x30 + ((_b) * 4))
97 #define INTC_ENABLE_REG(_b)		(0x40 + ((_b) * 4))
98 #define INTC_MASK_REG(_b)		(0x50 + ((_b) * 4))
99 #define INTC_RESP_REG(_b)		(0x60 + ((_b) * 4))
100 #define INTC_PRIO_REG(_b)		(0x80 + ((_b) * 4))
101 
102 #define IRQ2REG32(i)		(((i) >> 5) & 0x3)
103 #define IRQ2BIT32(i)		((i) & 0x1f)
104 
105 #define IRQ2REG16(i)		(((i) >> 4) & 0x5)
106 #define IRQ2BIT16(i)		(((i) & 0x0f) * 2)
107 
108 #define INTC_IRQ_HIPRIO		0x3
109 #define INTC_IRQ_ENABLED	0x2
110 #define INTC_IRQ_DISABLED	0x1
111 #define INTC_IRQ_LOWPRIO	0x0
112 #define INTC_PRIOCLEAR(i)	(~(INTC_IRQ_HIPRIO << IRQ2BIT16((i))))
113 #define INTC_PRIOENABLE(i)	(INTC_IRQ_ENABLED << IRQ2BIT16((i)))
114 #define INTC_PRIOHI(i)		(INTC_IRQ_HIPRIO << IRQ2BIT16((i)))
115 
116 
117 struct intrhand {
118 	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
119 	int (*ih_func)(void *);		/* handler */
120 	void *ih_arg;			/* arg for handler */
121 	int ih_ipl;			/* IPL_* */
122 	int ih_irq;			/* IRQ number */
123 	struct evcount	ih_count;
124 	char *ih_name;
125 };
126 
127 struct intrq {
128 	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
129 	int iq_irq;			/* IRQ to mask while handling */
130 	int iq_levels;			/* IPL_*'s this IRQ has */
131 	int iq_ist;			/* share type */
132 };
133 
134 volatile int a1xsoftint_pending;
135 
136 struct intrq sxiintc_handler[NIRQ];
137 u_int32_t sxiintc_smask[NIPL];
138 u_int32_t sxiintc_imask[NBANKS][NIPL];
139 struct interrupt_controller sxiintc_ic;
140 
141 bus_space_tag_t		sxiintc_iot;
142 bus_space_handle_t	sxiintc_ioh;
143 int			sxiintc_nirq;
144 
145 int	sxiintc_match(struct device *, void *, void *);
146 void	sxiintc_attach(struct device *, struct device *, void *);
147 int	sxiintc_spllower(int);
148 int	sxiintc_splraise(int);
149 void	sxiintc_setipl(int);
150 void	sxiintc_calc_masks(void);
151 void	*sxiintc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
152 	    int (*)(void *), void *, char *);
153 
154 const struct cfattach	sxiintc_ca = {
155 	sizeof (struct device), sxiintc_match, sxiintc_attach
156 };
157 
158 struct cfdriver sxiintc_cd = {
159 	NULL, "sxiintc", DV_DULL
160 };
161 
162 int sxiintc_attached = 0;
163 
164 int
165 sxiintc_match(struct device *parent, void *match, void *aux)
166 {
167 	struct fdt_attach_args *faa = aux;
168 
169 	return OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-ic");
170 }
171 
172 void
173 sxiintc_attach(struct device *parent, struct device *self, void *aux)
174 {
175 	struct fdt_attach_args *faa = aux;
176 	int i, j;
177 
178 	sxiintc_iot = faa->fa_iot;
179 	if (bus_space_map(sxiintc_iot, faa->fa_reg[0].addr,
180 	    faa->fa_reg[0].size, 0, &sxiintc_ioh))
181 		panic("sxiintc_attach: bus_space_map failed!");
182 
183 	/* disable/mask/clear all interrupts */
184 	for (i = 0; i < NBANKS; i++) {
185 		bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_ENABLE_REG(i), 0);
186 		bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_MASK_REG(i), 0);
187 		bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_IRQ_PENDING_REG(i),
188 		    0xffffffff);
189 		for (j = 0; j < NIPL; j++)
190 			sxiintc_imask[i][j] = 0;
191 	}
192 
193 	/* XXX */
194 	bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_PROTECTION_REG, 1);
195 	bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_NMI_CTRL_REG, 0);
196 
197 	for (i = 0; i < NIRQ; i++)
198 		TAILQ_INIT(&sxiintc_handler[i].iq_list);
199 
200 	sxiintc_calc_masks();
201 
202 	arm_init_smask();
203 	sxiintc_attached = 1;
204 
205 	/* insert self as interrupt handler */
206 	arm_set_intr_handler(sxiintc_splraise, sxiintc_spllower, sxiintc_splx,
207 	    sxiintc_setipl,
208 	    sxiintc_intr_establish, sxiintc_intr_disestablish, sxiintc_intr_string,
209 	    sxiintc_irq_handler);
210 	sxiintc_setipl(IPL_HIGH);  /* XXX ??? */
211 	enable_interrupts(PSR_I);
212 	printf("\n");
213 
214 	sxiintc_ic.ic_node = faa->fa_node;
215 	sxiintc_ic.ic_establish = sxiintc_intr_establish_fdt;
216 	arm_intr_register_fdt(&sxiintc_ic);
217 }
218 
219 void
220 sxiintc_calc_masks(void)
221 {
222 	struct cpu_info *ci = curcpu();
223 	int irq;
224 	struct intrhand *ih;
225 	int i;
226 
227 	for (irq = 0; irq < NIRQ; irq++) {
228 		int max = IPL_NONE;
229 		int min = IPL_HIGH;
230 		TAILQ_FOREACH(ih, &sxiintc_handler[irq].iq_list, ih_list) {
231 			if (ih->ih_ipl > max)
232 				max = ih->ih_ipl;
233 			if (ih->ih_ipl < min)
234 				min = ih->ih_ipl;
235 		}
236 
237 		sxiintc_handler[irq].iq_irq = max;
238 
239 		if (max == IPL_NONE)
240 			min = IPL_NONE;
241 
242 #ifdef DEBUG_INTC
243 		if (min != IPL_NONE) {
244 			printf("irq %d to block at %d %d reg %d bit %d\n",
245 			    irq, max, min, IRQ2REG32(irq),
246 			    IRQ2BIT32(irq));
247 		}
248 #endif
249 		/* Enable interrupts at lower levels, clear -> enable */
250 		for (i = 0; i < min; i++)
251 			sxiintc_imask[IRQ2REG32(irq)][i] &=
252 			    ~(1 << IRQ2BIT32(irq));
253 		for (; i < NIPL; i++)
254 			sxiintc_imask[IRQ2REG32(irq)][i] |=
255 			    (1 << IRQ2BIT32(irq));
256 		/* XXX - set enable/disable, priority */
257 	}
258 
259 	sxiintc_setipl(ci->ci_cpl);
260 }
261 
262 void
263 sxiintc_splx(int new)
264 {
265 	struct cpu_info *ci = curcpu();
266 	sxiintc_setipl(new);
267 
268 	if (ci->ci_ipending & arm_smask[ci->ci_cpl])
269 		arm_do_pending_intr(ci->ci_cpl);
270 }
271 
272 int
273 sxiintc_spllower(int new)
274 {
275 	struct cpu_info *ci = curcpu();
276 	int old = ci->ci_cpl;
277 	sxiintc_splx(new);
278 	return (old);
279 }
280 
281 int
282 sxiintc_splraise(int new)
283 {
284 	struct cpu_info *ci = curcpu();
285 	int old;
286 	old = ci->ci_cpl;
287 
288 	/*
289 	 * setipl must always be called because there is a race window
290 	 * where the variable is updated before the mask is set
291 	 * an interrupt occurs in that window without the mask always
292 	 * being set, the hardware might not get updated on the next
293 	 * splraise completely messing up spl protection.
294 	 */
295 	if (old > new)
296 		new = old;
297 
298 	sxiintc_setipl(new);
299 
300 	return (old);
301 }
302 
303 void
304 sxiintc_setipl(int new)
305 {
306 	struct cpu_info *ci = curcpu();
307 	int i, psw;
308 #if 1
309 	/*
310 	 * XXX not needed, because all interrupts are disabled
311 	 * by default, so touching maskregs has no effect, i hope.
312 	 */
313 	if (sxiintc_attached == 0) {
314 		ci->ci_cpl = new;
315 		return;
316 	}
317 #endif
318 	psw = disable_interrupts(PSR_I);
319 	ci->ci_cpl = new;
320 	for (i = 0; i < NBANKS; i++)
321 		bus_space_write_4(sxiintc_iot, sxiintc_ioh,
322 		    INTC_MASK_REG(i), sxiintc_imask[i][new]);
323 	restore_interrupts(psw);
324 }
325 
326 void
327 sxiintc_irq_handler(void *frame)
328 {
329 	struct intrhand *ih;
330 	void *arg;
331 	uint32_t pr;
332 	int irq, prio, s;
333 
334 	irq = bus_space_read_4(sxiintc_iot, sxiintc_ioh, INTC_VECTOR_REG) >> 2;
335 	if (irq == 0)
336 		return;
337 
338 	prio = sxiintc_handler[irq].iq_irq;
339 	s = sxiintc_splraise(prio);
340 	splassert(prio);
341 
342 	pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh,
343 	    INTC_ENABLE_REG(IRQ2REG32(irq)));
344 	bus_space_write_4(sxiintc_iot, sxiintc_ioh,
345 	    INTC_ENABLE_REG(IRQ2REG32(irq)),
346 	    pr & ~(1 << IRQ2BIT32(irq)));
347 
348 	/* clear pending */
349 	pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh,
350 	    INTC_IRQ_PENDING_REG(IRQ2REG32(irq)));
351 	bus_space_write_4(sxiintc_iot, sxiintc_ioh,
352 	    INTC_IRQ_PENDING_REG(IRQ2REG32(irq)),
353 	    pr | (1 << IRQ2BIT32(irq)));
354 
355 	pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh,
356 	    INTC_ENABLE_REG(IRQ2REG32(irq)));
357 	bus_space_write_4(sxiintc_iot, sxiintc_ioh,
358 	    INTC_ENABLE_REG(IRQ2REG32(irq)),
359 	    pr | (1 << IRQ2BIT32(irq)));
360 
361 	TAILQ_FOREACH(ih, &sxiintc_handler[irq].iq_list, ih_list) {
362 		if (ih->ih_arg)
363 			arg = ih->ih_arg;
364 		else
365 			arg = frame;
366 
367 		if (ih->ih_func(arg))
368 			ih->ih_count.ec_count++;
369 	}
370 	sxiintc_splx(s);
371 }
372 
373 void *
374 sxiintc_intr_establish(int irq, int level, struct cpu_info *ci,
375     int (*func)(void *), void *arg, char *name)
376 {
377 	int psw;
378 	struct intrhand *ih;
379 	uint32_t er;
380 
381 	if (irq <= 0 || irq >= NIRQ)
382 		panic("intr_establish: bogus irq %d %s", irq, name);
383 
384 	if (ci == NULL)
385 		ci = &cpu_info_primary;
386 	else if (!CPU_IS_PRIMARY(ci))
387 		return NULL;
388 
389 	DPRINTF(("intr_establish: irq %d level %d [%s]\n", irq, level,
390 	    name != NULL ? name : "NULL"));
391 
392 	psw = disable_interrupts(PSR_I);
393 
394 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
395 	ih->ih_func = func;
396 	ih->ih_arg = arg;
397 	ih->ih_ipl = level & IPL_IRQMASK;
398 	ih->ih_irq = irq;
399 	ih->ih_name = name;
400 
401 	TAILQ_INSERT_TAIL(&sxiintc_handler[irq].iq_list, ih, ih_list);
402 
403 	if (name != NULL)
404 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
405 
406 	er = bus_space_read_4(sxiintc_iot, sxiintc_ioh,
407 	    INTC_ENABLE_REG(IRQ2REG32(irq)));
408 	bus_space_write_4(sxiintc_iot, sxiintc_ioh,
409 	    INTC_ENABLE_REG(IRQ2REG32(irq)),
410 	    er | (1 << IRQ2BIT32(irq)));
411 
412 	sxiintc_calc_masks();
413 
414 	restore_interrupts(psw);
415 	return (ih);
416 }
417 
418 void *
419 sxiintc_intr_establish_fdt(void *cookie, int *cell, int level,
420     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
421 {
422 	return sxiintc_intr_establish(cell[0], level, ci, func, arg, name);
423 }
424 
425 void
426 sxiintc_intr_disestablish(void *cookie)
427 {
428 	struct intrhand *ih = cookie;
429 	int irq = ih->ih_irq;
430 	int psw;
431 	uint32_t er;
432 
433 	psw = disable_interrupts(PSR_I);
434 
435 	TAILQ_REMOVE(&sxiintc_handler[irq].iq_list, ih, ih_list);
436 
437 	if (ih->ih_name != NULL)
438 		evcount_detach(&ih->ih_count);
439 
440 	free(ih, M_DEVBUF, 0);
441 
442 	er = bus_space_read_4(sxiintc_iot, sxiintc_ioh,
443 	    INTC_ENABLE_REG(IRQ2REG32(irq)));
444 	bus_space_write_4(sxiintc_iot, sxiintc_ioh,
445 	    INTC_ENABLE_REG(IRQ2REG32(irq)),
446 	    er & ~(1 << IRQ2BIT32(irq)));
447 
448 	sxiintc_calc_masks();
449 
450 	restore_interrupts(psw);
451 }
452 
453 const char *
454 sxiintc_intr_string(void *cookie)
455 {
456 	return "asd?";
457 }
458