From: Gregory Bean <gbean@codeaurora.org>
To: davidb@codeaurora.org
Cc: linux-arm-msm@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
Gregory Bean <gbean@codeaurora.org>
Subject: [PATCH 1/2] msm: gpiomux: Improve gpiomux interface design.
Date: Mon, 13 Dec 2010 09:02:49 -0800 [thread overview]
Message-ID: <1292259770-22972-1-git-send-email-gbean@codeaurora.org> (raw)
Fix the gpiomux interface by:
- collapsing redundant abstractions into shared common ones
- disconnecting gpiomux abstractions from particular MSM implementations
- moving platform-specific details out of the common abstraction
- moving implementation complexity out of the interface and back into
the implementation, where it should have been in the first place
- allowing for the future extension of the set of power states
- disambiguating 'settings' and 'configurations'.
- improving the initialization sequence to provide more flexibility
- not forcing lines to reset to input mode at power transitions
Signed-off-by: Gregory Bean <gbean@codeaurora.org>
---
arch/arm/mach-msm/gpio.c | 17 ++++
arch/arm/mach-msm/gpio.h | 28 +++++++
arch/arm/mach-msm/gpiomux-7x30.c | 69 +++++++++++++----
arch/arm/mach-msm/gpiomux-8x50.c | 43 +++++++++--
arch/arm/mach-msm/gpiomux-8x60.c | 9 ++-
arch/arm/mach-msm/gpiomux-v1.c | 24 +++++-
arch/arm/mach-msm/gpiomux-v1.h | 67 -----------------
arch/arm/mach-msm/gpiomux-v2.c | 17 ++++-
arch/arm/mach-msm/gpiomux-v2.h | 61 ---------------
arch/arm/mach-msm/gpiomux.c | 121 ++++++++++++++++++++++--------
arch/arm/mach-msm/gpiomux.h | 152 +++++++++++++++++++++++++++-----------
11 files changed, 378 insertions(+), 230 deletions(-)
create mode 100644 arch/arm/mach-msm/gpio.h
delete mode 100644 arch/arm/mach-msm/gpiomux-v1.h
delete mode 100644 arch/arm/mach-msm/gpiomux-v2.h
diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c
index 33051b5..3f23454 100644
--- a/arch/arm/mach-msm/gpio.c
+++ b/arch/arm/mach-msm/gpio.c
@@ -374,3 +374,20 @@ static int __init msm_init_gpio(void)
}
postcore_initcall(msm_init_gpio);
+
+/* Locate the GPIO_OUT register for the given GPIO and return its address
+ * and the bit position of the gpio's bit within the register.
+ *
+ * This function is used by gpiomux-v1 in order to support output transitions.
+ */
+void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
+ unsigned *offset)
+{
+ struct msm_gpio_chip *msm_chip = msm_gpio_chips;
+
+ while (gpio >= msm_chip->chip.base + msm_chip->chip.ngpio)
+ ++msm_chip;
+
+ *out = msm_chip->regs.out;
+ *offset = gpio - msm_chip->chip.base;
+}
diff --git a/arch/arm/mach-msm/gpio.h b/arch/arm/mach-msm/gpio.h
new file mode 100644
index 0000000..b143b8c
--- /dev/null
+++ b/arch/arm/mach-msm/gpio.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef _ARCH_ARM_MACH_MSM_GPIO_H_
+#define _ARCH_ARM_MACH_MSM_GPIO_H_
+
+/* Locate the GPIO_OUT register for the given GPIO and return its address
+ * and the bit position of the gpio's bit within the register.
+ *
+ * This function is used by gpiomux-v1 in order to support output transitions.
+ */
+void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
+ unsigned *offset);
+
+#endif
diff --git a/arch/arm/mach-msm/gpiomux-7x30.c b/arch/arm/mach-msm/gpiomux-7x30.c
index 6ce41c5..fadb5da 100644
--- a/arch/arm/mach-msm/gpiomux-7x30.c
+++ b/arch/arm/mach-msm/gpiomux-7x30.c
@@ -14,25 +14,64 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/module.h>
+#include <mach/irqs.h>
#include "gpiomux.h"
-struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {
-#ifdef CONFIG_SERIAL_MSM_CONSOLE
- [49] = { /* UART2 RFR */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+static struct msm_gpiomux_config msm7x30_uart2_configs[] __initdata = {
+ {
+ .gpio = 49, /* UART2 RFR */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [50] = { /* UART2 CTS */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+ {
+ .gpio = 50, /* UART2 CTS */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [51] = { /* UART2 RX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+ {
+ .gpio = 51, /* UART2 RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [52] = { /* UART2 TX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+ {
+ .gpio = 52, /* UART2 TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
-#endif
};
+
+static int __init gpiomux_init(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc)
+ return rc;
+
+ msm_gpiomux_install(msm7x30_uart2_configs,
+ ARRAY_SIZE(msm7x30_uart2_configs));
+
+ return 0;
+}
+postcore_initcall(gpiomux_init);
diff --git a/arch/arm/mach-msm/gpiomux-8x50.c b/arch/arm/mach-msm/gpiomux-8x50.c
index 4406e0f..dd45851 100644
--- a/arch/arm/mach-msm/gpiomux-8x50.c
+++ b/arch/arm/mach-msm/gpiomux-8x50.c
@@ -14,15 +14,44 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/module.h>
+#include <mach/irqs.h>
#include "gpiomux.h"
-struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {
- [86] = { /* UART3 RX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_1 | GPIOMUX_VALID,
+static struct msm_gpiomux_config msm8x50_uart3_configs[] __initdata = {
+ {
+ .gpio = 86, /* UART3 RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [87] = { /* UART3 TX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_1 | GPIOMUX_VALID,
+ {
+ .gpio = 87, /* UART3 TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
};
+
+static int __init gpiomux_init(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc)
+ return rc;
+
+ msm_gpiomux_install(msm8x50_uart3_configs,
+ ARRAY_SIZE(msm8x50_uart3_configs));
+
+ return 0;
+}
+postcore_initcall(gpiomux_init);
diff --git a/arch/arm/mach-msm/gpiomux-8x60.c b/arch/arm/mach-msm/gpiomux-8x60.c
index 7b380b3..9415ae1 100644
--- a/arch/arm/mach-msm/gpiomux-8x60.c
+++ b/arch/arm/mach-msm/gpiomux-8x60.c
@@ -14,6 +14,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/module.h>
+#include <mach/irqs.h>
+#include <asm/mach-types.h>
#include "gpiomux.h"
-struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {};
+static int __init gpiomux_init(void)
+{
+ return msm_gpiomux_init(NR_GPIO_IRQS);
+}
+postcore_initcall(gpiomux_init);
diff --git a/arch/arm/mach-msm/gpiomux-v1.c b/arch/arm/mach-msm/gpiomux-v1.c
index 27de2ab..9ba2acd 100644
--- a/arch/arm/mach-msm/gpiomux-v1.c
+++ b/arch/arm/mach-msm/gpiomux-v1.c
@@ -14,17 +14,35 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
+#include <linux/io.h>
#include "gpiomux.h"
#include "proc_comm.h"
+#include "gpio.h"
-void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val)
+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val)
{
- unsigned tlmm_config = (val & ~GPIOMUX_CTL_MASK) |
- ((gpio & 0x3ff) << 4);
+ unsigned tlmm_config;
unsigned tlmm_disable = 0;
+ void __iomem *out_reg;
+ unsigned offset;
+ uint32_t bits;
int rc;
+ tlmm_config = (val.drv << 17) |
+ (val.pull << 15) |
+ ((gpio & 0x3ff) << 4) |
+ val.func;
+ if (val.func == GPIOMUX_FUNC_GPIO) {
+ tlmm_config |= (val.dir > GPIOMUX_IN ? BIT(14) : 0);
+ msm_gpio_find_out(gpio, &out_reg, &offset);
+ bits = readl(out_reg);
+ if (val.dir == GPIOMUX_OUT_HIGH)
+ writel(bits | BIT(offset), out_reg);
+ else
+ writel(bits & ~BIT(offset), out_reg);
+ }
rc = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
&tlmm_config, &tlmm_disable);
if (rc)
diff --git a/arch/arm/mach-msm/gpiomux-v1.h b/arch/arm/mach-msm/gpiomux-v1.h
deleted file mode 100644
index 71d86fe..0000000
--- a/arch/arm/mach-msm/gpiomux-v1.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_V1_H
-#define __ARCH_ARM_MACH_MSM_GPIOMUX_V1_H
-
-#if defined(CONFIG_ARCH_MSM7X30)
-#define GPIOMUX_NGPIOS 182
-#elif defined(CONFIG_ARCH_QSD8X50)
-#define GPIOMUX_NGPIOS 165
-#else
-#define GPIOMUX_NGPIOS 133
-#endif
-
-typedef u32 gpiomux_config_t;
-
-enum {
- GPIOMUX_DRV_2MA = 0UL << 17,
- GPIOMUX_DRV_4MA = 1UL << 17,
- GPIOMUX_DRV_6MA = 2UL << 17,
- GPIOMUX_DRV_8MA = 3UL << 17,
- GPIOMUX_DRV_10MA = 4UL << 17,
- GPIOMUX_DRV_12MA = 5UL << 17,
- GPIOMUX_DRV_14MA = 6UL << 17,
- GPIOMUX_DRV_16MA = 7UL << 17,
-};
-
-enum {
- GPIOMUX_FUNC_GPIO = 0UL,
- GPIOMUX_FUNC_1 = 1UL,
- GPIOMUX_FUNC_2 = 2UL,
- GPIOMUX_FUNC_3 = 3UL,
- GPIOMUX_FUNC_4 = 4UL,
- GPIOMUX_FUNC_5 = 5UL,
- GPIOMUX_FUNC_6 = 6UL,
- GPIOMUX_FUNC_7 = 7UL,
- GPIOMUX_FUNC_8 = 8UL,
- GPIOMUX_FUNC_9 = 9UL,
- GPIOMUX_FUNC_A = 10UL,
- GPIOMUX_FUNC_B = 11UL,
- GPIOMUX_FUNC_C = 12UL,
- GPIOMUX_FUNC_D = 13UL,
- GPIOMUX_FUNC_E = 14UL,
- GPIOMUX_FUNC_F = 15UL,
-};
-
-enum {
- GPIOMUX_PULL_NONE = 0UL << 15,
- GPIOMUX_PULL_DOWN = 1UL << 15,
- GPIOMUX_PULL_KEEPER = 2UL << 15,
- GPIOMUX_PULL_UP = 3UL << 15,
-};
-
-#endif
diff --git a/arch/arm/mach-msm/gpiomux-v2.c b/arch/arm/mach-msm/gpiomux-v2.c
index 273396d..f74552f 100644
--- a/arch/arm/mach-msm/gpiomux-v2.c
+++ b/arch/arm/mach-msm/gpiomux-v2.c
@@ -14,12 +14,23 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/bitops.h>
#include <linux/io.h>
#include <mach/msm_iomap.h>
#include "gpiomux.h"
-void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val)
+#define GPIO_CFG(n) (MSM_TLMM_BASE + 0x1000 + (0x10 * n))
+#define GPIO_IN_OUT(n) (MSM_TLMM_BASE + 0x1004 + (0x10 * n))
+
+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val)
{
- writel(val & ~GPIOMUX_CTL_MASK,
- MSM_TLMM_BASE + 0x1000 + (0x10 * gpio));
+ uint32_t bits;
+
+ bits = (val.drv << 6) | (val.func << 2) | val.pull;
+ if (val.func == GPIOMUX_FUNC_GPIO) {
+ bits |= val.dir > GPIOMUX_IN ? BIT(9) : 0;
+ writel(val.dir == GPIOMUX_OUT_HIGH ? BIT(1) : 0,
+ GPIO_IN_OUT(gpio));
+ }
+ writel(bits, GPIO_CFG(gpio));
}
diff --git a/arch/arm/mach-msm/gpiomux-v2.h b/arch/arm/mach-msm/gpiomux-v2.h
deleted file mode 100644
index 3bf10e7..0000000
--- a/arch/arm/mach-msm/gpiomux-v2.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_V2_H
-#define __ARCH_ARM_MACH_MSM_GPIOMUX_V2_H
-
-#define GPIOMUX_NGPIOS 173
-
-typedef u16 gpiomux_config_t;
-
-enum {
- GPIOMUX_DRV_2MA = 0UL << 6,
- GPIOMUX_DRV_4MA = 1UL << 6,
- GPIOMUX_DRV_6MA = 2UL << 6,
- GPIOMUX_DRV_8MA = 3UL << 6,
- GPIOMUX_DRV_10MA = 4UL << 6,
- GPIOMUX_DRV_12MA = 5UL << 6,
- GPIOMUX_DRV_14MA = 6UL << 6,
- GPIOMUX_DRV_16MA = 7UL << 6,
-};
-
-enum {
- GPIOMUX_FUNC_GPIO = 0UL << 2,
- GPIOMUX_FUNC_1 = 1UL << 2,
- GPIOMUX_FUNC_2 = 2UL << 2,
- GPIOMUX_FUNC_3 = 3UL << 2,
- GPIOMUX_FUNC_4 = 4UL << 2,
- GPIOMUX_FUNC_5 = 5UL << 2,
- GPIOMUX_FUNC_6 = 6UL << 2,
- GPIOMUX_FUNC_7 = 7UL << 2,
- GPIOMUX_FUNC_8 = 8UL << 2,
- GPIOMUX_FUNC_9 = 9UL << 2,
- GPIOMUX_FUNC_A = 10UL << 2,
- GPIOMUX_FUNC_B = 11UL << 2,
- GPIOMUX_FUNC_C = 12UL << 2,
- GPIOMUX_FUNC_D = 13UL << 2,
- GPIOMUX_FUNC_E = 14UL << 2,
- GPIOMUX_FUNC_F = 15UL << 2,
-};
-
-enum {
- GPIOMUX_PULL_NONE = 0UL,
- GPIOMUX_PULL_DOWN = 1UL,
- GPIOMUX_PULL_KEEPER = 2UL,
- GPIOMUX_PULL_UP = 3UL,
-};
-
-#endif
diff --git a/arch/arm/mach-msm/gpiomux.c b/arch/arm/mach-msm/gpiomux.c
index 53af21a..637845f 100644
--- a/arch/arm/mach-msm/gpiomux.c
+++ b/arch/arm/mach-msm/gpiomux.c
@@ -15,50 +15,74 @@
* 02110-1301, USA.
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include "gpiomux.h"
+struct msm_gpiomux_rec {
+ struct gpiomux_setting *sets[GPIOMUX_NSETTINGS];
+ int ref;
+};
static DEFINE_SPINLOCK(gpiomux_lock);
+static struct msm_gpiomux_rec *msm_gpiomux_recs;
+static struct gpiomux_setting *msm_gpiomux_sets;
+static unsigned msm_gpiomux_ngpio;
-int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended)
+int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
+ struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
+ unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which;
unsigned long irq_flags;
- gpiomux_config_t setting;
+ struct gpiomux_setting *new_set;
+ int status = 0;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- if (active & GPIOMUX_VALID)
- cfg->active = active;
+ if (old_setting) {
+ if (rec->sets[which] == NULL)
+ status = 1;
+ else
+ *old_setting = *(rec->sets[which]);
+ }
- if (suspended & GPIOMUX_VALID)
- cfg->suspended = suspended;
+ if (setting) {
+ msm_gpiomux_sets[set_slot] = *setting;
+ rec->sets[which] = &msm_gpiomux_sets[set_slot];
+ } else {
+ rec->sets[which] = NULL;
+ }
- setting = cfg->ref ? active : suspended;
- if (setting & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, setting);
+ new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] :
+ rec->sets[GPIOMUX_SUSPENDED];
+ if (new_set)
+ __msm_gpiomux_write(gpio, *new_set);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
- return 0;
+ return status;
}
EXPORT_SYMBOL(msm_gpiomux_write);
int msm_gpiomux_get(unsigned gpio)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
unsigned long irq_flags;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- if (cfg->ref++ == 0 && cfg->active & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, cfg->active);
+ if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE])
+ __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
return 0;
}
@@ -66,31 +90,66 @@ EXPORT_SYMBOL(msm_gpiomux_get);
int msm_gpiomux_put(unsigned gpio)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
unsigned long irq_flags;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- BUG_ON(cfg->ref == 0);
- if (--cfg->ref == 0 && cfg->suspended & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, cfg->suspended);
+ BUG_ON(rec->ref == 0);
+ if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED])
+ __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
return 0;
}
EXPORT_SYMBOL(msm_gpiomux_put);
-static int __init gpiomux_init(void)
+int msm_gpiomux_init(size_t ngpio)
{
- unsigned n;
+ if (!ngpio)
+ return -EINVAL;
- for (n = 0; n < GPIOMUX_NGPIOS; ++n) {
- msm_gpiomux_configs[n].ref = 0;
- if (!(msm_gpiomux_configs[n].suspended & GPIOMUX_VALID))
- continue;
- __msm_gpiomux_write(n, msm_gpiomux_configs[n].suspended);
+ if (msm_gpiomux_recs)
+ return -EPERM;
+
+ msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio,
+ GFP_KERNEL);
+ if (!msm_gpiomux_recs)
+ return -ENOMEM;
+
+ /* There is no need to zero this memory, as clients will be blindly
+ * installing settings on top of it.
+ */
+ msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio *
+ GPIOMUX_NSETTINGS, GFP_KERNEL);
+ if (!msm_gpiomux_sets) {
+ kfree(msm_gpiomux_recs);
+ msm_gpiomux_recs = NULL;
+ return -ENOMEM;
}
+
+ msm_gpiomux_ngpio = ngpio;
+
return 0;
}
-postcore_initcall(gpiomux_init);
+EXPORT_SYMBOL(msm_gpiomux_init);
+
+void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs)
+{
+ unsigned c, s;
+ int rc;
+
+ for (c = 0; c < nconfigs; ++c) {
+ for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
+ rc = msm_gpiomux_write(configs[c].gpio, s,
+ &configs[c].settings[s], NULL);
+ if (rc)
+ pr_err("%s: write failure: %d\n", __func__, rc);
+ }
+ }
+}
+EXPORT_SYMBOL(msm_gpiomux_install);
diff --git a/arch/arm/mach-msm/gpiomux.h b/arch/arm/mach-msm/gpiomux.h
index b178d9c..227e210 100644
--- a/arch/arm/mach-msm/gpiomux.h
+++ b/arch/arm/mach-msm/gpiomux.h
@@ -20,56 +20,112 @@
#include <linux/bitops.h>
#include <linux/errno.h>
-#if defined(CONFIG_MSM_V2_TLMM)
-#include "gpiomux-v2.h"
-#else
-#include "gpiomux-v1.h"
-#endif
+enum msm_gpiomux_setting {
+ GPIOMUX_ACTIVE = 0,
+ GPIOMUX_SUSPENDED,
+ GPIOMUX_NSETTINGS
+};
+
+enum gpiomux_drv {
+ GPIOMUX_DRV_2MA = 0,
+ GPIOMUX_DRV_4MA,
+ GPIOMUX_DRV_6MA,
+ GPIOMUX_DRV_8MA,
+ GPIOMUX_DRV_10MA,
+ GPIOMUX_DRV_12MA,
+ GPIOMUX_DRV_14MA,
+ GPIOMUX_DRV_16MA,
+};
+
+enum gpiomux_func {
+ GPIOMUX_FUNC_GPIO = 0,
+ GPIOMUX_FUNC_1,
+ GPIOMUX_FUNC_2,
+ GPIOMUX_FUNC_3,
+ GPIOMUX_FUNC_4,
+ GPIOMUX_FUNC_5,
+ GPIOMUX_FUNC_6,
+ GPIOMUX_FUNC_7,
+ GPIOMUX_FUNC_8,
+ GPIOMUX_FUNC_9,
+ GPIOMUX_FUNC_A,
+ GPIOMUX_FUNC_B,
+ GPIOMUX_FUNC_C,
+ GPIOMUX_FUNC_D,
+ GPIOMUX_FUNC_E,
+ GPIOMUX_FUNC_F,
+};
+
+enum gpiomux_pull {
+ GPIOMUX_PULL_NONE = 0,
+ GPIOMUX_PULL_DOWN,
+ GPIOMUX_PULL_KEEPER,
+ GPIOMUX_PULL_UP,
+};
+
+/* Direction settings are only meaningful when GPIOMUX_FUNC_GPIO is selected.
+ * This element is ignored for all other FUNC selections, as the output-
+ * enable pin is not under software control in those cases. See the SWI
+ * for your target for more details.
+ */
+enum gpiomux_dir {
+ GPIOMUX_IN = 0,
+ GPIOMUX_OUT_HIGH,
+ GPIOMUX_OUT_LOW,
+};
+
+struct gpiomux_setting {
+ enum gpiomux_func func;
+ enum gpiomux_drv drv;
+ enum gpiomux_pull pull;
+ enum gpiomux_dir dir;
+};
/**
* struct msm_gpiomux_config: gpiomux settings for one gpio line.
*
- * A complete gpiomux config is the bitwise-or of a drive-strength,
- * function, and pull. For functions other than GPIO, the OE
- * is hard-wired according to the function. For GPIO mode,
- * OE is controlled by gpiolib.
- *
- * Available settings differ by target; see the gpiomux header
- * specific to your target arch for available configurations.
+ * A complete gpiomux config is the combination of a drive-strength,
+ * function, pull, and (sometimes) direction. For functions other than GPIO,
+ * the input/output setting is hard-wired according to the function.
*
- * @active: The configuration to be installed when the line is
- * active, or its reference count is > 0.
- * @suspended: The configuration to be installed when the line
- * is suspended, or its reference count is 0.
- * @ref: The reference count of the line. For internal use of
- * the gpiomux framework only.
+ * @gpio: The index number of the gpio being described.
+ * @settings: The settings to be installed, specifically:
+ * GPIOMUX_ACTIVE: The setting to be installed when the
+ * line is active, or its reference count is > 0.
+ * GPIOMUX_SUSPENDED: The setting to be installed when
+ * the line is suspended, or its reference count is 0.
*/
struct msm_gpiomux_config {
- gpiomux_config_t active;
- gpiomux_config_t suspended;
- unsigned ref;
+ unsigned gpio;
+ struct gpiomux_setting settings[GPIOMUX_NSETTINGS];
};
/**
- * @GPIOMUX_VALID: If set, the config field contains 'good data'.
- * The absence of this bit will prevent the gpiomux
- * system from applying the configuration under all
- * circumstances.
+ * struct msm_gpiomux_configs: a collection of gpiomux configs.
+ *
+ * It is so common to manage blocks of gpiomux configs that the data structure
+ * for doing so has been standardized here as a convenience.
+ *
+ * @cfg: A pointer to the first config in an array of configs.
+ * @ncfg: The number of configs in the array.
*/
-enum {
- GPIOMUX_VALID = BIT(sizeof(gpiomux_config_t) * BITS_PER_BYTE - 1),
- GPIOMUX_CTL_MASK = GPIOMUX_VALID,
+struct msm_gpiomux_configs {
+ struct msm_gpiomux_config *cfg;
+ size_t ncfg;
};
#ifdef CONFIG_MSM_GPIOMUX
-/* Each architecture must provide its own instance of this table.
- * To avoid having gpiomux manage any given gpio, one or both of
- * the entries can avoid setting GPIOMUX_VALID - the absence
- * of that flag will prevent the configuration from being applied
- * during state transitions.
+/* Before using gpiomux, initialize the subsystem by telling it how many
+ * gpios are going to be managed. Calling any other gpiomux functions before
+ * msm_gpiomux_init is unsupported.
+ */
+int msm_gpiomux_init(size_t ngpio);
+
+/* Install a block of gpiomux configurations in gpiomux. This is functionally
+ * identical to calling msm_gpiomux_write many times.
*/
-extern struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS];
+void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs);
/* Increment a gpio's reference count, possibly activating the line. */
int __must_check msm_gpiomux_get(unsigned gpio);
@@ -77,12 +133,16 @@ int __must_check msm_gpiomux_get(unsigned gpio);
/* Decrement a gpio's reference count, possibly suspending the line. */
int msm_gpiomux_put(unsigned gpio);
-/* Install a new configuration to the gpio line. To avoid overwriting
- * a configuration, leave the VALID bit out.
+/* Install a new setting in a gpio. To erase a slot, use NULL.
+ * The old setting that was overwritten can be passed back to the caller
+ * old_setting can be NULL if the caller is not interested in the previous
+ * setting
+ * If a previous setting was not available to return (NULL configuration)
+ * - the function returns 1
+ * else function returns 0
*/
-int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended);
+int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
+ struct gpiomux_setting *setting, struct gpiomux_setting *old_setting);
/* Architecture-internal function for use by the framework only.
* This function can assume the following:
@@ -92,8 +152,16 @@ int msm_gpiomux_write(unsigned gpio,
* This function is not for public consumption. External users
* should use msm_gpiomux_write.
*/
-void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val);
+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val);
#else
+static inline int msm_gpiomux_init(size_t ngpio)
+{
+ return -ENOSYS;
+}
+
+static inline void
+msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs) {}
+
static inline int __must_check msm_gpiomux_get(unsigned gpio)
{
return -ENOSYS;
@@ -105,8 +173,8 @@ static inline int msm_gpiomux_put(unsigned gpio)
}
static inline int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended)
+ enum msm_gpiomux_setting which, struct gpiomux_setting *setting,
+ struct gpiomux_setting *old_setting);
{
return -ENOSYS;
}
--
1.7.0.4
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
WARNING: multiple messages have this Message-ID (diff)
From: gbean@codeaurora.org (Gregory Bean)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/2] msm: gpiomux: Improve gpiomux interface design.
Date: Mon, 13 Dec 2010 09:02:49 -0800 [thread overview]
Message-ID: <1292259770-22972-1-git-send-email-gbean@codeaurora.org> (raw)
Fix the gpiomux interface by:
- collapsing redundant abstractions into shared common ones
- disconnecting gpiomux abstractions from particular MSM implementations
- moving platform-specific details out of the common abstraction
- moving implementation complexity out of the interface and back into
the implementation, where it should have been in the first place
- allowing for the future extension of the set of power states
- disambiguating 'settings' and 'configurations'.
- improving the initialization sequence to provide more flexibility
- not forcing lines to reset to input mode at power transitions
Signed-off-by: Gregory Bean <gbean@codeaurora.org>
---
arch/arm/mach-msm/gpio.c | 17 ++++
arch/arm/mach-msm/gpio.h | 28 +++++++
arch/arm/mach-msm/gpiomux-7x30.c | 69 +++++++++++++----
arch/arm/mach-msm/gpiomux-8x50.c | 43 +++++++++--
arch/arm/mach-msm/gpiomux-8x60.c | 9 ++-
arch/arm/mach-msm/gpiomux-v1.c | 24 +++++-
arch/arm/mach-msm/gpiomux-v1.h | 67 -----------------
arch/arm/mach-msm/gpiomux-v2.c | 17 ++++-
arch/arm/mach-msm/gpiomux-v2.h | 61 ---------------
arch/arm/mach-msm/gpiomux.c | 121 ++++++++++++++++++++++--------
arch/arm/mach-msm/gpiomux.h | 152 +++++++++++++++++++++++++++-----------
11 files changed, 378 insertions(+), 230 deletions(-)
create mode 100644 arch/arm/mach-msm/gpio.h
delete mode 100644 arch/arm/mach-msm/gpiomux-v1.h
delete mode 100644 arch/arm/mach-msm/gpiomux-v2.h
diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c
index 33051b5..3f23454 100644
--- a/arch/arm/mach-msm/gpio.c
+++ b/arch/arm/mach-msm/gpio.c
@@ -374,3 +374,20 @@ static int __init msm_init_gpio(void)
}
postcore_initcall(msm_init_gpio);
+
+/* Locate the GPIO_OUT register for the given GPIO and return its address
+ * and the bit position of the gpio's bit within the register.
+ *
+ * This function is used by gpiomux-v1 in order to support output transitions.
+ */
+void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
+ unsigned *offset)
+{
+ struct msm_gpio_chip *msm_chip = msm_gpio_chips;
+
+ while (gpio >= msm_chip->chip.base + msm_chip->chip.ngpio)
+ ++msm_chip;
+
+ *out = msm_chip->regs.out;
+ *offset = gpio - msm_chip->chip.base;
+}
diff --git a/arch/arm/mach-msm/gpio.h b/arch/arm/mach-msm/gpio.h
new file mode 100644
index 0000000..b143b8c
--- /dev/null
+++ b/arch/arm/mach-msm/gpio.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef _ARCH_ARM_MACH_MSM_GPIO_H_
+#define _ARCH_ARM_MACH_MSM_GPIO_H_
+
+/* Locate the GPIO_OUT register for the given GPIO and return its address
+ * and the bit position of the gpio's bit within the register.
+ *
+ * This function is used by gpiomux-v1 in order to support output transitions.
+ */
+void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
+ unsigned *offset);
+
+#endif
diff --git a/arch/arm/mach-msm/gpiomux-7x30.c b/arch/arm/mach-msm/gpiomux-7x30.c
index 6ce41c5..fadb5da 100644
--- a/arch/arm/mach-msm/gpiomux-7x30.c
+++ b/arch/arm/mach-msm/gpiomux-7x30.c
@@ -14,25 +14,64 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/module.h>
+#include <mach/irqs.h>
#include "gpiomux.h"
-struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {
-#ifdef CONFIG_SERIAL_MSM_CONSOLE
- [49] = { /* UART2 RFR */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+static struct msm_gpiomux_config msm7x30_uart2_configs[] __initdata = {
+ {
+ .gpio = 49, /* UART2 RFR */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [50] = { /* UART2 CTS */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+ {
+ .gpio = 50, /* UART2 CTS */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [51] = { /* UART2 RX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+ {
+ .gpio = 51, /* UART2 RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [52] = { /* UART2 TX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_2 | GPIOMUX_VALID,
+ {
+ .gpio = 52, /* UART2 TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
-#endif
};
+
+static int __init gpiomux_init(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc)
+ return rc;
+
+ msm_gpiomux_install(msm7x30_uart2_configs,
+ ARRAY_SIZE(msm7x30_uart2_configs));
+
+ return 0;
+}
+postcore_initcall(gpiomux_init);
diff --git a/arch/arm/mach-msm/gpiomux-8x50.c b/arch/arm/mach-msm/gpiomux-8x50.c
index 4406e0f..dd45851 100644
--- a/arch/arm/mach-msm/gpiomux-8x50.c
+++ b/arch/arm/mach-msm/gpiomux-8x50.c
@@ -14,15 +14,44 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/module.h>
+#include <mach/irqs.h>
#include "gpiomux.h"
-struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {
- [86] = { /* UART3 RX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_1 | GPIOMUX_VALID,
+static struct msm_gpiomux_config msm8x50_uart3_configs[] __initdata = {
+ {
+ .gpio = 86, /* UART3 RX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
- [87] = { /* UART3 TX */
- .suspended = GPIOMUX_DRV_2MA | GPIOMUX_PULL_DOWN |
- GPIOMUX_FUNC_1 | GPIOMUX_VALID,
+ {
+ .gpio = 87, /* UART3 TX */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ },
+ },
},
};
+
+static int __init gpiomux_init(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc)
+ return rc;
+
+ msm_gpiomux_install(msm8x50_uart3_configs,
+ ARRAY_SIZE(msm8x50_uart3_configs));
+
+ return 0;
+}
+postcore_initcall(gpiomux_init);
diff --git a/arch/arm/mach-msm/gpiomux-8x60.c b/arch/arm/mach-msm/gpiomux-8x60.c
index 7b380b3..9415ae1 100644
--- a/arch/arm/mach-msm/gpiomux-8x60.c
+++ b/arch/arm/mach-msm/gpiomux-8x60.c
@@ -14,6 +14,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/module.h>
+#include <mach/irqs.h>
+#include <asm/mach-types.h>
#include "gpiomux.h"
-struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = {};
+static int __init gpiomux_init(void)
+{
+ return msm_gpiomux_init(NR_GPIO_IRQS);
+}
+postcore_initcall(gpiomux_init);
diff --git a/arch/arm/mach-msm/gpiomux-v1.c b/arch/arm/mach-msm/gpiomux-v1.c
index 27de2ab..9ba2acd 100644
--- a/arch/arm/mach-msm/gpiomux-v1.c
+++ b/arch/arm/mach-msm/gpiomux-v1.c
@@ -14,17 +14,35 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
+#include <linux/io.h>
#include "gpiomux.h"
#include "proc_comm.h"
+#include "gpio.h"
-void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val)
+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val)
{
- unsigned tlmm_config = (val & ~GPIOMUX_CTL_MASK) |
- ((gpio & 0x3ff) << 4);
+ unsigned tlmm_config;
unsigned tlmm_disable = 0;
+ void __iomem *out_reg;
+ unsigned offset;
+ uint32_t bits;
int rc;
+ tlmm_config = (val.drv << 17) |
+ (val.pull << 15) |
+ ((gpio & 0x3ff) << 4) |
+ val.func;
+ if (val.func == GPIOMUX_FUNC_GPIO) {
+ tlmm_config |= (val.dir > GPIOMUX_IN ? BIT(14) : 0);
+ msm_gpio_find_out(gpio, &out_reg, &offset);
+ bits = readl(out_reg);
+ if (val.dir == GPIOMUX_OUT_HIGH)
+ writel(bits | BIT(offset), out_reg);
+ else
+ writel(bits & ~BIT(offset), out_reg);
+ }
rc = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX,
&tlmm_config, &tlmm_disable);
if (rc)
diff --git a/arch/arm/mach-msm/gpiomux-v1.h b/arch/arm/mach-msm/gpiomux-v1.h
deleted file mode 100644
index 71d86fe..0000000
--- a/arch/arm/mach-msm/gpiomux-v1.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_V1_H
-#define __ARCH_ARM_MACH_MSM_GPIOMUX_V1_H
-
-#if defined(CONFIG_ARCH_MSM7X30)
-#define GPIOMUX_NGPIOS 182
-#elif defined(CONFIG_ARCH_QSD8X50)
-#define GPIOMUX_NGPIOS 165
-#else
-#define GPIOMUX_NGPIOS 133
-#endif
-
-typedef u32 gpiomux_config_t;
-
-enum {
- GPIOMUX_DRV_2MA = 0UL << 17,
- GPIOMUX_DRV_4MA = 1UL << 17,
- GPIOMUX_DRV_6MA = 2UL << 17,
- GPIOMUX_DRV_8MA = 3UL << 17,
- GPIOMUX_DRV_10MA = 4UL << 17,
- GPIOMUX_DRV_12MA = 5UL << 17,
- GPIOMUX_DRV_14MA = 6UL << 17,
- GPIOMUX_DRV_16MA = 7UL << 17,
-};
-
-enum {
- GPIOMUX_FUNC_GPIO = 0UL,
- GPIOMUX_FUNC_1 = 1UL,
- GPIOMUX_FUNC_2 = 2UL,
- GPIOMUX_FUNC_3 = 3UL,
- GPIOMUX_FUNC_4 = 4UL,
- GPIOMUX_FUNC_5 = 5UL,
- GPIOMUX_FUNC_6 = 6UL,
- GPIOMUX_FUNC_7 = 7UL,
- GPIOMUX_FUNC_8 = 8UL,
- GPIOMUX_FUNC_9 = 9UL,
- GPIOMUX_FUNC_A = 10UL,
- GPIOMUX_FUNC_B = 11UL,
- GPIOMUX_FUNC_C = 12UL,
- GPIOMUX_FUNC_D = 13UL,
- GPIOMUX_FUNC_E = 14UL,
- GPIOMUX_FUNC_F = 15UL,
-};
-
-enum {
- GPIOMUX_PULL_NONE = 0UL << 15,
- GPIOMUX_PULL_DOWN = 1UL << 15,
- GPIOMUX_PULL_KEEPER = 2UL << 15,
- GPIOMUX_PULL_UP = 3UL << 15,
-};
-
-#endif
diff --git a/arch/arm/mach-msm/gpiomux-v2.c b/arch/arm/mach-msm/gpiomux-v2.c
index 273396d..f74552f 100644
--- a/arch/arm/mach-msm/gpiomux-v2.c
+++ b/arch/arm/mach-msm/gpiomux-v2.c
@@ -14,12 +14,23 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
+#include <linux/bitops.h>
#include <linux/io.h>
#include <mach/msm_iomap.h>
#include "gpiomux.h"
-void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val)
+#define GPIO_CFG(n) (MSM_TLMM_BASE + 0x1000 + (0x10 * n))
+#define GPIO_IN_OUT(n) (MSM_TLMM_BASE + 0x1004 + (0x10 * n))
+
+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val)
{
- writel(val & ~GPIOMUX_CTL_MASK,
- MSM_TLMM_BASE + 0x1000 + (0x10 * gpio));
+ uint32_t bits;
+
+ bits = (val.drv << 6) | (val.func << 2) | val.pull;
+ if (val.func == GPIOMUX_FUNC_GPIO) {
+ bits |= val.dir > GPIOMUX_IN ? BIT(9) : 0;
+ writel(val.dir == GPIOMUX_OUT_HIGH ? BIT(1) : 0,
+ GPIO_IN_OUT(gpio));
+ }
+ writel(bits, GPIO_CFG(gpio));
}
diff --git a/arch/arm/mach-msm/gpiomux-v2.h b/arch/arm/mach-msm/gpiomux-v2.h
deleted file mode 100644
index 3bf10e7..0000000
--- a/arch/arm/mach-msm/gpiomux-v2.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-#ifndef __ARCH_ARM_MACH_MSM_GPIOMUX_V2_H
-#define __ARCH_ARM_MACH_MSM_GPIOMUX_V2_H
-
-#define GPIOMUX_NGPIOS 173
-
-typedef u16 gpiomux_config_t;
-
-enum {
- GPIOMUX_DRV_2MA = 0UL << 6,
- GPIOMUX_DRV_4MA = 1UL << 6,
- GPIOMUX_DRV_6MA = 2UL << 6,
- GPIOMUX_DRV_8MA = 3UL << 6,
- GPIOMUX_DRV_10MA = 4UL << 6,
- GPIOMUX_DRV_12MA = 5UL << 6,
- GPIOMUX_DRV_14MA = 6UL << 6,
- GPIOMUX_DRV_16MA = 7UL << 6,
-};
-
-enum {
- GPIOMUX_FUNC_GPIO = 0UL << 2,
- GPIOMUX_FUNC_1 = 1UL << 2,
- GPIOMUX_FUNC_2 = 2UL << 2,
- GPIOMUX_FUNC_3 = 3UL << 2,
- GPIOMUX_FUNC_4 = 4UL << 2,
- GPIOMUX_FUNC_5 = 5UL << 2,
- GPIOMUX_FUNC_6 = 6UL << 2,
- GPIOMUX_FUNC_7 = 7UL << 2,
- GPIOMUX_FUNC_8 = 8UL << 2,
- GPIOMUX_FUNC_9 = 9UL << 2,
- GPIOMUX_FUNC_A = 10UL << 2,
- GPIOMUX_FUNC_B = 11UL << 2,
- GPIOMUX_FUNC_C = 12UL << 2,
- GPIOMUX_FUNC_D = 13UL << 2,
- GPIOMUX_FUNC_E = 14UL << 2,
- GPIOMUX_FUNC_F = 15UL << 2,
-};
-
-enum {
- GPIOMUX_PULL_NONE = 0UL,
- GPIOMUX_PULL_DOWN = 1UL,
- GPIOMUX_PULL_KEEPER = 2UL,
- GPIOMUX_PULL_UP = 3UL,
-};
-
-#endif
diff --git a/arch/arm/mach-msm/gpiomux.c b/arch/arm/mach-msm/gpiomux.c
index 53af21a..637845f 100644
--- a/arch/arm/mach-msm/gpiomux.c
+++ b/arch/arm/mach-msm/gpiomux.c
@@ -15,50 +15,74 @@
* 02110-1301, USA.
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include "gpiomux.h"
+struct msm_gpiomux_rec {
+ struct gpiomux_setting *sets[GPIOMUX_NSETTINGS];
+ int ref;
+};
static DEFINE_SPINLOCK(gpiomux_lock);
+static struct msm_gpiomux_rec *msm_gpiomux_recs;
+static struct gpiomux_setting *msm_gpiomux_sets;
+static unsigned msm_gpiomux_ngpio;
-int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended)
+int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
+ struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
+ unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which;
unsigned long irq_flags;
- gpiomux_config_t setting;
+ struct gpiomux_setting *new_set;
+ int status = 0;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- if (active & GPIOMUX_VALID)
- cfg->active = active;
+ if (old_setting) {
+ if (rec->sets[which] == NULL)
+ status = 1;
+ else
+ *old_setting = *(rec->sets[which]);
+ }
- if (suspended & GPIOMUX_VALID)
- cfg->suspended = suspended;
+ if (setting) {
+ msm_gpiomux_sets[set_slot] = *setting;
+ rec->sets[which] = &msm_gpiomux_sets[set_slot];
+ } else {
+ rec->sets[which] = NULL;
+ }
- setting = cfg->ref ? active : suspended;
- if (setting & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, setting);
+ new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] :
+ rec->sets[GPIOMUX_SUSPENDED];
+ if (new_set)
+ __msm_gpiomux_write(gpio, *new_set);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
- return 0;
+ return status;
}
EXPORT_SYMBOL(msm_gpiomux_write);
int msm_gpiomux_get(unsigned gpio)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
unsigned long irq_flags;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- if (cfg->ref++ == 0 && cfg->active & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, cfg->active);
+ if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE])
+ __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
return 0;
}
@@ -66,31 +90,66 @@ EXPORT_SYMBOL(msm_gpiomux_get);
int msm_gpiomux_put(unsigned gpio)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
unsigned long irq_flags;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- BUG_ON(cfg->ref == 0);
- if (--cfg->ref == 0 && cfg->suspended & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, cfg->suspended);
+ BUG_ON(rec->ref == 0);
+ if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED])
+ __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
return 0;
}
EXPORT_SYMBOL(msm_gpiomux_put);
-static int __init gpiomux_init(void)
+int msm_gpiomux_init(size_t ngpio)
{
- unsigned n;
+ if (!ngpio)
+ return -EINVAL;
- for (n = 0; n < GPIOMUX_NGPIOS; ++n) {
- msm_gpiomux_configs[n].ref = 0;
- if (!(msm_gpiomux_configs[n].suspended & GPIOMUX_VALID))
- continue;
- __msm_gpiomux_write(n, msm_gpiomux_configs[n].suspended);
+ if (msm_gpiomux_recs)
+ return -EPERM;
+
+ msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio,
+ GFP_KERNEL);
+ if (!msm_gpiomux_recs)
+ return -ENOMEM;
+
+ /* There is no need to zero this memory, as clients will be blindly
+ * installing settings on top of it.
+ */
+ msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio *
+ GPIOMUX_NSETTINGS, GFP_KERNEL);
+ if (!msm_gpiomux_sets) {
+ kfree(msm_gpiomux_recs);
+ msm_gpiomux_recs = NULL;
+ return -ENOMEM;
}
+
+ msm_gpiomux_ngpio = ngpio;
+
return 0;
}
-postcore_initcall(gpiomux_init);
+EXPORT_SYMBOL(msm_gpiomux_init);
+
+void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs)
+{
+ unsigned c, s;
+ int rc;
+
+ for (c = 0; c < nconfigs; ++c) {
+ for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
+ rc = msm_gpiomux_write(configs[c].gpio, s,
+ &configs[c].settings[s], NULL);
+ if (rc)
+ pr_err("%s: write failure: %d\n", __func__, rc);
+ }
+ }
+}
+EXPORT_SYMBOL(msm_gpiomux_install);
diff --git a/arch/arm/mach-msm/gpiomux.h b/arch/arm/mach-msm/gpiomux.h
index b178d9c..227e210 100644
--- a/arch/arm/mach-msm/gpiomux.h
+++ b/arch/arm/mach-msm/gpiomux.h
@@ -20,56 +20,112 @@
#include <linux/bitops.h>
#include <linux/errno.h>
-#if defined(CONFIG_MSM_V2_TLMM)
-#include "gpiomux-v2.h"
-#else
-#include "gpiomux-v1.h"
-#endif
+enum msm_gpiomux_setting {
+ GPIOMUX_ACTIVE = 0,
+ GPIOMUX_SUSPENDED,
+ GPIOMUX_NSETTINGS
+};
+
+enum gpiomux_drv {
+ GPIOMUX_DRV_2MA = 0,
+ GPIOMUX_DRV_4MA,
+ GPIOMUX_DRV_6MA,
+ GPIOMUX_DRV_8MA,
+ GPIOMUX_DRV_10MA,
+ GPIOMUX_DRV_12MA,
+ GPIOMUX_DRV_14MA,
+ GPIOMUX_DRV_16MA,
+};
+
+enum gpiomux_func {
+ GPIOMUX_FUNC_GPIO = 0,
+ GPIOMUX_FUNC_1,
+ GPIOMUX_FUNC_2,
+ GPIOMUX_FUNC_3,
+ GPIOMUX_FUNC_4,
+ GPIOMUX_FUNC_5,
+ GPIOMUX_FUNC_6,
+ GPIOMUX_FUNC_7,
+ GPIOMUX_FUNC_8,
+ GPIOMUX_FUNC_9,
+ GPIOMUX_FUNC_A,
+ GPIOMUX_FUNC_B,
+ GPIOMUX_FUNC_C,
+ GPIOMUX_FUNC_D,
+ GPIOMUX_FUNC_E,
+ GPIOMUX_FUNC_F,
+};
+
+enum gpiomux_pull {
+ GPIOMUX_PULL_NONE = 0,
+ GPIOMUX_PULL_DOWN,
+ GPIOMUX_PULL_KEEPER,
+ GPIOMUX_PULL_UP,
+};
+
+/* Direction settings are only meaningful when GPIOMUX_FUNC_GPIO is selected.
+ * This element is ignored for all other FUNC selections, as the output-
+ * enable pin is not under software control in those cases. See the SWI
+ * for your target for more details.
+ */
+enum gpiomux_dir {
+ GPIOMUX_IN = 0,
+ GPIOMUX_OUT_HIGH,
+ GPIOMUX_OUT_LOW,
+};
+
+struct gpiomux_setting {
+ enum gpiomux_func func;
+ enum gpiomux_drv drv;
+ enum gpiomux_pull pull;
+ enum gpiomux_dir dir;
+};
/**
* struct msm_gpiomux_config: gpiomux settings for one gpio line.
*
- * A complete gpiomux config is the bitwise-or of a drive-strength,
- * function, and pull. For functions other than GPIO, the OE
- * is hard-wired according to the function. For GPIO mode,
- * OE is controlled by gpiolib.
- *
- * Available settings differ by target; see the gpiomux header
- * specific to your target arch for available configurations.
+ * A complete gpiomux config is the combination of a drive-strength,
+ * function, pull, and (sometimes) direction. For functions other than GPIO,
+ * the input/output setting is hard-wired according to the function.
*
- * @active: The configuration to be installed when the line is
- * active, or its reference count is > 0.
- * @suspended: The configuration to be installed when the line
- * is suspended, or its reference count is 0.
- * @ref: The reference count of the line. For internal use of
- * the gpiomux framework only.
+ * @gpio: The index number of the gpio being described.
+ * @settings: The settings to be installed, specifically:
+ * GPIOMUX_ACTIVE: The setting to be installed when the
+ * line is active, or its reference count is > 0.
+ * GPIOMUX_SUSPENDED: The setting to be installed when
+ * the line is suspended, or its reference count is 0.
*/
struct msm_gpiomux_config {
- gpiomux_config_t active;
- gpiomux_config_t suspended;
- unsigned ref;
+ unsigned gpio;
+ struct gpiomux_setting settings[GPIOMUX_NSETTINGS];
};
/**
- * @GPIOMUX_VALID: If set, the config field contains 'good data'.
- * The absence of this bit will prevent the gpiomux
- * system from applying the configuration under all
- * circumstances.
+ * struct msm_gpiomux_configs: a collection of gpiomux configs.
+ *
+ * It is so common to manage blocks of gpiomux configs that the data structure
+ * for doing so has been standardized here as a convenience.
+ *
+ * @cfg: A pointer to the first config in an array of configs.
+ * @ncfg: The number of configs in the array.
*/
-enum {
- GPIOMUX_VALID = BIT(sizeof(gpiomux_config_t) * BITS_PER_BYTE - 1),
- GPIOMUX_CTL_MASK = GPIOMUX_VALID,
+struct msm_gpiomux_configs {
+ struct msm_gpiomux_config *cfg;
+ size_t ncfg;
};
#ifdef CONFIG_MSM_GPIOMUX
-/* Each architecture must provide its own instance of this table.
- * To avoid having gpiomux manage any given gpio, one or both of
- * the entries can avoid setting GPIOMUX_VALID - the absence
- * of that flag will prevent the configuration from being applied
- * during state transitions.
+/* Before using gpiomux, initialize the subsystem by telling it how many
+ * gpios are going to be managed. Calling any other gpiomux functions before
+ * msm_gpiomux_init is unsupported.
+ */
+int msm_gpiomux_init(size_t ngpio);
+
+/* Install a block of gpiomux configurations in gpiomux. This is functionally
+ * identical to calling msm_gpiomux_write many times.
*/
-extern struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS];
+void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs);
/* Increment a gpio's reference count, possibly activating the line. */
int __must_check msm_gpiomux_get(unsigned gpio);
@@ -77,12 +133,16 @@ int __must_check msm_gpiomux_get(unsigned gpio);
/* Decrement a gpio's reference count, possibly suspending the line. */
int msm_gpiomux_put(unsigned gpio);
-/* Install a new configuration to the gpio line. To avoid overwriting
- * a configuration, leave the VALID bit out.
+/* Install a new setting in a gpio. To erase a slot, use NULL.
+ * The old setting that was overwritten can be passed back to the caller
+ * old_setting can be NULL if the caller is not interested in the previous
+ * setting
+ * If a previous setting was not available to return (NULL configuration)
+ * - the function returns 1
+ * else function returns 0
*/
-int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended);
+int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
+ struct gpiomux_setting *setting, struct gpiomux_setting *old_setting);
/* Architecture-internal function for use by the framework only.
* This function can assume the following:
@@ -92,8 +152,16 @@ int msm_gpiomux_write(unsigned gpio,
* This function is not for public consumption. External users
* should use msm_gpiomux_write.
*/
-void __msm_gpiomux_write(unsigned gpio, gpiomux_config_t val);
+void __msm_gpiomux_write(unsigned gpio, struct gpiomux_setting val);
#else
+static inline int msm_gpiomux_init(size_t ngpio)
+{
+ return -ENOSYS;
+}
+
+static inline void
+msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs) {}
+
static inline int __must_check msm_gpiomux_get(unsigned gpio)
{
return -ENOSYS;
@@ -105,8 +173,8 @@ static inline int msm_gpiomux_put(unsigned gpio)
}
static inline int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended)
+ enum msm_gpiomux_setting which, struct gpiomux_setting *setting,
+ struct gpiomux_setting *old_setting);
{
return -ENOSYS;
}
--
1.7.0.4
--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
next reply other threads:[~2010-12-13 17:02 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-12-13 17:02 Gregory Bean [this message]
2010-12-13 17:02 ` [PATCH 1/2] msm: gpiomux: Improve gpiomux interface design Gregory Bean
2010-12-13 17:02 ` [PATCH 2/2] msm: documentation: Update gpiomux documentation Gregory Bean
2010-12-13 17:02 ` Gregory Bean
2010-12-15 14:26 ` [PATCH 1/2] msm: gpiomux: Improve gpiomux interface design Daniel Walker
2010-12-15 14:26 ` Daniel Walker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1292259770-22972-1-git-send-email-gbean@codeaurora.org \
--to=gbean@codeaurora.org \
--cc=davidb@codeaurora.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-arm-msm@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.