sxiintc.c revision 1.4
1/*	$OpenBSD: sxiintc.c,v 1.4 2018/06/04 09:24:49 kettenis 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)
38int	sxiintcdebug = 10;
39char *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
118struct 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
128struct 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
135volatile int a1xsoftint_pending;
136
137struct intrq sxiintc_handler[NIRQ];
138u_int32_t sxiintc_smask[NIPL];
139u_int32_t sxiintc_imask[NBANKS][NIPL];
140struct interrupt_controller sxiintc_ic;
141
142bus_space_tag_t		sxiintc_iot;
143bus_space_handle_t	sxiintc_ioh;
144int			sxiintc_nirq;
145
146int	sxiintc_match(struct device *, void *, void *);
147void	sxiintc_attach(struct device *, struct device *, void *);
148int	sxiintc_spllower(int);
149int	sxiintc_splraise(int);
150void	sxiintc_setipl(int);
151void	sxiintc_calc_masks(void);
152void	*sxiintc_intr_establish_fdt(void *, int *, int, int (*)(void *),
153	    void *, char *);
154
155struct cfattach	sxiintc_ca = {
156	sizeof (struct device), sxiintc_match, sxiintc_attach
157};
158
159struct cfdriver sxiintc_cd = {
160	NULL, "sxiintc", DV_DULL
161};
162
163int sxiintc_attached = 0;
164
165int
166sxiintc_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
173void
174sxiintc_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
220void
221sxiintc_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
263void
264sxiintc_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
273int
274sxiintc_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
282int
283sxiintc_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
304void
305sxiintc_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
327void
328sxiintc_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
374void *
375sxiintc_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
414void *
415sxiintc_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
421void
422sxiintc_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
449const char *
450sxiintc_intr_string(void *cookie)
451{
452	return "asd?";
453}
454