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