mx31: rework of iomux support
authorValentin Longchamp <valentin.longchamp@epfl.ch>
Wed, 28 Jan 2009 14:13:50 +0000 (15:13 +0100)
committerSascha Hauer <s.hauer@pengutronix.de>
Fri, 13 Mar 2009 09:34:00 +0000 (10:34 +0100)
This new implemenatation avoids that two physical pins are claimed by
the same driver (also with the the gpr hardware modes).
The gpio kernel lib is also called when a capable gpio pin is assigned
its gpio function.
The mxc_iomux_mode function is still here for backward compatibility but
should not be used anymore.

V2:

In the precendent revision, the iomux code was claiming a pin when its
hardware mode was changed. This was uncorrect: when the hardware mode is
changed, the pin must still be claimed through the iomux.

In order to have a pin working in mode hw2, we must fist issue the
mxc_iomux_set_gpr call and then the corresponding mxc_iomux_mode calls
with the FUNC mode (usually done with mxc_iomux_setup_multiple_pins).
The reverse calls must be done to fee the pins.

Signed-off-by: Valentin Longchamp <valentin.longchamp@epfl.ch>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
arch/arm/mach-mx3/iomux.c
arch/arm/plat-mxc/include/mach/iomux-mx3.h

index 7a5088b519a8c6a1d077b4bd3cd997b7adde7c9f..40ffc5a664d93231c176956f0a3c927a6631a787 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
  * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
+ * Copyright (C) 2009 by Valentin Longchamp <valentin.longchamp@epfl.ch>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -21,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
+#include <linux/kernel.h>
 #include <mach/hardware.h>
 #include <mach/gpio.h>
 #include <mach/iomux-mx3.h>
@@ -38,6 +40,8 @@
 static DEFINE_SPINLOCK(gpio_mux_lock);
 
 #define IOMUX_REG_MASK (IOMUX_PADNUM_MASK & ~0x3)
+
+unsigned long mxc_pin_alloc_map[NB_PORTS * 32 / BITS_PER_LONG];
 /*
  * set the mode for a IOMUX pin.
  */
@@ -50,9 +54,6 @@ int mxc_iomux_mode(unsigned int pin_mode)
        field = pin_mode & 0x3;
        mode = (pin_mode & IOMUX_MODE_MASK) >> IOMUX_MODE_SHIFT;
 
-       pr_debug("%s: reg offset = 0x%x field = %d mode = 0x%02x\n",
-                       __func__, (pin_mode & IOMUX_REG_MASK), field, mode);
-
        spin_lock(&gpio_mux_lock);
 
        l = __raw_readl(reg);
@@ -92,6 +93,86 @@ void mxc_iomux_set_pad(enum iomux_pins pin, u32 config)
 }
 EXPORT_SYMBOL(mxc_iomux_set_pad);
 
+/*
+ * setups a single pin:
+ *     - reserves the pin so that it is not claimed by another driver
+ *     - setups the iomux according to the configuration
+ *     - if the pin is configured as a GPIO, we claim it through kernel gpiolib
+ */
+int mxc_iomux_setup_pin(const unsigned int pin, const char *label)
+{
+       unsigned pad = pin & IOMUX_PADNUM_MASK;
+       unsigned gpio;
+
+       if (pad >= (PIN_MAX + 1)) {
+               printk(KERN_ERR "mxc_iomux: Attempt to request nonexistant pin %u for \"%s\"\n",
+                       pad, label ? label : "?");
+               return -EINVAL;
+       }
+
+       if (test_and_set_bit(pad, mxc_pin_alloc_map)) {
+               printk(KERN_ERR "mxc_iomux: pin %u already used. Allocation for \"%s\" failed\n",
+                       pad, label ? label : "?");
+               return -EINVAL;
+       }
+       mxc_iomux_mode(pin);
+
+       /* if we have a gpio, we can allocate it */
+       gpio = (pin & IOMUX_GPIONUM_MASK) >> IOMUX_GPIONUM_SHIFT;
+       if (gpio < (GPIO_PORT_MAX + 1) * 32)
+               if (gpio_request(gpio, label))
+                       return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL(mxc_iomux_setup_pin);
+
+int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count,
+               const char *label)
+{
+       unsigned int *p = pin_list;
+       int i;
+       int ret = -EINVAL;
+
+       for (i = 0; i < count; i++) {
+               if (mxc_iomux_setup_pin(*p, label))
+                       goto setup_error;
+               p++;
+       }
+       return 0;
+
+setup_error:
+       mxc_iomux_release_multiple_pins(pin_list, i);
+       return ret;
+}
+EXPORT_SYMBOL(mxc_iomux_setup_multiple_pins);
+
+void mxc_iomux_release_pin(const unsigned int pin)
+{
+       unsigned pad = pin & IOMUX_PADNUM_MASK;
+       unsigned gpio;
+
+       if (pad < (PIN_MAX + 1))
+               clear_bit(pad, mxc_pin_alloc_map);
+
+       gpio = (pin & IOMUX_GPIONUM_MASK) >> IOMUX_GPIONUM_SHIFT;
+       if (gpio < (GPIO_PORT_MAX + 1) * 32)
+               gpio_free(gpio);
+}
+EXPORT_SYMBOL(mxc_iomux_release_pin);
+
+void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count)
+{
+       unsigned int *p = pin_list;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               mxc_iomux_release_pin(*p);
+               p++;
+       }
+}
+EXPORT_SYMBOL(mxc_iomux_release_multiple_pins);
+
 /*
  * This function enables/disables the general purpose function for a particular
  * signal.
@@ -111,4 +192,3 @@ void mxc_iomux_set_gpr(enum iomux_gp_func gp, bool en)
        spin_unlock(&gpio_mux_lock);
 }
 EXPORT_SYMBOL(mxc_iomux_set_gpr);
-
index c9198c0aea1827a10580a492512bee7de200f1fe..4bdc4712be1d75ddd1d0ffa80f36062a3589e329 100644 (file)
@@ -92,7 +92,7 @@ enum iomux_gp_func {
        MUX_EXTDMAREQ2_MBX_SEL          = 1 << 15,
        MUX_TAMPER_DETECT_EN            = 1 << 16,
        MUX_PGP_USB_4WIRE               = 1 << 17,
-       MUX_PGB_USB_COMMON              = 1 << 18,
+       MUX_PGP_USB_COMMON              = 1 << 18,
        MUX_SDHC_MEMSTICK1              = 1 << 19,
        MUX_SDHC_MEMSTICK2              = 1 << 20,
        MUX_PGP_SPLL_BYP                = 1 << 21,
@@ -109,21 +109,44 @@ enum iomux_gp_func {
 };
 
 /*
- * This function enables/disables the general purpose function for a particular
- * signal.
+ * setups a single pin:
+ *     - reserves the pin so that it is not claimed by another driver
+ *     - setups the iomux according to the configuration
+ *     - if the pin is configured as a GPIO, we claim it throug kernel gpiolib
+ */
+int mxc_iomux_setup_pin(const unsigned int pin, const char *label);
+/*
+ * setups mutliple pins
+ * convenient way to call the above function with tables
  */
-void iomux_config_gpr(enum iomux_gp_func , bool);
+int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count,
+               const char *label);
 
 /*
- * set the mode for a IOMUX pin.
+ * releases a single pin:
+ *     - make it available for a future use by another driver
+ *     - frees the GPIO if the pin was configured as GPIO
+ *     - DOES NOT reconfigure the IOMUX in its reset state
  */
-int mxc_iomux_mode(unsigned int);
+void mxc_iomux_release_pin(const unsigned int pin);
+/*
+ * releases multiple pins
+ * convenvient way to call the above function with tables
+ */
+void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count);
 
 /*
  * This function enables/disables the general purpose function for a particular
  * signal.
  */
-void mxc_iomux_set_gpr(enum iomux_gp_func, bool);
+void mxc_iomux_set_gpr(enum iomux_gp_func, bool en);
+
+/*
+ * This function only configures the iomux hardware.
+ * It is called by the setup functions and should not be called directly anymore.
+ * It is here visible for backward compatibility
+ */
+int mxc_iomux_mode(unsigned int pin_mode);
 
 #define IOMUX_PADNUM_MASK      0x1ff
 #define IOMUX_GPIONUM_SHIFT    9
@@ -143,6 +166,11 @@ void mxc_iomux_set_gpr(enum iomux_gp_func, bool);
        (((iomux_pin & IOMUX_GPIONUM_MASK) >> IOMUX_GPIONUM_SHIFT) + \
        MXC_GPIO_IRQ_START)
 
+/*
+ * The number of gpio devices among the pads
+ */
+#define GPIO_PORT_MAX 3
+
 /*
  * This enumeration is constructed based on the Section
  * "sw_pad_ctl & sw_mux_ctl details" of the MX31 IC Spec. Each enumerated
@@ -480,6 +508,9 @@ enum iomux_pins {
        MX31_PIN_CAPTURE        = IOMUX_PIN( 7,   327),
 };
 
+#define PIN_MAX 327
+#define NB_PORTS 12 /* NB_PINS/32, we chose 32 pins per "PORT" */
+
 /*
  * Convenience values for use with mxc_iomux_mode()
  *