1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

Allow external interrupts.

- Set the external pin to interrupt in bus_setup_intr
- Implement bus_config_intr for external interrupts
- Extend arm_{,un}mask_irq to work with external interrupts

Approved by:	imp (mentor)
This commit is contained in:
Andrew Turner 2010-07-24 23:41:09 +00:00
parent 34f56898a1
commit fa94061701
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=210458
2 changed files with 163 additions and 7 deletions

View File

@ -118,6 +118,8 @@ static int s3c24x0_setup_intr(device_t, device_t, struct resource *, int,
driver_filter_t *, driver_intr_t *, void *, void **);
static int s3c24x0_teardown_intr(device_t, device_t, struct resource *,
void *);
static int s3c24x0_config_intr(device_t, int, enum intr_trigger,
enum intr_polarity);
static struct resource *s3c24x0_alloc_resource(device_t, device_t, int, int *,
u_long, u_long, u_long, u_int);
static int s3c24x0_activate_resource(device_t, device_t, int, int,
@ -134,6 +136,7 @@ static device_method_t s3c24x0_methods[] = {
DEVMETHOD(device_identify, s3c24x0_identify),
DEVMETHOD(bus_setup_intr, s3c24x0_setup_intr),
DEVMETHOD(bus_teardown_intr, s3c24x0_teardown_intr),
DEVMETHOD(bus_config_intr, s3c24x0_config_intr),
DEVMETHOD(bus_alloc_resource, s3c24x0_alloc_resource),
DEVMETHOD(bus_activate_resource, s3c24x0_activate_resource),
DEVMETHOD(bus_release_resource, s3c24x0_release_resource),
@ -176,6 +179,30 @@ s3c24x0_add_child(device_t bus, int prio, const char *name, int unit)
return (child);
}
static void
s3c24x0_enable_ext_intr(unsigned int irq)
{
uint32_t reg, value;
int offset;
if (irq <= 7) {
reg = GPIO_PFCON;
offset = irq * 2;
} else if (irq <= 23) {
reg = GPIO_PGCON;
offset = (irq - 8) * 2;
} else
return;
/* Make the pin an interrupt source */
value = bus_space_read_4(s3c2xx0_softc->sc_iot,
s3c2xx0_softc->sc_gpio_ioh, reg);
value &= ~(3 << offset);
value |= 2 << offset;
bus_space_write_4(s3c2xx0_softc->sc_iot, s3c2xx0_softc->sc_gpio_ioh,
reg, value);
}
static int
s3c24x0_setup_intr(device_t dev, device_t child,
struct resource *ires, int flags, driver_filter_t *filt,
@ -189,6 +216,10 @@ s3c24x0_setup_intr(device_t dev, device_t child,
return (error);
for (irq = rman_get_start(ires); irq <= rman_get_end(ires); irq++) {
if (irq >= S3C24X0_EXTIRQ_MIN && irq <= S3C24X0_EXTIRQ_MAX) {
/* Enable the external interrupt pin */
s3c24x0_enable_ext_intr(irq - S3C24X0_EXTIRQ_MIN);
}
arm_unmask_irq(irq);
}
return (0);
@ -201,6 +232,59 @@ s3c24x0_teardown_intr(device_t dev, device_t child, struct resource *res,
return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie));
}
static int
s3c24x0_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol)
{
uint32_t mask, reg, value;
int offset;
/* Only external interrupts can be configured */
if (irq < S3C24X0_EXTIRQ_MIN || irq > S3C24X0_EXTIRQ_MAX)
return (EINVAL);
/* There is no standard trigger or polarity for the bus */
if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
return (EINVAL);
irq -= S3C24X0_EXTIRQ_MIN;
/* Get the bits to set */
mask = 0;
if (pol == INTR_POLARITY_LOW) {
mask = 2;
} else if (pol == INTR_POLARITY_HIGH) {
mask = 4;
}
if (trig == INTR_TRIGGER_LEVEL) {
mask >>= 2;
}
/* Get the register to set */
if (irq <= 7) {
reg = GPIO_EXTINT(0);
offset = irq * 4;
} else if (irq <= 15) {
reg = GPIO_EXTINT(1);
offset = (irq - 8) * 4;
} else if (irq <= 23) {
reg = GPIO_EXTINT(2);
offset = (irq - 16) * 4;
} else {
return (EINVAL);
}
/* Set the new signaling method */
value = bus_space_read_4(s3c2xx0_softc->sc_iot,
s3c2xx0_softc->sc_gpio_ioh, reg);
value &= ~(7 << offset);
value |= mask << offset;
bus_space_write_4(s3c2xx0_softc->sc_iot,
s3c2xx0_softc->sc_gpio_ioh, reg, value);
return (0);
}
static struct resource *
s3c24x0_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
@ -356,6 +440,7 @@ s3c24x0_attach(device_t dev)
bus_space_tag_t iot;
device_t child;
unsigned int i, j;
u_long irqmax;
s3c2xx0_softc = &(sc->sc_sx);
sc->sc_sx.sc_iot = iot = &s3c2xx0_bs_tag;
@ -363,10 +448,6 @@ s3c24x0_attach(device_t dev)
s3c2xx0_softc->s3c2xx0_irq_rman.rm_descr = "S3C24X0 IRQs";
s3c2xx0_softc->s3c2xx0_mem_rman.rm_type = RMAN_ARRAY;
s3c2xx0_softc->s3c2xx0_mem_rman.rm_descr = "S3C24X0 Device Registers";
if (rman_init(&s3c2xx0_softc->s3c2xx0_irq_rman) != 0 ||
rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman, 0,
S3C2410_SUBIRQ_MAX) != 0) /* XXX Change S3C2440_SUBIRQ_MAX depending on micro */
panic("s3c24x0_attach: failed to set up IRQ rman");
/* Manage the registor memory space */
if ((rman_init(&s3c2xx0_softc->s3c2xx0_mem_rman) != 0) ||
(rman_manage_region(&s3c2xx0_softc->s3c2xx0_mem_rman,
@ -388,6 +469,22 @@ s3c24x0_attach(device_t dev)
*/
s3c24x0_identify_cpu(dev);
/*
* Manage the interrupt space.
* We need to put this after s3c24x0_identify_cpu as the avaliable
* interrupts change depending on which CPU we have.
*/
if (sc->sc_sx.sc_cpu == CPU_S3C2410)
irqmax = S3C2410_SUBIRQ_MAX;
else
irqmax = S3C2440_SUBIRQ_MAX;
if (rman_init(&s3c2xx0_softc->s3c2xx0_irq_rman) != 0 ||
rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman, 0,
irqmax) != 0 ||
rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman,
S3C24X0_EXTIRQ_MIN, S3C24X0_EXTIRQ_MAX))
panic("s3c24x0_attach: failed to set up IRQ rman");
/* calculate current clock frequency */
s3c24x0_clock_freq(&sc->sc_sx);
device_printf(dev, "fclk %d MHz hclk %d MHz pclk %d MHz\n",
@ -607,6 +704,33 @@ arm_get_next_irq(int last __unused)
return (irq);
return (S3C24X0_SUBIRQ_MIN + subirq);
case S3C24X0_INT_0:
case S3C24X0_INT_1:
case S3C24X0_INT_2:
case S3C24X0_INT_3:
/* There is a 1:1 mapping to the IRQ we are handling */
return S3C24X0_INT_EXT(irq);
case S3C24X0_INT_4_7:
case S3C24X0_INT_8_23:
/* Find the external interrupt being called */
subirq = 0x7fffff;
subirq &= bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTPEND);
subirq &= ~bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
if (subirq == 0)
return (irq);
subirq = ffs(subirq) - 1;
/* Clear the external irq pending bit */
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTPEND,
(1 << subirq));
return S3C24X0_INT_EXT(subirq);
}
return (irq);
@ -619,18 +743,28 @@ arm_mask_irq(uintptr_t irq)
{
u_int32_t mask;
if (irq >= S3C24X0_INT_EXT(0) && irq <= S3C24X0_INT_EXT(3)) {
/* External interrupt 0..3 are directly mapped to irq 0..3 */
irq -= S3C24X0_EXTIRQ_MIN;
}
if (irq < S3C24X0_SUBIRQ_MIN) {
mask = bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK);
mask |= (1 << irq);
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK, mask);
} else {
} else if (irq < S3C24X0_EXTIRQ_MIN) {
mask = bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK);
mask |= (1 << (irq - S3C24X0_SUBIRQ_MIN));
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK, mask);
} else {
mask = bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
mask |= (1 << (irq - S3C24X0_EXTIRQ_MIN));
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, GPIO_EINTMASK, mask);
}
}
@ -639,17 +773,27 @@ arm_unmask_irq(uintptr_t irq)
{
u_int32_t mask;
if (irq >= S3C24X0_INT_EXT(0) && irq <= S3C24X0_INT_EXT(3)) {
/* External interrupt 0..3 are directly mapped to irq 0..3 */
irq -= S3C24X0_EXTIRQ_MIN;
}
if (irq < S3C24X0_SUBIRQ_MIN) {
mask = bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK);
mask &= ~(1 << irq);
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK, mask);
} else {
} else if (irq < S3C24X0_EXTIRQ_MIN) {
mask = bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK);
mask &= ~(1 << (irq - S3C24X0_SUBIRQ_MIN));
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK, mask);
} else {
mask = bus_space_read_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
mask &= ~(1 << (irq - S3C24X0_EXTIRQ_MIN));
bus_space_write_4(&s3c2xx0_bs_tag,
s3c2xx0_softc->sc_intctl_ioh, GPIO_EINTMASK, mask);
}
}

View File

@ -207,7 +207,10 @@
#define S3C24X0_INT_BFLT 7 /* Battery fault */
#define S3C24X0_INT_8_23 5 /* Ext int 8..23 */
#define S3C24X0_INT_4_7 4 /* Ext int 4..7 */
#define S3C24X0_INT_EXT(n) (n) /* External interrupt [3:0] for 24{1,4}0 */
#define S3C24X0_INT_3 3
#define S3C24X0_INT_2 2
#define S3C24X0_INT_1 1
#define S3C24X0_INT_0 0
/* 24{1,4}0 has more than 32 interrupt sources. These are sub-sources
* that are OR-ed into main interrupt sources, and controlled via
@ -230,6 +233,15 @@
#define S3C24X0_INT_TXD0 (S3C24X0_SUBIRQ_MIN+1) /* UART0 Tx */
#define S3C24X0_INT_RXD0 (S3C24X0_SUBIRQ_MIN+0) /* UART0 Rx */
/*
* Support for external interrupts. We use values from 48
* to allow new CPU's to allocate new subirq's.
*/
#define S3C24X0_EXTIRQ_MIN 48
#define S3C24X0_EXTIRQ_COUNT 24
#define S3C24X0_EXTIRQ_MAX (S3C24X0_EXTIRQ_MIN + S3C24X0_EXTIRQ_COUNT - 1)
#define S3C24X0_INT_EXT(n) (S3C24X0_EXTIRQ_MIN + (n))
/* DMA controller */
/* XXX */