Author: loos
Date: Thu Apr  3 17:55:08 2014
New Revision: 264083
URL: http://svnweb.freebsd.org/changeset/base/264083

Log:
  Move the GPIO bank initialization to a new function to make easier to detect
  errors.
  
  Reset the GPIO module during the initialization.  This is guaranteed to be
  the same as a hardware reset.  Tested on AM335x (BBB) and checked against
  the omap3 and omap4 TRM.
  
  Do a better job freeing resources when there are errors and on
  ti_gpio_detach().

Modified:
  head/sys/arm/ti/ti_gpio.c

Modified: head/sys/arm/ti/ti_gpio.c
==============================================================================
--- head/sys/arm/ti/ti_gpio.c   Thu Apr  3 17:31:38 2014        (r264082)
+++ head/sys/arm/ti/ti_gpio.c   Thu Apr  3 17:55:08 2014        (r264083)
@@ -69,6 +69,9 @@ __FBSDID("$FreeBSD$");
 /* Register definitions */
 #define        TI_GPIO_REVISION                0x0000
 #define        TI_GPIO_SYSCONFIG               0x0010
+#define        TI_GPIO_SYSCONFIG_SOFTRESET             (1 << 1)
+#define        TI_GPIO_SYSCONFIG_AUTOIDLE              (1 << 0)
+#define        TI_GPIO_SYSSTATUS_RESETDONE             (1 << 0)
 #if defined(SOC_OMAP3)
 #define        TI_GPIO_SYSSTATUS               0x0014
 #define        TI_GPIO_IRQSTATUS1              0x0018
@@ -637,6 +640,107 @@ ti_gpio_probe(device_t dev)
        return (0);
 }
 
+static int
+ti_gpio_attach_intr(device_t dev)
+{
+       int i;
+       struct ti_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       for (i = 0; i < MAX_GPIO_BANKS; i++) {
+               if (sc->sc_irq_res[i] == NULL)
+                       break;
+
+               /*
+                * Register our interrupt handler for each of the IRQ resources.
+                */
+               if (bus_setup_intr(dev, sc->sc_irq_res[i],
+                   INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_gpio_intr, sc,
+                   &sc->sc_irq_hdl[i]) != 0) {
+                       device_printf(dev,
+                           "WARNING: unable to register interrupt handler\n");
+                       return (-1);
+               }
+       }
+
+       return (0);
+}
+
+static int
+ti_gpio_detach_intr(device_t dev)
+{
+       int i;
+       struct ti_gpio_softc *sc;
+
+       /* Teardown our interrupt handlers. */
+       sc = device_get_softc(dev);
+       for (i = 0; i < MAX_GPIO_BANKS; i++) {
+               if (sc->sc_irq_res[i] == NULL)
+                       break;
+
+               if (sc->sc_irq_hdl[i]) {
+                       bus_teardown_intr(dev, sc->sc_irq_res[i],
+                           sc->sc_irq_hdl[i]);
+               }
+       }
+
+       return (0);
+}
+
+static int
+ti_gpio_bank_init(device_t dev, int bank)
+{
+       int pin, timeout;
+       struct ti_gpio_softc *sc;
+       uint32_t flags, reg_oe;
+
+       sc = device_get_softc(dev);
+
+       /* Enable the interface and functional clocks for the module. */
+       ti_prcm_clk_enable(GPIO0_CLK + FIRST_GPIO_BANK + bank);
+
+       /* Reset the GPIO module. */
+       timeout = 0;
+       ti_gpio_write_4(sc, bank, TI_GPIO_SYSCONFIG, 
TI_GPIO_SYSCONFIG_SOFTRESET);
+       while ((ti_gpio_read_4(sc, bank, TI_GPIO_SYSSTATUS) &
+           TI_GPIO_SYSSTATUS_RESETDONE) == 0) {
+               if (timeout++ > 100)
+                       return (EBUSY);
+               DELAY(100);
+       }
+
+       /*
+        * Read the revision number of the module.  TI don't publish the
+        * actual revision numbers, so instead the values have been
+        * determined by experimentation.
+        */
+       sc->sc_revision[bank] = ti_gpio_read_4(sc, bank, TI_GPIO_REVISION);
+
+       /* Check the revision. */
+       if (sc->sc_revision[bank] != TI_GPIO_REV) {
+               device_printf(dev, "Warning: could not determine the revision "
+                   "of %u GPIO module (revision:0x%08x)\n",
+                   bank, sc->sc_revision[bank]);
+               return (EINVAL);
+       }
+
+       /* Disable interrupts for all pins. */
+       ti_gpio_write_4(sc, bank, TI_GPIO_CLEARIRQENABLE1, 0xffffffff);
+       ti_gpio_write_4(sc, bank, TI_GPIO_CLEARIRQENABLE2, 0xffffffff);
+
+       /* Init OE register based on pads configuration. */
+       reg_oe = 0xffffffff;
+       for (pin = 0; pin < PINS_PER_BANK; pin++) {
+               ti_scm_padconf_get_gpioflags(PINS_PER_BANK * bank + pin,
+                   &flags);
+               if (flags & GPIO_PIN_OUTPUT)
+                       reg_oe &= ~(1UL << pin);
+       }
+       ti_gpio_write_4(sc, bank, TI_GPIO_OE, reg_oe);
+ 
+       return (0);
+}
+
 /**
  *     ti_gpio_attach - attach function for the driver
  *     @dev: gpio device handle
@@ -653,13 +757,11 @@ ti_gpio_probe(device_t dev)
 static int
 ti_gpio_attach(device_t dev)
 {
-       struct ti_gpio_softc *sc = device_get_softc(dev);
+       struct ti_gpio_softc *sc;
        unsigned int i;
-       int err = 0;
-       int pin;
-       uint32_t flags;
-       uint32_t reg_oe;
+       int err;
 
+       sc = device_get_softc(dev);
        sc->sc_dev = dev;
 
        TI_GPIO_LOCK_INIT(sc);
@@ -668,30 +770,24 @@ ti_gpio_attach(device_t dev)
         * memory areas on the chip.  The memory range should have been set for
         * the driver when it was added as a child.
         */
-       err = bus_alloc_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res);
-       if (err) {
+       if (bus_alloc_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res) != 0) {
                device_printf(dev, "Error: could not allocate mem resources\n");
                return (ENXIO);
        }
 
        /* Request the IRQ resources */
-       err = bus_alloc_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res);
-       if (err) {
+       if (bus_alloc_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res) != 0) {
+               bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res);
                device_printf(dev, "Error: could not allocate irq resources\n");
                return (ENXIO);
        }
 
        /* Setup the IRQ resources */
-       for (i = 0; i < MAX_GPIO_BANKS; i++) {
-               if (sc->sc_irq_res[i] == NULL)
-                       break;
-
-               /* Register an interrupt handler for each of the IRQ resources 
*/
-               if ((bus_setup_intr(dev, sc->sc_irq_res[i], INTR_TYPE_MISC | 
INTR_MPSAFE, 
-                                   NULL, ti_gpio_intr, sc, 
&(sc->sc_irq_hdl[i])))) {
-                       device_printf(dev, "WARNING: unable to register 
interrupt handler\n");
-                       return (ENXIO);
-               }
+       if (ti_gpio_attach_intr(dev) != 0) {
+               ti_gpio_detach_intr(dev);
+               bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res);
+               bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res);
+               return (ENXIO);
        }
 
        /* We need to go through each block and ensure the clocks are running 
and
@@ -701,38 +797,16 @@ ti_gpio_attach(device_t dev)
         */
        for (i = 0; i < MAX_GPIO_BANKS; i++) {
                if (sc->sc_mem_res[i] != NULL) {
-
-                       /* Enable the interface and functional clocks for the 
module */
-                       ti_prcm_clk_enable(GPIO0_CLK + FIRST_GPIO_BANK + i);
-
-                       /* Read the revision number of the module. TI don't 
publish the
-                        * actual revision numbers, so instead the values have 
been
-                        * determined by experimentation.
-                        */
-                       sc->sc_revision[i] = ti_gpio_read_4(sc, i, 
TI_GPIO_REVISION);
-
-                       /* Check the revision */
-                       if (sc->sc_revision[i] != TI_GPIO_REV) {
-                               device_printf(dev, "Warning: could not 
determine the revision"
-                                             "of %u GPIO module 
(revision:0x%08x)\n",
-                                             i, sc->sc_revision[i]);
-                               continue;
-                       }
-
-                       /* Disable interrupts for all pins */
-                       ti_gpio_write_4(sc, i, TI_GPIO_CLEARIRQENABLE1, 
0xffffffff);
-                       ti_gpio_write_4(sc, i, TI_GPIO_CLEARIRQENABLE2, 
0xffffffff);
-
-                       /* Init OE register based on pads configuration */
-                       reg_oe = 0xffffffff;
-                       for (pin = 0; pin < 32; pin++) {
-                               ti_scm_padconf_get_gpioflags(
-                                   PINS_PER_BANK*i + pin, &flags);
-                               if (flags & GPIO_PIN_OUTPUT)
-                                       reg_oe &= ~(1U << pin);
+                       /* Reset and initialize the GPIO module. */
+                       err = ti_gpio_bank_init(dev, i);
+                       if (err != 0) {
+                               ti_gpio_detach_intr(dev);
+                               bus_release_resources(dev, ti_gpio_irq_spec,
+                                   sc->sc_irq_res);
+                               bus_release_resources(dev, ti_gpio_mem_spec,
+                                   sc->sc_mem_res);
+                               return (err);
                        }
-
-                       ti_gpio_write_4(sc, i, TI_GPIO_OE, reg_oe);
                }
        }
 
@@ -774,13 +848,10 @@ ti_gpio_detach(device_t dev)
 
        bus_generic_detach(dev);
 
-       /* Release the memory and IRQ resources */
-       for (i = 0; i < MAX_GPIO_BANKS; i++) {
-               if (sc->sc_mem_res[i] != NULL)
-                       bus_release_resource(dev, SYS_RES_MEMORY, i, 
sc->sc_mem_res[i]);
-               if (sc->sc_irq_res[i] != NULL)
-                       bus_release_resource(dev, SYS_RES_IRQ, i, 
sc->sc_irq_res[i]);
-       }
+       /* Release the memory and IRQ resources. */
+       ti_gpio_detach_intr(dev);
+       bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res);
+       bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res);
 
        TI_GPIO_LOCK_DESTROY(sc);
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to