diff options
Diffstat (limited to 'sys/arch/armv7/omap/intc.c')
| -rw-r--r-- | sys/arch/armv7/omap/intc.c | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/sys/arch/armv7/omap/intc.c b/sys/arch/armv7/omap/intc.c new file mode 100644 index 00000000000..2e515a54b16 --- /dev/null +++ b/sys/arch/armv7/omap/intc.c @@ -0,0 +1,423 @@ +/* $OpenBSD: intc.c,v 1.1 2013/09/04 14:38:30 patrick Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <machine/bus.h> +#include <armv7/omap/omapvar.h> +#include "intc.h" + +#define INTC_NUM_IRQ intc_nirq +#define INTC_NUM_BANKS (intc_nirq/32) +#define INTC_MAX_IRQ 128 +#define INTC_MAX_BANKS (INTC_MAX_IRQ/32) + +/* registers */ +#define INTC_REVISION 0x00 /* R */ +#define INTC_SYSCONFIG 0x10 /* RW */ +#define INTC_SYSCONFIG_AUTOIDLE 0x1 +#define INTC_SYSCONFIG_SOFTRESET 0x2 +#define INTC_SYSSTATUS 0x14 /* R */ +#define INTC_SYSSYSTATUS_RESETDONE 0x1 +#define INTC_SIR_IRQ 0x40 /* R */ +#define INTC_SIR_FIQ 0x44 /* R */ +#define INTC_CONTROL 0x48 /* RW */ +#define INTC_CONTROL_NEWIRQ 0x1 +#define INTC_CONTROL_NEWFIQ 0x2 +#define INTC_CONTROL_GLOBALMASK 0x1 +#define INTC_PROTECTION 0x4c /* RW */ +#define INTC_PROTECTION_PROT 1 /* only privileged mode */ +#define INTC_IDLE 0x50 /* RW */ + +#define INTC_IRQ_TO_REG(i) (((i) >> 5) & 0x3) +#define INTC_IRQ_TO_REGi(i) ((i) & 0x1f) +#define INTC_ITRn(i) 0x80+(0x20*i)+0x00 /* R */ +#define INTC_MIRn(i) 0x80+(0x20*i)+0x04 /* RW */ +#define INTC_CLEARn(i) 0x80+(0x20*i)+0x08 /* RW */ +#define INTC_SETn(i) 0x80+(0x20*i)+0x0c /* RW */ +#define INTC_ISR_SETn(i) 0x80+(0x20*i)+0x10 /* RW */ +#define INTC_ISR_CLEARn(i) 0x80+(0x20*i)+0x14 /* RW */ +#define INTC_PENDING_IRQn(i) 0x80+(0x20*i)+0x18 /* R */ +#define INTC_PENDING_FIQn(i) 0x80+(0x20*i)+0x1c /* R */ + +#define INTC_ILRn(i) 0x100+(4*i) +#define INTC_ILR_IRQ 0x0 /* not of FIQ */ +#define INTC_ILR_FIQ 0x1 +#define INTC_ILR_PRIs(pri) ((pri) << 2) +#define INTC_ILR_PRI(reg) (((reg) >> 2) & 0x2f) +#define INTC_MIN_PRI 63 +#define INTC_STD_PRI 32 +#define INTC_MAX_PRI 0 + +struct intrhand { + TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ + int (*ih_func)(void *); /* handler */ + void *ih_arg; /* arg for handler */ + int ih_ipl; /* IPL_* */ + int ih_irq; /* IRQ number */ + struct evcount ih_count; + char *ih_name; +}; + +struct intrq { + TAILQ_HEAD(, intrhand) iq_list; /* handler list */ + int iq_irq; /* IRQ to mask while handling */ + int iq_levels; /* IPL_*'s this IRQ has */ + int iq_ist; /* share type */ +}; + +volatile int softint_pending; + +struct intrq intc_handler[INTC_MAX_IRQ]; +u_int32_t intc_smask[NIPL]; +u_int32_t intc_imask[INTC_MAX_BANKS][NIPL]; + +bus_space_tag_t intc_iot; +bus_space_handle_t intc_ioh; +int intc_nirq; + +void intc_attach(struct device *, struct device *, void *); +int intc_spllower(int new); +int intc_splraise(int new); +void intc_setipl(int new); +void intc_calc_mask(void); + +struct cfattach intc_ca = { + sizeof (struct device), NULL, intc_attach +}; + +struct cfdriver intc_cd = { + NULL, "intc", DV_DULL +}; + +int intc_attached = 0; + +void +intc_attach(struct device *parent, struct device *self, void *args) +{ + struct omap_attach_args *oa = args; + int i; + u_int32_t rev; + + intc_iot = oa->oa_iot; + if (bus_space_map(intc_iot, oa->oa_dev->mem[0].addr, + oa->oa_dev->mem[0].size, 0, &intc_ioh)) + panic("intc_attach: bus_space_map failed!"); + + rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION); + + printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); + + /* software reset of the part? */ + /* set protection bit (kernel only)? */ +#if 0 + bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION, + INTC_PROTECTION_PROT); +#endif + + /* enable interface clock power saving mode */ + bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG, + INTC_SYSCONFIG_AUTOIDLE); + + switch (board_id) { + case BOARD_ID_AM335X_BEAGLEBONE: + intc_nirq = 128; + break; + default: + intc_nirq = 96; + break; + } + + /* mask all interrupts */ + for (i = 0; i < INTC_NUM_BANKS; i++) + bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff); + + for (i = 0; i < INTC_NUM_IRQ; i++) { + bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i), + INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ); + + TAILQ_INIT(&intc_handler[i].iq_list); + } + + intc_calc_mask(); + bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, + INTC_CONTROL_NEWIRQ); + + intc_attached = 1; + + /* insert self as interrupt handler */ + arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx, + intc_setipl, + intc_intr_establish, intc_intr_disestablish, intc_intr_string, + intc_irq_handler); + + intc_setipl(IPL_HIGH); /* XXX ??? */ + enable_interrupts(I32_bit); +} + +void +intc_calc_mask(void) +{ + struct cpu_info *ci = curcpu(); + int irq; + struct intrhand *ih; + int i; + + for (irq = 0; irq < INTC_NUM_IRQ; irq++) { + int max = IPL_NONE; + int min = IPL_HIGH; + TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { + if (ih->ih_ipl > max) + max = ih->ih_ipl; + + if (ih->ih_ipl < min) + min = ih->ih_ipl; + } + + intc_handler[irq].iq_irq = max; + + if (max == IPL_NONE) + min = IPL_NONE; + +#ifdef DEBUG_INTC + if (min != IPL_NONE) { + printf("irq %d to block at %d %d reg %d bit %d\n", + irq, max, min, INTC_IRQ_TO_REG(irq), + INTC_IRQ_TO_REGi(irq)); + } +#endif + /* Enable interrupts at lower levels, clear -> enable */ + for (i = 0; i < min; i++) + intc_imask[INTC_IRQ_TO_REG(irq)][i] &= + ~(1 << INTC_IRQ_TO_REGi(irq)); + for (; i <= IPL_HIGH; i++) + intc_imask[INTC_IRQ_TO_REG(irq)][i] |= + 1 << INTC_IRQ_TO_REGi(irq); + /* XXX - set enable/disable, priority */ + bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq), + INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ); + } + arm_init_smask(); + intc_setipl(ci->ci_cpl); +} + +void +intc_splx(int new) +{ + struct cpu_info *ci = curcpu(); + intc_setipl(new); + + if (ci->ci_ipending & arm_smask[ci->ci_cpl]) + arm_do_pending_intr(ci->ci_cpl); +} + +int +intc_spllower(int new) +{ + struct cpu_info *ci = curcpu(); + int old = ci->ci_cpl; + intc_splx(new); + return (old); +} + +int +intc_splraise(int new) +{ + struct cpu_info *ci = curcpu(); + int old; + old = ci->ci_cpl; + + /* + * setipl must always be called because there is a race window + * where the variable is updated before the mask is set + * an interrupt occurs in that window without the mask always + * being set, the hardware might not get updated on the next + * splraise completely messing up spl protection. + */ + if (old > new) + new = old; + + intc_setipl(new); + + return (old); +} + +void +intc_setipl(int new) +{ + struct cpu_info *ci = curcpu(); + int i; + int psw; + if (intc_attached == 0) + return; + + psw = disable_interrupts(I32_bit); +#if 0 + { + volatile static int recursed = 0; + if (recursed == 0) { + recursed = 1; + if (new != 12) + printf("setipl %d\n", new); + recursed = 0; + } + } +#endif + ci->ci_cpl = new; + for (i = 0; i < INTC_NUM_BANKS; i++) + bus_space_write_4(intc_iot, intc_ioh, + INTC_MIRn(i), intc_imask[i][new]); + bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, + INTC_CONTROL_NEWIRQ); + restore_interrupts(psw); +} + +void +intc_intr_bootstrap(vaddr_t addr) +{ + int i, j; + extern struct bus_space armv7_bs_tag; + intc_iot = &armv7_bs_tag; + intc_ioh = addr; + for (i = 0; i < INTC_NUM_BANKS; i++) + for (j = 0; j < NIPL; j++) + intc_imask[i][j] = 0xffffffff; +} + +void +intc_irq_handler(void *frame) +{ + int irq, pri, s; + struct intrhand *ih; + void *arg; + + irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ); +#ifdef DEBUG_INTC + printf("irq %d fired\n", irq); +#endif + + pri = intc_handler[irq].iq_irq; + s = intc_splraise(pri); + TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { + if (ih->ih_arg != 0) + arg = ih->ih_arg; + else + arg = frame; + + if (ih->ih_func(arg)) + ih->ih_count.ec_count++; + + } + bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, + INTC_CONTROL_NEWIRQ); + + intc_splx(s); +} + +void * +intc_intr_establish(int irqno, int level, int (*func)(void *), + void *arg, char *name) +{ + int psw; + struct intrhand *ih; + + if (irqno < 0 || irqno >= INTC_NUM_IRQ) + panic("intc_intr_establish: bogus irqnumber %d: %s", + irqno, name); + psw = disable_interrupts(I32_bit); + + /* no point in sleeping unless someone can free memory. */ + ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF, + cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("intr_establish: can't malloc handler info"); + ih->ih_func = func; + ih->ih_arg = arg; + ih->ih_ipl = level; + ih->ih_irq = irqno; + ih->ih_name = name; + + TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list); + + if (name != NULL) + evcount_attach(&ih->ih_count, name, &ih->ih_irq); + +#ifdef DEBUG_INTC + printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level, + name); +#endif + intc_calc_mask(); + + restore_interrupts(psw); + return (ih); +} + +void +intc_intr_disestablish(void *cookie) +{ + int psw; + struct intrhand *ih = cookie; + int irqno = ih->ih_irq; + psw = disable_interrupts(I32_bit); + TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list); + if (ih->ih_name != NULL) + evcount_detach(&ih->ih_count); + free(ih, M_DEVBUF); + restore_interrupts(psw); +} + +const char * +intc_intr_string(void *cookie) +{ + return "huh?"; +} + + +#if 0 +int intc_tst(void *a); + +int +intc_tst(void *a) +{ + printf("inct_tst called\n"); + bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); + return 1; +} + +void intc_test(void); +void intc_test(void) +{ + void * ih; + printf("about to register handler\n"); + ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst"); + + printf("about to set bit\n"); + bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2); + + printf("about to clear bit\n"); + bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); + + printf("about to remove handler\n"); + intc_intr_disestablish(ih); + + printf("done\n"); +} +#endif |
