summaryrefslogtreecommitdiffstats
path: root/sys/arm/s3c2xx0
diff options
context:
space:
mode:
authorandrew <andrew@FreeBSD.org>2010-07-24 23:41:09 +0000
committerandrew <andrew@FreeBSD.org>2010-07-24 23:41:09 +0000
commita656afced5987a67c02dc3a0bc80380cf0ae26d2 (patch)
tree3bc9d863a16e871ca4b234075a6472efda25b5da /sys/arm/s3c2xx0
parent2e35298c1d92dd0df0226c387309a162c44ca282 (diff)
downloadFreeBSD-src-a656afced5987a67c02dc3a0bc80380cf0ae26d2.zip
FreeBSD-src-a656afced5987a67c02dc3a0bc80380cf0ae26d2.tar.gz
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)
Diffstat (limited to 'sys/arm/s3c2xx0')
-rw-r--r--sys/arm/s3c2xx0/s3c24x0.c156
-rw-r--r--sys/arm/s3c2xx0/s3c24x0reg.h14
2 files changed, 163 insertions, 7 deletions
diff --git a/sys/arm/s3c2xx0/s3c24x0.c b/sys/arm/s3c2xx0/s3c24x0.c
index fbcc42c..7550cb1 100644
--- a/sys/arm/s3c2xx0/s3c24x0.c
+++ b/sys/arm/s3c2xx0/s3c24x0.c
@@ -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);
}
}
diff --git a/sys/arm/s3c2xx0/s3c24x0reg.h b/sys/arm/s3c2xx0/s3c24x0reg.h
index 1c18fa0..3e93e6c 100644
--- a/sys/arm/s3c2xx0/s3c24x0reg.h
+++ b/sys/arm/s3c2xx0/s3c24x0reg.h
@@ -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 */
OpenPOWER on IntegriCloud