xref: /openbsd/sys/arch/armv7/omap/intc.c (revision da5607f6)
1 /* $OpenBSD: intc.c,v 1.15 2024/06/26 01:40:49 jsg Exp $ */
2 /*
3  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/queue.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/evcount.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <armv7/armv7/armv7var.h>
29 
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/fdt.h>
32 
33 #include "intc.h"
34 
35 #define INTC_NUM_IRQ intc_nirq
36 #define INTC_NUM_BANKS (intc_nirq/32)
37 #define INTC_MAX_IRQ 128
38 #define INTC_MAX_BANKS (INTC_MAX_IRQ/32)
39 
40 /* registers */
41 #define	INTC_REVISION		0x00	/* R */
42 #define	INTC_SYSCONFIG		0x10	/* RW */
43 #define		INTC_SYSCONFIG_AUTOIDLE		0x1
44 #define		INTC_SYSCONFIG_SOFTRESET	0x2
45 #define	INTC_SYSSTATUS		0x14	/* R */
46 #define		INTC_SYSSYSTATUS_RESETDONE	0x1
47 #define	INTC_SIR_IRQ		0x40	/* R */
48 #define	INTC_SIR_FIQ		0x44	/* R */
49 #define	INTC_CONTROL		0x48	/* RW */
50 #define		INTC_CONTROL_NEWIRQ	0x1
51 #define		INTC_CONTROL_NEWFIQ	0x2
52 #define		INTC_CONTROL_GLOBALMASK	0x1
53 #define	INTC_PROTECTION		0x4c	/* RW */
54 #define		INTC_PROTECTION_PROT 1	/* only privileged mode */
55 #define	INTC_IDLE		0x50	/* RW */
56 
57 #define INTC_IRQ_TO_REG(i)	(((i) >> 5) & 0x3)
58 #define INTC_IRQ_TO_REGi(i)	((i) & 0x1f)
59 #define	INTC_ITRn(i)		0x80+(0x20*i)+0x00	/* R */
60 #define	INTC_MIRn(i)		0x80+(0x20*i)+0x04	/* RW */
61 #define	INTC_CLEARn(i)		0x80+(0x20*i)+0x08	/* RW */
62 #define	INTC_SETn(i)		0x80+(0x20*i)+0x0c	/* RW */
63 #define	INTC_ISR_SETn(i)	0x80+(0x20*i)+0x10	/* RW */
64 #define	INTC_ISR_CLEARn(i)	0x80+(0x20*i)+0x14	/* RW */
65 #define INTC_PENDING_IRQn(i)	0x80+(0x20*i)+0x18	/* R */
66 #define INTC_PENDING_FIQn(i)	0x80+(0x20*i)+0x1c	/* R */
67 
68 #define INTC_ILRn(i)		0x100+(4*i)
69 #define		INTC_ILR_IRQ	0x0		/* not of FIQ */
70 #define		INTC_ILR_FIQ	0x1
71 #define		INTC_ILR_PRIs(pri)	((pri) << 2)
72 #define		INTC_ILR_PRI(reg)	(((reg) >> 2) & 0x2f)
73 #define		INTC_MIN_PRI	63
74 #define		INTC_STD_PRI	32
75 #define		INTC_MAX_PRI	0
76 
77 struct intrhand {
78 	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
79 	int (*ih_func)(void *);		/* handler */
80 	void *ih_arg;			/* arg for handler */
81 	int ih_ipl;			/* IPL_* */
82 	int ih_irq;			/* IRQ number */
83 	struct evcount	ih_count;
84 	char *ih_name;
85 };
86 
87 struct intrq {
88 	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
89 	int iq_irq;			/* IRQ to mask while handling */
90 	int iq_levels;			/* IPL_*'s this IRQ has */
91 	int iq_ist;			/* share type */
92 };
93 
94 struct intrq intc_handler[INTC_MAX_IRQ];
95 u_int32_t intc_smask[NIPL];
96 u_int32_t intc_imask[INTC_MAX_BANKS][NIPL];
97 struct interrupt_controller intc_ic;
98 
99 bus_space_tag_t		intc_iot;
100 bus_space_handle_t	intc_ioh;
101 int			intc_nirq;
102 
103 int	intc_match(struct device *, void *, void *);
104 void	intc_attach(struct device *, struct device *, void *);
105 int	intc_spllower(int new);
106 int	intc_splraise(int new);
107 void	intc_setipl(int new);
108 void	intc_calc_mask(void);
109 void	*intc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
110 	    int (*)(void *), void *, char *);
111 
112 const struct cfattach	intc_ca = {
113 	sizeof (struct device), intc_match, intc_attach
114 };
115 
116 struct cfdriver intc_cd = {
117 	NULL, "intc", DV_DULL
118 };
119 
120 int intc_attached = 0;
121 
122 int
intc_match(struct device * parent,void * match,void * aux)123 intc_match(struct device *parent, void *match, void *aux)
124 {
125 	struct fdt_attach_args *faa = aux;
126 
127 	return (OF_is_compatible(faa->fa_node, "ti,omap3-intc") ||
128 	    OF_is_compatible(faa->fa_node, "ti,am33xx-intc"));
129 }
130 
131 void
intc_attach(struct device * parent,struct device * self,void * aux)132 intc_attach(struct device *parent, struct device *self, void *aux)
133 {
134 	struct fdt_attach_args *faa = aux;
135 	int i;
136 	u_int32_t rev;
137 
138 	intc_iot = faa->fa_iot;
139 	if (bus_space_map(intc_iot, faa->fa_reg[0].addr,
140 	    faa->fa_reg[0].size, 0, &intc_ioh))
141 		panic("intc_attach: bus_space_map failed!");
142 
143 	rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION);
144 
145 	printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
146 
147 	/* software reset of the part? */
148 	/* set protection bit (kernel only)? */
149 #if 0
150 	bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION,
151 	     INTC_PROTECTION_PROT);
152 #endif
153 
154 	/* enable interface clock power saving mode */
155 	bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG,
156 	    INTC_SYSCONFIG_AUTOIDLE);
157 
158 	if (OF_is_compatible(faa->fa_node, "ti,am33xx-intc"))
159 		intc_nirq = 128;
160 	else
161 		intc_nirq = 96;
162 
163 	/* mask all interrupts */
164 	for (i = 0; i < INTC_NUM_BANKS; i++)
165 		bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff);
166 
167 	for (i = 0; i < INTC_NUM_IRQ; i++) {
168 		bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i),
169 		    INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ);
170 
171 		TAILQ_INIT(&intc_handler[i].iq_list);
172 	}
173 
174 	intc_calc_mask();
175 	bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
176 	    INTC_CONTROL_NEWIRQ);
177 
178 	intc_attached = 1;
179 
180 	/* insert self as interrupt handler */
181 	arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx,
182 	    intc_setipl,
183 	    intc_intr_establish, intc_intr_disestablish, intc_intr_string,
184 	    intc_irq_handler);
185 
186 	intc_setipl(IPL_HIGH);  /* XXX ??? */
187 	enable_interrupts(PSR_I);
188 
189 	intc_ic.ic_node = faa->fa_node;
190 	intc_ic.ic_establish = intc_intr_establish_fdt;
191 	arm_intr_register_fdt(&intc_ic);
192 }
193 
194 void
intc_calc_mask(void)195 intc_calc_mask(void)
196 {
197 	struct cpu_info *ci = curcpu();
198 	int irq;
199 	struct intrhand *ih;
200 	int i;
201 
202 	for (irq = 0; irq < INTC_NUM_IRQ; irq++) {
203 		int max = IPL_NONE;
204 		int min = IPL_HIGH;
205 		TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) {
206 			if (ih->ih_ipl > max)
207 				max = ih->ih_ipl;
208 
209 			if (ih->ih_ipl < min)
210 				min = ih->ih_ipl;
211 		}
212 
213 		intc_handler[irq].iq_irq = max;
214 
215 		if (max == IPL_NONE)
216 			min = IPL_NONE;
217 
218 #ifdef DEBUG_INTC
219 		if (min != IPL_NONE) {
220 			printf("irq %d to block at %d %d reg %d bit %d\n",
221 			    irq, max, min, INTC_IRQ_TO_REG(irq),
222 			    INTC_IRQ_TO_REGi(irq));
223 		}
224 #endif
225 		/* Enable interrupts at lower levels, clear -> enable */
226 		for (i = 0; i < min; i++)
227 			intc_imask[INTC_IRQ_TO_REG(irq)][i] &=
228 			    ~(1 << INTC_IRQ_TO_REGi(irq));
229 		for (; i <= IPL_HIGH; i++)
230 			intc_imask[INTC_IRQ_TO_REG(irq)][i] |=
231 			    1 << INTC_IRQ_TO_REGi(irq);
232 		/* XXX - set enable/disable, priority */
233 		bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq),
234 		    INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ);
235 	}
236 	arm_init_smask();
237 	intc_setipl(ci->ci_cpl);
238 }
239 
240 void
intc_splx(int new)241 intc_splx(int new)
242 {
243 	struct cpu_info *ci = curcpu();
244 	intc_setipl(new);
245 
246 	if (ci->ci_ipending & arm_smask[ci->ci_cpl])
247 		arm_do_pending_intr(ci->ci_cpl);
248 }
249 
250 int
intc_spllower(int new)251 intc_spllower(int new)
252 {
253 	struct cpu_info *ci = curcpu();
254 	int old = ci->ci_cpl;
255 	intc_splx(new);
256 	return (old);
257 }
258 
259 int
intc_splraise(int new)260 intc_splraise(int new)
261 {
262 	struct cpu_info *ci = curcpu();
263 	int old;
264 	old = ci->ci_cpl;
265 
266 	/*
267 	 * setipl must always be called because there is a race window
268 	 * where the variable is updated before the mask is set
269 	 * an interrupt occurs in that window without the mask always
270 	 * being set, the hardware might not get updated on the next
271 	 * splraise completely messing up spl protection.
272 	 */
273 	if (old > new)
274 		new = old;
275 
276 	intc_setipl(new);
277 
278 	return (old);
279 }
280 
281 void
intc_setipl(int new)282 intc_setipl(int new)
283 {
284 	struct cpu_info *ci = curcpu();
285 	int i;
286 	int psw;
287 	if (intc_attached == 0)
288 		return;
289 
290 	psw = disable_interrupts(PSR_I);
291 #if 0
292 	{
293 		volatile static int recursed = 0;
294 		if (recursed == 0) {
295 			recursed = 1;
296 			if (new != 12)
297 				printf("setipl %d\n", new);
298 			recursed = 0;
299 		}
300 	}
301 #endif
302 	ci->ci_cpl = new;
303 	for (i = 0; i < INTC_NUM_BANKS; i++)
304 		bus_space_write_4(intc_iot, intc_ioh,
305 		    INTC_MIRn(i), intc_imask[i][new]);
306 	bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
307 	    INTC_CONTROL_NEWIRQ);
308 	restore_interrupts(psw);
309 }
310 
311 void
intc_irq_handler(void * frame)312 intc_irq_handler(void *frame)
313 {
314 	int irq, pri, s;
315 	struct intrhand *ih;
316 	void *arg;
317 
318 	irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ);
319 #ifdef DEBUG_INTC
320 	printf("irq %d fired\n", irq);
321 #endif
322 
323 	pri = intc_handler[irq].iq_irq;
324 	s = intc_splraise(pri);
325 	TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) {
326 		if (ih->ih_arg)
327 			arg = ih->ih_arg;
328 		else
329 			arg = frame;
330 
331 		if (ih->ih_func(arg))
332 			ih->ih_count.ec_count++;
333 
334 	}
335 	bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL,
336 	    INTC_CONTROL_NEWIRQ);
337 
338 	intc_splx(s);
339 }
340 
341 void *
intc_intr_establish(int irqno,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)342 intc_intr_establish(int irqno, int level, struct cpu_info *ci,
343     int (*func)(void *), void *arg, char *name)
344 {
345 	int psw;
346 	struct intrhand *ih;
347 
348 	if (irqno < 0 || irqno >= INTC_NUM_IRQ)
349 		panic("intc_intr_establish: bogus irqnumber %d: %s",
350 		     irqno, name);
351 
352 	if (ci == NULL)
353 		ci = &cpu_info_primary;
354 	else if (!CPU_IS_PRIMARY(ci))
355 		return NULL;
356 
357 	psw = disable_interrupts(PSR_I);
358 
359 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
360 	ih->ih_func = func;
361 	ih->ih_arg = arg;
362 	ih->ih_ipl = level & IPL_IRQMASK;
363 	ih->ih_irq = irqno;
364 	ih->ih_name = name;
365 
366 	TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list);
367 
368 	if (name != NULL)
369 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
370 
371 #ifdef DEBUG_INTC
372 	printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level,
373 	    name);
374 #endif
375 	intc_calc_mask();
376 
377 	restore_interrupts(psw);
378 	return (ih);
379 }
380 
381 void *
intc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)382 intc_intr_establish_fdt(void *cookie, int *cell, int level,
383     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
384 {
385 	return intc_intr_establish(cell[0], level, ci, func, arg, name);
386 }
387 
388 void
intc_intr_disestablish(void * cookie)389 intc_intr_disestablish(void *cookie)
390 {
391 	int psw;
392 	struct intrhand *ih = cookie;
393 	int irqno = ih->ih_irq;
394 	psw = disable_interrupts(PSR_I);
395 	TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list);
396 	if (ih->ih_name != NULL)
397 		evcount_detach(&ih->ih_count);
398 	free(ih, M_DEVBUF, 0);
399 	restore_interrupts(psw);
400 }
401 
402 const char *
intc_intr_string(void * cookie)403 intc_intr_string(void *cookie)
404 {
405 	return "huh?";
406 }
407 
408 
409 #if 0
410 int intc_tst(void *a);
411 
412 int
413 intc_tst(void *a)
414 {
415 	printf("inct_tst called\n");
416 	bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2);
417 	return 1;
418 }
419 
420 void intc_test(void);
421 void
422 intc_test(void)
423 {
424 	void * ih;
425 	printf("about to register handler\n");
426 	ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst");
427 
428 	printf("about to set bit\n");
429 	bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2);
430 
431 	printf("about to clear bit\n");
432 	bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2);
433 
434 	printf("about to remove handler\n");
435 	intc_intr_disestablish(ih);
436 
437 	printf("done\n");
438 }
439 #endif
440