Author: andrew
Date: Sat Jul 24 23:41:09 2010
New Revision: 210458
URL: http://svn.freebsd.org/changeset/base/210458

Log:
  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)

Modified:
  head/sys/arm/s3c2xx0/s3c24x0.c
  head/sys/arm/s3c2xx0/s3c24x0reg.h

Modified: head/sys/arm/s3c2xx0/s3c24x0.c
==============================================================================
--- head/sys/arm/s3c2xx0/s3c24x0.c      Sat Jul 24 22:28:29 2010        
(r210457)
+++ head/sys/arm/s3c2xx0/s3c24x0.c      Sat Jul 24 23:41:09 2010        
(r210458)
@@ -118,6 +118,8 @@ static int  s3c24x0_setup_intr(device_t, 
         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
        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_
                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, devi
        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);
        }
 }

Modified: head/sys/arm/s3c2xx0/s3c24x0reg.h
==============================================================================
--- head/sys/arm/s3c2xx0/s3c24x0reg.h   Sat Jul 24 22:28:29 2010        
(r210457)
+++ head/sys/arm/s3c2xx0/s3c24x0reg.h   Sat Jul 24 23:41:09 2010        
(r210458)
@@ -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 */
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to