1 /* $OpenBSD: intc.c,v 1.4 2016/01/31 00:14:50 jsg Exp $ */
3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
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.
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.
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 #include <machine/bus.h>
25 #include <armv7/armv7/armv7var.h>
28 #define INTC_NUM_IRQ intc_nirq
29 #define INTC_NUM_BANKS (intc_nirq/32)
30 #define INTC_MAX_IRQ 128
31 #define INTC_MAX_BANKS (INTC_MAX_IRQ/32)
34 #define INTC_REVISION 0x00 /* R */
35 #define INTC_SYSCONFIG 0x10 /* RW */
36 #define INTC_SYSCONFIG_AUTOIDLE 0x1
37 #define INTC_SYSCONFIG_SOFTRESET 0x2
38 #define INTC_SYSSTATUS 0x14 /* R */
39 #define INTC_SYSSYSTATUS_RESETDONE 0x1
40 #define INTC_SIR_IRQ 0x40 /* R */
41 #define INTC_SIR_FIQ 0x44 /* R */
42 #define INTC_CONTROL 0x48 /* RW */
43 #define INTC_CONTROL_NEWIRQ 0x1
44 #define INTC_CONTROL_NEWFIQ 0x2
45 #define INTC_CONTROL_GLOBALMASK 0x1
46 #define INTC_PROTECTION 0x4c /* RW */
47 #define INTC_PROTECTION_PROT 1 /* only privileged mode */
48 #define INTC_IDLE 0x50 /* RW */
50 #define INTC_IRQ_TO_REG(i) (((i) >> 5) & 0x3)
51 #define INTC_IRQ_TO_REGi(i) ((i) & 0x1f)
52 #define INTC_ITRn(i) 0x80+(0x20*i)+0x00 /* R */
53 #define INTC_MIRn(i) 0x80+(0x20*i)+0x04 /* RW */
54 #define INTC_CLEARn(i) 0x80+(0x20*i)+0x08 /* RW */
55 #define INTC_SETn(i) 0x80+(0x20*i)+0x0c /* RW */
56 #define INTC_ISR_SETn(i) 0x80+(0x20*i)+0x10 /* RW */
57 #define INTC_ISR_CLEARn(i) 0x80+(0x20*i)+0x14 /* RW */
58 #define INTC_PENDING_IRQn(i) 0x80+(0x20*i)+0x18 /* R */
59 #define INTC_PENDING_FIQn(i) 0x80+(0x20*i)+0x1c /* R */
61 #define INTC_ILRn(i) 0x100+(4*i)
62 #define INTC_ILR_IRQ 0x0 /* not of FIQ */
63 #define INTC_ILR_FIQ 0x1
64 #define INTC_ILR_PRIs(pri) ((pri) << 2)
65 #define INTC_ILR_PRI(reg) (((reg) >> 2) & 0x2f)
66 #define INTC_MIN_PRI 63
67 #define INTC_STD_PRI 32
68 #define INTC_MAX_PRI 0
71 TAILQ_ENTRY(intrhand
) ih_list
; /* link on intrq list */
72 int (*ih_func
)(void *); /* handler */
73 void *ih_arg
; /* arg for handler */
74 int ih_ipl
; /* IPL_* */
75 int ih_irq
; /* IRQ number */
76 struct evcount ih_count
;
81 TAILQ_HEAD(, intrhand
) iq_list
; /* handler list */
82 int iq_irq
; /* IRQ to mask while handling */
83 int iq_levels
; /* IPL_*'s this IRQ has */
84 int iq_ist
; /* share type */
87 volatile int softint_pending
;
89 struct intrq intc_handler
[INTC_MAX_IRQ
];
90 u_int32_t intc_smask
[NIPL
];
91 u_int32_t intc_imask
[INTC_MAX_BANKS
][NIPL
];
93 bus_space_tag_t intc_iot
;
94 bus_space_handle_t intc_ioh
;
97 void intc_attach(struct device
*, struct device
*, void *);
98 int intc_spllower(int new);
99 int intc_splraise(int new);
100 void intc_setipl(int new);
101 void intc_calc_mask(void);
103 struct cfattach intc_ca
= {
104 sizeof (struct device
), NULL
, intc_attach
107 struct cfdriver intc_cd
= {
108 NULL
, "intc", DV_DULL
111 int intc_attached
= 0;
114 intc_attach(struct device
*parent
, struct device
*self
, void *args
)
116 struct armv7_attach_args
*aa
= args
;
120 intc_iot
= aa
->aa_iot
;
121 if (bus_space_map(intc_iot
, aa
->aa_dev
->mem
[0].addr
,
122 aa
->aa_dev
->mem
[0].size
, 0, &intc_ioh
))
123 panic("intc_attach: bus_space_map failed!");
125 rev
= bus_space_read_4(intc_iot
, intc_ioh
, INTC_REVISION
);
127 printf(" rev %d.%d\n", rev
>> 4 & 0xf, rev
& 0xf);
129 /* software reset of the part? */
130 /* set protection bit (kernel only)? */
132 bus_space_write_4(intc_iot
, intc_ioh
, INTC_PROTECTION
,
133 INTC_PROTECTION_PROT
);
136 /* enable interface clock power saving mode */
137 bus_space_write_4(intc_iot
, intc_ioh
, INTC_SYSCONFIG
,
138 INTC_SYSCONFIG_AUTOIDLE
);
141 case BOARD_ID_AM335X_BEAGLEBONE
:
149 /* mask all interrupts */
150 for (i
= 0; i
< INTC_NUM_BANKS
; i
++)
151 bus_space_write_4(intc_iot
, intc_ioh
, INTC_MIRn(i
), 0xffffffff);
153 for (i
= 0; i
< INTC_NUM_IRQ
; i
++) {
154 bus_space_write_4(intc_iot
, intc_ioh
, INTC_ILRn(i
),
155 INTC_ILR_PRIs(INTC_MIN_PRI
)|INTC_ILR_IRQ
);
157 TAILQ_INIT(&intc_handler
[i
].iq_list
);
161 bus_space_write_4(intc_iot
, intc_ioh
, INTC_CONTROL
,
162 INTC_CONTROL_NEWIRQ
);
166 /* insert self as interrupt handler */
167 arm_set_intr_handler(intc_splraise
, intc_spllower
, intc_splx
,
169 intc_intr_establish
, intc_intr_disestablish
, intc_intr_string
,
172 intc_setipl(IPL_HIGH
); /* XXX ??? */
173 enable_interrupts(PSR_I
);
179 struct cpu_info
*ci
= curcpu();
184 for (irq
= 0; irq
< INTC_NUM_IRQ
; irq
++) {
187 TAILQ_FOREACH(ih
, &intc_handler
[irq
].iq_list
, ih_list
) {
188 if (ih
->ih_ipl
> max
)
191 if (ih
->ih_ipl
< min
)
195 intc_handler
[irq
].iq_irq
= max
;
201 if (min
!= IPL_NONE
) {
202 printf("irq %d to block at %d %d reg %d bit %d\n",
203 irq
, max
, min
, INTC_IRQ_TO_REG(irq
),
204 INTC_IRQ_TO_REGi(irq
));
207 /* Enable interrupts at lower levels, clear -> enable */
208 for (i
= 0; i
< min
; i
++)
209 intc_imask
[INTC_IRQ_TO_REG(irq
)][i
] &=
210 ~(1 << INTC_IRQ_TO_REGi(irq
));
211 for (; i
<= IPL_HIGH
; i
++)
212 intc_imask
[INTC_IRQ_TO_REG(irq
)][i
] |=
213 1 << INTC_IRQ_TO_REGi(irq
);
214 /* XXX - set enable/disable, priority */
215 bus_space_write_4(intc_iot
, intc_ioh
, INTC_ILRn(irq
),
216 INTC_ILR_PRIs(NIPL
-max
)|INTC_ILR_IRQ
);
219 intc_setipl(ci
->ci_cpl
);
225 struct cpu_info
*ci
= curcpu();
228 if (ci
->ci_ipending
& arm_smask
[ci
->ci_cpl
])
229 arm_do_pending_intr(ci
->ci_cpl
);
233 intc_spllower(int new)
235 struct cpu_info
*ci
= curcpu();
236 int old
= ci
->ci_cpl
;
242 intc_splraise(int new)
244 struct cpu_info
*ci
= curcpu();
249 * setipl must always be called because there is a race window
250 * where the variable is updated before the mask is set
251 * an interrupt occurs in that window without the mask always
252 * being set, the hardware might not get updated on the next
253 * splraise completely messing up spl protection.
266 struct cpu_info
*ci
= curcpu();
269 if (intc_attached
== 0)
272 psw
= disable_interrupts(PSR_I
);
275 volatile static int recursed
= 0;
279 printf("setipl %d\n", new);
285 for (i
= 0; i
< INTC_NUM_BANKS
; i
++)
286 bus_space_write_4(intc_iot
, intc_ioh
,
287 INTC_MIRn(i
), intc_imask
[i
][new]);
288 bus_space_write_4(intc_iot
, intc_ioh
, INTC_CONTROL
,
289 INTC_CONTROL_NEWIRQ
);
290 restore_interrupts(psw
);
294 intc_intr_bootstrap(vaddr_t addr
)
297 extern struct bus_space armv7_bs_tag
;
298 intc_iot
= &armv7_bs_tag
;
300 for (i
= 0; i
< INTC_NUM_BANKS
; i
++)
301 for (j
= 0; j
< NIPL
; j
++)
302 intc_imask
[i
][j
] = 0xffffffff;
306 intc_irq_handler(void *frame
)
312 irq
= bus_space_read_4(intc_iot
, intc_ioh
, INTC_SIR_IRQ
);
314 printf("irq %d fired\n", irq
);
317 pri
= intc_handler
[irq
].iq_irq
;
318 s
= intc_splraise(pri
);
319 TAILQ_FOREACH(ih
, &intc_handler
[irq
].iq_list
, ih_list
) {
325 if (ih
->ih_func(arg
))
326 ih
->ih_count
.ec_count
++;
329 bus_space_write_4(intc_iot
, intc_ioh
, INTC_CONTROL
,
330 INTC_CONTROL_NEWIRQ
);
336 intc_intr_establish(int irqno
, int level
, int (*func
)(void *),
337 void *arg
, char *name
)
342 if (irqno
< 0 || irqno
>= INTC_NUM_IRQ
)
343 panic("intc_intr_establish: bogus irqnumber %d: %s",
345 psw
= disable_interrupts(PSR_I
);
347 /* no point in sleeping unless someone can free memory. */
348 ih
= (struct intrhand
*)malloc (sizeof *ih
, M_DEVBUF
,
349 cold
? M_NOWAIT
: M_WAITOK
);
351 panic("intr_establish: can't malloc handler info");
358 TAILQ_INSERT_TAIL(&intc_handler
[irqno
].iq_list
, ih
, ih_list
);
361 evcount_attach(&ih
->ih_count
, name
, &ih
->ih_irq
);
364 printf("intc_intr_establish irq %d level %d [%s]\n", irqno
, level
,
369 restore_interrupts(psw
);
374 intc_intr_disestablish(void *cookie
)
377 struct intrhand
*ih
= cookie
;
378 int irqno
= ih
->ih_irq
;
379 psw
= disable_interrupts(PSR_I
);
380 TAILQ_REMOVE(&intc_handler
[irqno
].iq_list
, ih
, ih_list
);
381 if (ih
->ih_name
!= NULL
)
382 evcount_detach(&ih
->ih_count
);
383 free(ih
, M_DEVBUF
, 0);
384 restore_interrupts(psw
);
388 intc_intr_string(void *cookie
)
395 int intc_tst(void *a
);
400 printf("inct_tst called\n");
401 bus_space_write_4(intc_iot
, intc_ioh
, INTC_ISR_CLEARn(0), 2);
405 void intc_test(void);
409 printf("about to register handler\n");
410 ih
= intc_intr_establish(1, IPL_BIO
, intc_tst
, NULL
, "intctst");
412 printf("about to set bit\n");
413 bus_space_write_4(intc_iot
, intc_ioh
, INTC_ISR_SETn(0), 2);
415 printf("about to clear bit\n");
416 bus_space_write_4(intc_iot
, intc_ioh
, INTC_ISR_CLEARn(0), 2);
418 printf("about to remove handler\n");
419 intc_intr_disestablish(ih
);