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