sxiintc.c revision 1.3
1/*	$OpenBSD: sxiintc.c,v 1.3 2017/01/21 08:26:49 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 <dev/fdt/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)
39int	sxiintcdebug = 10;
40char *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
119struct 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
129struct 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
136volatile int a1xsoftint_pending;
137
138struct intrq sxiintc_handler[NIRQ];
139u_int32_t sxiintc_smask[NIPL];
140u_int32_t sxiintc_imask[NBANKS][NIPL];
141struct interrupt_controller sxiintc_ic;
142
143bus_space_tag_t		sxiintc_iot;
144bus_space_handle_t	sxiintc_ioh;
145int			sxiintc_nirq;
146
147int	sxiintc_match(struct device *, void *, void *);
148void	sxiintc_attach(struct device *, struct device *, void *);
149int	sxiintc_spllower(int);
150int	sxiintc_splraise(int);
151void	sxiintc_setipl(int);
152void	sxiintc_calc_masks(void);
153void	*sxiintc_intr_establish_fdt(void *, int *, int, int (*)(void *),
154	    void *, char *);
155
156struct cfattach	sxiintc_ca = {
157	sizeof (struct device), sxiintc_match, sxiintc_attach
158};
159
160struct cfdriver sxiintc_cd = {
161	NULL, "sxiintc", DV_DULL
162};
163
164int sxiintc_attached = 0;
165
166int
167sxiintc_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
174void
175sxiintc_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
221void
222sxiintc_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
264void
265sxiintc_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
274int
275sxiintc_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
283int
284sxiintc_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
305void
306sxiintc_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
328void
329sxiintc_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
375void *
376sxiintc_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
415void *
416sxiintc_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
422void
423sxiintc_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
450const char *
451sxiintc_intr_string(void *cookie)
452{
453	return "asd?";
454}
455