@@ -227,7 +227,7 @@ static int __init nhk8815_eth_init(void)
int err;
err = gpio_request(gpio_nr, "eth_irq");
- if (!err) err = nmk_gpio_set_mode(gpio_nr, NMK_GPIO_ALT_GPIO);
+ if (!err) err = gpio_config(gpio_nr, GPIO_CONFIG_NMK_ALTF_GPIO, NULL);
if (!err) err = gpio_direction_input(gpio_nr);
if (err)
pr_err("Error %i in %s\n", err, __func__);
@@ -43,12 +43,16 @@ static struct platform_device nhk8815_i2c_dev1 = {
static int __init nhk8815_i2c_init(void)
{
- nmk_gpio_set_mode(nhk8815_i2c_data0.scl_pin, NMK_GPIO_ALT_GPIO);
- nmk_gpio_set_mode(nhk8815_i2c_data0.sda_pin, NMK_GPIO_ALT_GPIO);
+ gpio_config(nhk8815_i2c_data0.scl_pin,
+ GPIO_CONFIG_NMK_ALTF_GPIO, NULL);
+ gpio_config(nhk8815_i2c_data0.sda_pin,
+ GPIO_CONFIG_NMK_ALTF_GPIO, NULL);
platform_device_register(&nhk8815_i2c_dev0);
- nmk_gpio_set_mode(nhk8815_i2c_data1.scl_pin, NMK_GPIO_ALT_GPIO);
- nmk_gpio_set_mode(nhk8815_i2c_data1.sda_pin, NMK_GPIO_ALT_GPIO);
+ gpio_config(nhk8815_i2c_data1.scl_pin,
+ GPIO_CONFIG_NMK_ALTF_GPIO, NULL);
+ gpio_config(nhk8815_i2c_data1.sda_pin,
+ GPIO_CONFIG_NMK_ALTF_GPIO, NULL);
platform_device_register(&nhk8815_i2c_dev1);
return 0;
@@ -9,7 +9,6 @@
#include <linux/gpio/nomadik.h>
#include <asm/mach-types.h>
-#include <plat/pincfg.h>
#include <mach/hardware.h>
#include "pins-db8500.h"
@@ -9,7 +9,6 @@
#include <linux/mmc/host.h>
#include <linux/gpio/nomadik.h>
-#include <plat/pincfg.h>
#include <mach/db5500-regs.h>
#include <plat/ste_dma40.h>
@@ -8,6 +8,8 @@
#ifndef __MACH_DB5500_PINS_H
#define __MACH_DB5500_PINS_H
+#include <linux/gpio/nomadik.h>
+
#define GPIO0_GPIO PIN_CFG(0, GPIO)
#define GPIO0_SM_CS3n PIN_CFG(0, ALT_A)
@@ -8,6 +8,8 @@
#ifndef __MACH_PINS_DB8500_H
#define __MACH_PINS_DB8500_H
+#include <linux/gpio/nomadik.h>
+
/*
* TODO: Eventually encode all non-board specific pull up/down configuration
* here.
deleted file mode 100644
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * License terms: GNU General Public License, version 2
- * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
- *
- * Based on arch/arm/mach-pxa/include/mach/mfp.h:
- * Copyright (C) 2007 Marvell International Ltd.
- * eric miao <eric.miao@marvell.com>
- */
-
-#ifndef __PLAT_PINCFG_H
-#define __PLAT_PINCFG_H
-
-/*
- * pin configurations are represented by 32-bit integers:
- *
- * bit 0.. 8 - Pin Number (512 Pins Maximum)
- * bit 9..10 - Alternate Function Selection
- * bit 11..12 - Pull up/down state
- * bit 13 - Sleep mode behaviour
- * bit 14 - Direction
- * bit 15 - Value (if output)
- * bit 16..18 - SLPM pull up/down state
- * bit 19..20 - SLPM direction
- * bit 21..22 - SLPM Value (if output)
- *
- * to facilitate the definition, the following macros are provided
- *
- * PIN_CFG_DEFAULT - default config (0):
- * pull up/down = disabled
- * sleep mode = input/wakeup
- * direction = input
- * value = low
- * SLPM direction = same as normal
- * SLPM pull = same as normal
- * SLPM value = same as normal
- *
- * PIN_CFG - default config with alternate function
- */
-
-typedef unsigned long pin_cfg_t;
-
-#define PIN_NUM_MASK 0x1ff
-#define PIN_NUM(x) ((x) & PIN_NUM_MASK)
-
-#define PIN_ALT_SHIFT 9
-#define PIN_ALT_MASK (0x3 << PIN_ALT_SHIFT)
-#define PIN_ALT(x) (((x) & PIN_ALT_MASK) >> PIN_ALT_SHIFT)
-#define PIN_GPIO (NMK_GPIO_ALT_GPIO << PIN_ALT_SHIFT)
-#define PIN_ALT_A (NMK_GPIO_ALT_A << PIN_ALT_SHIFT)
-#define PIN_ALT_B (NMK_GPIO_ALT_B << PIN_ALT_SHIFT)
-#define PIN_ALT_C (NMK_GPIO_ALT_C << PIN_ALT_SHIFT)
-
-#define PIN_PULL_SHIFT 11
-#define PIN_PULL_MASK (0x3 << PIN_PULL_SHIFT)
-#define PIN_PULL(x) (((x) & PIN_PULL_MASK) >> PIN_PULL_SHIFT)
-#define PIN_PULL_NONE (NMK_GPIO_PULL_NONE << PIN_PULL_SHIFT)
-#define PIN_PULL_UP (NMK_GPIO_PULL_UP << PIN_PULL_SHIFT)
-#define PIN_PULL_DOWN (NMK_GPIO_PULL_DOWN << PIN_PULL_SHIFT)
-
-#define PIN_SLPM_SHIFT 13
-#define PIN_SLPM_MASK (0x1 << PIN_SLPM_SHIFT)
-#define PIN_SLPM(x) (((x) & PIN_SLPM_MASK) >> PIN_SLPM_SHIFT)
-#define PIN_SLPM_MAKE_INPUT (NMK_GPIO_SLPM_INPUT << PIN_SLPM_SHIFT)
-#define PIN_SLPM_NOCHANGE (NMK_GPIO_SLPM_NOCHANGE << PIN_SLPM_SHIFT)
-/* These two replace the above in DB8500v2+ */
-#define PIN_SLPM_WAKEUP_ENABLE (NMK_GPIO_SLPM_WAKEUP_ENABLE << PIN_SLPM_SHIFT)
-#define PIN_SLPM_WAKEUP_DISABLE (NMK_GPIO_SLPM_WAKEUP_DISABLE << PIN_SLPM_SHIFT)
-
-#define PIN_DIR_SHIFT 14
-#define PIN_DIR_MASK (0x1 << PIN_DIR_SHIFT)
-#define PIN_DIR(x) (((x) & PIN_DIR_MASK) >> PIN_DIR_SHIFT)
-#define PIN_DIR_INPUT (0 << PIN_DIR_SHIFT)
-#define PIN_DIR_OUTPUT (1 << PIN_DIR_SHIFT)
-
-#define PIN_VAL_SHIFT 15
-#define PIN_VAL_MASK (0x1 << PIN_VAL_SHIFT)
-#define PIN_VAL(x) (((x) & PIN_VAL_MASK) >> PIN_VAL_SHIFT)
-#define PIN_VAL_LOW (0 << PIN_VAL_SHIFT)
-#define PIN_VAL_HIGH (1 << PIN_VAL_SHIFT)
-
-#define PIN_SLPM_PULL_SHIFT 16
-#define PIN_SLPM_PULL_MASK (0x7 << PIN_SLPM_PULL_SHIFT)
-#define PIN_SLPM_PULL(x) \
- (((x) & PIN_SLPM_PULL_MASK) >> PIN_SLPM_PULL_SHIFT)
-#define PIN_SLPM_PULL_NONE \
- ((1 + NMK_GPIO_PULL_NONE) << PIN_SLPM_PULL_SHIFT)
-#define PIN_SLPM_PULL_UP \
- ((1 + NMK_GPIO_PULL_UP) << PIN_SLPM_PULL_SHIFT)
-#define PIN_SLPM_PULL_DOWN \
- ((1 + NMK_GPIO_PULL_DOWN) << PIN_SLPM_PULL_SHIFT)
-
-#define PIN_SLPM_DIR_SHIFT 19
-#define PIN_SLPM_DIR_MASK (0x3 << PIN_SLPM_DIR_SHIFT)
-#define PIN_SLPM_DIR(x) \
- (((x) & PIN_SLPM_DIR_MASK) >> PIN_SLPM_DIR_SHIFT)
-#define PIN_SLPM_DIR_INPUT ((1 + 0) << PIN_SLPM_DIR_SHIFT)
-#define PIN_SLPM_DIR_OUTPUT ((1 + 1) << PIN_SLPM_DIR_SHIFT)
-
-#define PIN_SLPM_VAL_SHIFT 21
-#define PIN_SLPM_VAL_MASK (0x3 << PIN_SLPM_VAL_SHIFT)
-#define PIN_SLPM_VAL(x) \
- (((x) & PIN_SLPM_VAL_MASK) >> PIN_SLPM_VAL_SHIFT)
-#define PIN_SLPM_VAL_LOW ((1 + 0) << PIN_SLPM_VAL_SHIFT)
-#define PIN_SLPM_VAL_HIGH ((1 + 1) << PIN_SLPM_VAL_SHIFT)
-
-/* Shortcuts. Use these instead of separate DIR, PULL, and VAL. */
-#define PIN_INPUT_PULLDOWN (PIN_DIR_INPUT | PIN_PULL_DOWN)
-#define PIN_INPUT_PULLUP (PIN_DIR_INPUT | PIN_PULL_UP)
-#define PIN_INPUT_NOPULL (PIN_DIR_INPUT | PIN_PULL_NONE)
-#define PIN_OUTPUT_LOW (PIN_DIR_OUTPUT | PIN_VAL_LOW)
-#define PIN_OUTPUT_HIGH (PIN_DIR_OUTPUT | PIN_VAL_HIGH)
-
-#define PIN_SLPM_INPUT_PULLDOWN (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_DOWN)
-#define PIN_SLPM_INPUT_PULLUP (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_UP)
-#define PIN_SLPM_INPUT_NOPULL (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_NONE)
-#define PIN_SLPM_OUTPUT_LOW (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_LOW)
-#define PIN_SLPM_OUTPUT_HIGH (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_HIGH)
-
-#define PIN_CFG_DEFAULT (0)
-
-#define PIN_CFG(num, alt) \
- (PIN_CFG_DEFAULT |\
- (PIN_NUM(num) | PIN_##alt))
-
-#define PIN_CFG_INPUT(num, alt, pull) \
- (PIN_CFG_DEFAULT |\
- (PIN_NUM(num) | PIN_##alt | PIN_INPUT_##pull))
-
-#define PIN_CFG_OUTPUT(num, alt, val) \
- (PIN_CFG_DEFAULT |\
- (PIN_NUM(num) | PIN_##alt | PIN_OUTPUT_##val))
-
-extern int nmk_config_pin(pin_cfg_t cfg, bool sleep);
-extern int nmk_config_pins(pin_cfg_t *cfgs, int num);
-extern int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num);
-
-#endif
@@ -25,7 +25,6 @@
#include <linux/slab.h>
#include <linux/gpio/nomadik.h>
-#include <plat/pincfg.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
@@ -39,6 +38,26 @@
#define NMK_GPIO_PER_CHIP 32
+/* Register in the logic block */
+#define NMK_GPIO_DAT 0x00
+#define NMK_GPIO_DATS 0x04
+#define NMK_GPIO_DATC 0x08
+#define NMK_GPIO_PDIS 0x0c
+#define NMK_GPIO_DIR 0x10
+#define NMK_GPIO_DIRS 0x14
+#define NMK_GPIO_DIRC 0x18
+#define NMK_GPIO_SLPC 0x1c
+#define NMK_GPIO_AFSLA 0x20
+#define NMK_GPIO_AFSLB 0x24
+
+#define NMK_GPIO_RIMSC 0x40
+#define NMK_GPIO_FIMSC 0x44
+#define NMK_GPIO_IS 0x48
+#define NMK_GPIO_IC 0x4c
+#define NMK_GPIO_RWIMSC 0x50
+#define NMK_GPIO_FWIMSC 0x54
+#define NMK_GPIO_WKS 0x58
+
struct nmk_gpio_chip {
struct gpio_chip chip;
void __iomem *addr;
@@ -60,6 +79,7 @@ struct nmk_gpio_chip {
u32 pull_up;
};
+/* FIXME: this is insane since it will create chips for the GPIO expanders! */
static struct nmk_gpio_chip *
nmk_gpio_chips[DIV_ROUND_UP(ARCH_NR_GPIOS, NMK_GPIO_PER_CHIP)];
@@ -76,43 +96,156 @@ static inline struct nmk_gpio_chip *to_nmk_chip(struct gpio_chip *chip)
}
static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip,
- unsigned offset, int gpio_mode)
+ unsigned offset, u16 altfunc)
{
u32 bit = 1 << offset;
u32 afunc, bfunc;
+ bool glitch = (altfunc == GPIO_CONFIG_NMK_ALTF_C);
+ u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
+ u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC);
+ unsigned long flags = 0;
+ int i;
+
+ /*
+ * If we're setting altfunc C by setting both AFSLA and AFSLB to 1,
+ * we may pass through an undesired state. In this case we take
+ * some extra care.
+ *
+ * Safe sequence used to switch IOs between GPIO and Alternate-C mode:
+ * - Save SLPM registers (since we have a shadow register in the
+ * nmk_chip we're using that as backup)
+ * - Set SLPM=0 for the IOs you want to switch and others to 1
+ * - Configure the GPIO registers for the IOs that are being switched
+ * - Set IOFORCE=1
+ * - Modify the AFLSA/B registers for the IOs that are being switched
+ * - Set IOFORCE=0
+ * - Restore SLPM registers
+ * - Any spurious wake up event during switch sequence to be ignored
+ * and cleared
+ *
+ * TODO: Do we REALLY need to save ALL slpm registers?? Isn't it
+ * enough to save the one for the port we're modifying, really?
+ */
+ if (glitch) {
+ u32 bit = BIT(offset);
+
+ /* Take the special sleep mode spinlock */
+ spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ break;
+ clk_enable(chip->clk);
+ writel(0xFFFFFFFFU, chip->addr + NMK_GPIO_SLPC);
+ }
+
+ /* Set "my" sleep bit to 0 */
+ writel(0xFFFFFFFFU & ~bit, nmk_chip->addr + NMK_GPIO_SLPC);
+ /* Prevent spurious wakeups */
+ writel(rwimsc & ~bit, nmk_chip->addr + NMK_GPIO_RWIMSC);
+ writel(fwimsc & ~bit, nmk_chip->addr + NMK_GPIO_FWIMSC);
+ if (nmk_chip->set_ioforce)
+ nmk_chip->set_ioforce(true);
+ }
+ /* Set desired altfunc */
afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit;
bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit;
- if (gpio_mode & NMK_GPIO_ALT_A)
+ if ((altfunc == GPIO_CONFIG_NMK_ALTF_A) ||
+ (altfunc == GPIO_CONFIG_NMK_ALTF_C))
afunc |= bit;
- if (gpio_mode & NMK_GPIO_ALT_B)
+ if ((altfunc == GPIO_CONFIG_NMK_ALTF_B) ||
+ (altfunc == GPIO_CONFIG_NMK_ALTF_C))
bfunc |= bit;
writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA);
writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB);
+
+ if (glitch) {
+ if (nmk_chip->set_ioforce)
+ nmk_chip->set_ioforce(false);
+ writel(rwimsc, nmk_chip->addr + NMK_GPIO_RWIMSC);
+ writel(fwimsc, nmk_chip->addr + NMK_GPIO_FWIMSC);
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ break;
+ writel(chip->slpm, chip->addr + NMK_GPIO_SLPC);
+ clk_disable(chip->clk);
+ }
+ spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ }
+}
+
+static int __nmk_gpio_get_mode(struct nmk_gpio_chip *nmk_chip,
+ unsigned offset, u16 *data)
+{
+ u32 afunc, bfunc, bit;
+
+ bit = 1 << offset;
+
+ afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit;
+ bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit;
+
+ if (afunc && bfunc)
+ *data = GPIO_CONFIG_NMK_ALTF_C;
+ else if (afunc)
+ *data = GPIO_CONFIG_NMK_ALTF_A;
+ else if (bfunc)
+ *data = GPIO_CONFIG_NMK_ALTF_B;
+ else
+ *data = GPIO_CONFIG_NMK_ALTF_GPIO;
+ return 0;
}
+/**
+ * __nmk_gpio_set_slpm() - configure the sleep mode of a pin
+ * @offset: pin number
+ * @enable: enable wakeup on the pin on DB8500v2, or force the pin to retain
+ * its value in sleep mode (not become an input) on DB8500v1 if true
+ *
+ * Sets the sleep mode of a pin. On DB8500v1, if @enable is
+ * true, the pin is NOT changed to an input (with pullup/down enabled) in
+ * sleep and deep sleep. If @enable is false, the pin remains in the state
+ * it was configured even when in sleep and deep sleep.
+ *
+ * On DB8500v2 onwards, this setting loses the previous meaning and instead
+ * indicates if wakeup detection is enabled on the pin. Note that
+ * enable_irq_wake() will automatically enable wakeup detection.
+ */
static void __nmk_gpio_set_slpm(struct nmk_gpio_chip *nmk_chip,
- unsigned offset, enum nmk_gpio_slpm mode)
+ unsigned offset, bool enable)
{
u32 bit = 1 << offset;
- u32 slpm;
- slpm = readl(nmk_chip->addr + NMK_GPIO_SLPC);
- if (mode == NMK_GPIO_SLPM_NOCHANGE)
- slpm |= bit;
+ /*
+ * NOTE: the meaning of "enable" is inverted, nominally every GPIO
+ * will wake up the system (or convert the pin to an input on DB8500v1)
+ */
+ if (enable)
+ nmk_chip->slpm &= ~bit;
else
- slpm &= ~bit;
- writel(slpm, nmk_chip->addr + NMK_GPIO_SLPC);
+ /*
+ * So setting this bit disables wakeups (or makes the pin
+ * retain its value on DB8500v1)
+ */
+ nmk_chip->slpm |= bit;
+
+ writel(nmk_chip->slpm, nmk_chip->addr + NMK_GPIO_SLPC);
}
static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip,
- unsigned offset, enum nmk_gpio_pull pull)
+ unsigned offset, u16 pullmode)
{
u32 bit = 1 << offset;
u32 pdis;
+ /* FIXME: use shadow registers instead of read-modify-write */
pdis = readl(nmk_chip->addr + NMK_GPIO_PDIS);
- if (pull == NMK_GPIO_PULL_NONE) {
+ if ((pullmode == GPIO_CONFIG_BIAS_UNKNOWN) ||
+ (pullmode == GPIO_CONFIG_BIAS_FLOAT)) {
pdis |= bit;
nmk_chip->pull_up &= ~bit;
} else {
@@ -121,10 +254,10 @@ static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip,
writel(pdis, nmk_chip->addr + NMK_GPIO_PDIS);
- if (pull == NMK_GPIO_PULL_UP) {
+ if (pullmode == GPIO_CONFIG_BIAS_PULL_UP) {
nmk_chip->pull_up |= bit;
writel(bit, nmk_chip->addr + NMK_GPIO_DATS);
- } else if (pull == NMK_GPIO_PULL_DOWN) {
+ } else if (pullmode == GPIO_CONFIG_BIAS_PULL_DOWN) {
nmk_chip->pull_up &= ~bit;
writel(bit, nmk_chip->addr + NMK_GPIO_DATC);
}
@@ -152,381 +285,169 @@ static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip,
__nmk_gpio_set_output(nmk_chip, offset, val);
}
-static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip,
- unsigned offset, int gpio_mode,
- bool glitch)
-{
- u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
- u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC);
-
- if (glitch && nmk_chip->set_ioforce) {
- u32 bit = BIT(offset);
-
- /* Prevent spurious wakeups */
- writel(rwimsc & ~bit, nmk_chip->addr + NMK_GPIO_RWIMSC);
- writel(fwimsc & ~bit, nmk_chip->addr + NMK_GPIO_FWIMSC);
-
- nmk_chip->set_ioforce(true);
- }
-
- __nmk_gpio_set_mode(nmk_chip, offset, gpio_mode);
-
- if (glitch && nmk_chip->set_ioforce) {
- nmk_chip->set_ioforce(false);
-
- writel(rwimsc, nmk_chip->addr + NMK_GPIO_RWIMSC);
- writel(fwimsc, nmk_chip->addr + NMK_GPIO_FWIMSC);
- }
-}
-static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
- pin_cfg_t cfg, bool sleep, unsigned int *slpmregs)
+static void __nmk_setup_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
+ struct nmk_gpio_pin_config *cfg)
{
static const char *afnames[] = {
- [NMK_GPIO_ALT_GPIO] = "GPIO",
- [NMK_GPIO_ALT_A] = "A",
- [NMK_GPIO_ALT_B] = "B",
- [NMK_GPIO_ALT_C] = "C"
- };
- static const char *pullnames[] = {
- [NMK_GPIO_PULL_NONE] = "none",
- [NMK_GPIO_PULL_UP] = "up",
- [NMK_GPIO_PULL_DOWN] = "down",
- [3] /* illegal */ = "??"
+ [GPIO_CONFIG_NMK_ALTF_GPIO] = "GPIO",
+ [GPIO_CONFIG_NMK_ALTF_A] = "A",
+ [GPIO_CONFIG_NMK_ALTF_B] = "B",
+ [GPIO_CONFIG_NMK_ALTF_C] = "C"
};
- static const char *slpmnames[] = {
- [NMK_GPIO_SLPM_INPUT] = "input/wakeup",
- [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup",
+ static const char *biasnames[] = {
+ [GPIO_CONFIG_BIAS_UNKNOWN] = "none",
+ [GPIO_CONFIG_BIAS_FLOAT] = "none",
+ [GPIO_CONFIG_BIAS_PULL_UP] = "up",
+ [GPIO_CONFIG_BIAS_PULL_DOWN] = "down",
};
- int pin = PIN_NUM(cfg);
- int pull = PIN_PULL(cfg);
- int af = PIN_ALT(cfg);
- int slpm = PIN_SLPM(cfg);
- int output = PIN_DIR(cfg);
- int val = PIN_VAL(cfg);
- bool glitch = af == NMK_GPIO_ALT_C;
-
- dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n",
- pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm],
- output ? "output " : "input",
- output ? (val ? "high" : "low") : "");
-
- if (sleep) {
- int slpm_pull = PIN_SLPM_PULL(cfg);
- int slpm_output = PIN_SLPM_DIR(cfg);
- int slpm_val = PIN_SLPM_VAL(cfg);
-
- af = NMK_GPIO_ALT_GPIO;
-
- /*
- * The SLPM_* values are normal values + 1 to allow zero to
- * mean "same as normal".
- */
- if (slpm_pull)
- pull = slpm_pull - 1;
- if (slpm_output)
- output = slpm_output - 1;
- if (slpm_val)
- val = slpm_val - 1;
-
- dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n",
- pin,
- slpm_pull ? pullnames[pull] : "same",
- slpm_output ? (output ? "output" : "input") : "same",
- slpm_val ? (val ? "high" : "low") : "same");
- }
+ dev_dbg(nmk_chip->chip.dev, "pin %d: af: %s, bias: %s, "
+ "slpm: %s (%s%s)\n",
+ cfg->pin, afnames[cfg->altfunc],
+ biasnames[cfg->bias_mode],
+ cfg->sleep_mode ? "input/wakeup" : "no-change/no-wakeup",
+ cfg->output ? "output " : "input",
+ cfg->output ? (cfg->outval ? "high" : "low") : "");
- if (output)
- __nmk_gpio_make_output(nmk_chip, offset, val);
+ if (cfg->output)
+ __nmk_gpio_make_output(nmk_chip, offset, cfg->outval);
else {
__nmk_gpio_make_input(nmk_chip, offset);
- __nmk_gpio_set_pull(nmk_chip, offset, pull);
+ __nmk_gpio_set_pull(nmk_chip, offset, cfg->bias_mode);
}
-
- /*
- * If we've backed up the SLPM registers (glitch workaround), modify
- * the backups since they will be restored.
- */
- if (slpmregs) {
- if (slpm == NMK_GPIO_SLPM_NOCHANGE)
- slpmregs[nmk_chip->bank] |= BIT(offset);
- else
- slpmregs[nmk_chip->bank] &= ~BIT(offset);
- } else
- __nmk_gpio_set_slpm(nmk_chip, offset, slpm);
-
- __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch);
+ __nmk_gpio_set_slpm(nmk_chip, offset, cfg->sleep_mode);
+ __nmk_gpio_set_mode(nmk_chip, offset, cfg->altfunc);
}
-/*
- * Safe sequence used to switch IOs between GPIO and Alternate-C mode:
- * - Save SLPM registers
- * - Set SLPM=0 for the IOs you want to switch and others to 1
- * - Configure the GPIO registers for the IOs that are being switched
- * - Set IOFORCE=1
- * - Modify the AFLSA/B registers for the IOs that are being switched
- * - Set IOFORCE=0
- * - Restore SLPM registers
- * - Any spurious wake up event during switch sequence to be ignored and
- * cleared
+/**
+ * This translates the old pin config parameter type into a config
+ * struct and passes to the new API
*/
-static void nmk_gpio_glitch_slpm_init(unsigned int *slpm)
-{
- int i;
-
- for (i = 0; i < NUM_BANKS; i++) {
- struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
- unsigned int temp = slpm[i];
-
- if (!chip)
- break;
-
- clk_enable(chip->clk);
-
- slpm[i] = readl(chip->addr + NMK_GPIO_SLPC);
- writel(temp, chip->addr + NMK_GPIO_SLPC);
- }
-}
-
-static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm)
-{
- int i;
-
- for (i = 0; i < NUM_BANKS; i++) {
- struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
-
- if (!chip)
- break;
-
- writel(slpm[i], chip->addr + NMK_GPIO_SLPC);
-
- clk_disable(chip->clk);
- }
-}
-
-static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep)
+static void __nmk_setup_pin_legacy(struct nmk_gpio_chip *nmk_chip,
+ unsigned offset,
+ pin_cfg_t pincfg)
{
- static unsigned int slpm[NUM_BANKS];
- unsigned long flags;
- bool glitch = false;
- int ret = 0;
- int i;
-
- for (i = 0; i < num; i++) {
- if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) {
- glitch = true;
- break;
- }
+ struct nmk_gpio_pin_config cfg;
+
+ cfg.pin = PIN_NUM(pincfg);
+
+ switch (PIN_ALT(pincfg)) {
+ case NMK_GPIO_ALT_GPIO:
+ cfg.altfunc = GPIO_CONFIG_NMK_ALTF_GPIO;
+ break;
+ case NMK_GPIO_ALT_A:
+ cfg.altfunc = GPIO_CONFIG_NMK_ALTF_A;
+ break;
+ case NMK_GPIO_ALT_B:
+ cfg.altfunc = GPIO_CONFIG_NMK_ALTF_B;
+ break;
+ case NMK_GPIO_ALT_C:
+ cfg.altfunc = GPIO_CONFIG_NMK_ALTF_C;
+ break;
+ default:
+ cfg.altfunc = GPIO_CONFIG_NMK_ALTF_GPIO;
+ break;
}
- spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
-
- if (glitch) {
- memset(slpm, 0xff, sizeof(slpm));
-
- for (i = 0; i < num; i++) {
- int pin = PIN_NUM(cfgs[i]);
- int offset = pin % NMK_GPIO_PER_CHIP;
-
- if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C)
- slpm[pin / NMK_GPIO_PER_CHIP] &= ~BIT(offset);
- }
-
- nmk_gpio_glitch_slpm_init(slpm);
+ if (PIN_DIR(pincfg))
+ cfg.output = true;
+
+ if (PIN_VAL(pincfg))
+ cfg.outval = 1;
+
+ switch (PIN_PULL(pincfg)) {
+ case NMK_GPIO_PULL_NONE:
+ cfg.bias_mode = GPIO_CONFIG_BIAS_FLOAT;
+ break;
+ case NMK_GPIO_PULL_UP:
+ cfg.bias_mode = GPIO_CONFIG_BIAS_PULL_UP;
+ break;
+ case NMK_GPIO_PULL_DOWN:
+ cfg.bias_mode = GPIO_CONFIG_BIAS_PULL_DOWN;
+ break;
+ default:
+ cfg.bias_mode = GPIO_CONFIG_BIAS_FLOAT;
+ break;
}
- for (i = 0; i < num; i++) {
- struct nmk_gpio_chip *nmk_chip;
- int pin = PIN_NUM(cfgs[i]);
-
- nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(pin));
- if (!nmk_chip) {
- ret = -EINVAL;
- break;
- }
-
- clk_enable(nmk_chip->clk);
- spin_lock(&nmk_chip->lock);
- __nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base,
- cfgs[i], sleep, glitch ? slpm : NULL);
- spin_unlock(&nmk_chip->lock);
- clk_disable(nmk_chip->clk);
+ switch (PIN_SLPM(pincfg)) {
+ case NMK_GPIO_SLPM_WAKEUP_ENABLE:
+ /* Also NMK_GPIO_SLPM_INPUT */
+ break;
+ case NMK_GPIO_SLPM_WAKEUP_DISABLE:
+ /* Also NMK_GPIO_SLPM_NOCHANGE */
+ cfg.sleep_mode = true;
+ default:
+ break;
}
- if (glitch)
- nmk_gpio_glitch_slpm_restore(slpm);
-
- spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
-
- return ret;
-}
-
-/**
- * nmk_config_pin - configure a pin's mux attributes
- * @cfg: pin confguration
- *
- * Configures a pin's mode (alternate function or GPIO), its pull up status,
- * and its sleep mode based on the specified configuration. The @cfg is
- * usually one of the SoC specific macros defined in mach/<soc>-pins.h. These
- * are constructed using, and can be further enhanced with, the macros in
- * plat/pincfg.h.
- *
- * If a pin's mode is set to GPIO, it is configured as an input to avoid
- * side-effects. The gpio can be manipulated later using standard GPIO API
- * calls.
- */
-int nmk_config_pin(pin_cfg_t cfg, bool sleep)
-{
- return __nmk_config_pins(&cfg, 1, sleep);
-}
-EXPORT_SYMBOL(nmk_config_pin);
-
-/**
- * nmk_config_pins - configure several pins at once
- * @cfgs: array of pin configurations
- * @num: number of elments in the array
- *
- * Configures several pins using nmk_config_pin(). Refer to that function for
- * further information.
- */
-int nmk_config_pins(pin_cfg_t *cfgs, int num)
-{
- return __nmk_config_pins(cfgs, num, false);
-}
-EXPORT_SYMBOL(nmk_config_pins);
-
-int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num)
-{
- return __nmk_config_pins(cfgs, num, true);
-}
-EXPORT_SYMBOL(nmk_config_pins_sleep);
-
-/**
- * nmk_gpio_set_slpm() - configure the sleep mode of a pin
- * @gpio: pin number
- * @mode: NMK_GPIO_SLPM_INPUT or NMK_GPIO_SLPM_NOCHANGE,
- *
- * Sets the sleep mode of a pin. If @mode is NMK_GPIO_SLPM_INPUT, the pin is
- * changed to an input (with pullup/down enabled) in sleep and deep sleep. If
- * @mode is NMK_GPIO_SLPM_NOCHANGE, the pin remains in the state it was
- * configured even when in sleep and deep sleep.
- *
- * On DB8500v2 onwards, this setting loses the previous meaning and instead
- * indicates if wakeup detection is enabled on the pin. Note that
- * enable_irq_wake() will automatically enable wakeup detection.
- */
-int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode)
-{
- struct nmk_gpio_chip *nmk_chip;
- unsigned long flags;
-
- nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
- if (!nmk_chip)
- return -EINVAL;
-
- clk_enable(nmk_chip->clk);
- spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
- spin_lock(&nmk_chip->lock);
-
- __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, mode);
-
- spin_unlock(&nmk_chip->lock);
- spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
- clk_disable(nmk_chip->clk);
-
- return 0;
+ __nmk_setup_pin(nmk_chip, offset, &cfg);
}
-/**
- * nmk_gpio_set_pull() - enable/disable pull up/down on a gpio
- * @gpio: pin number
- * @pull: one of NMK_GPIO_PULL_DOWN, NMK_GPIO_PULL_UP, and NMK_GPIO_PULL_NONE
- *
- * Enables/disables pull up/down on a specified pin. This only takes effect if
- * the pin is configured as an input (either explicitly or by the alternate
- * function).
- *
- * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is
- * configured as an input. Otherwise, due to the way the controller registers
- * work, this function will change the value output on the pin.
- */
-int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull)
+static int nmk_gpio_config(struct gpio_chip *chip, unsigned offset,
+ u16 param, unsigned long *data)
{
- struct nmk_gpio_chip *nmk_chip;
+ struct nmk_gpio_chip *nmk_chip = to_nmk_chip(chip);
unsigned long flags;
- nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
if (!nmk_chip)
return -EINVAL;
clk_enable(nmk_chip->clk);
- spin_lock_irqsave(&nmk_chip->lock, flags);
- __nmk_gpio_set_pull(nmk_chip, gpio - nmk_chip->chip.base, pull);
- spin_unlock_irqrestore(&nmk_chip->lock, flags);
- clk_disable(nmk_chip->clk);
-
- return 0;
-}
-
-/* Mode functions */
-/**
- * nmk_gpio_set_mode() - set the mux mode of a gpio pin
- * @gpio: pin number
- * @gpio_mode: one of NMK_GPIO_ALT_GPIO, NMK_GPIO_ALT_A,
- * NMK_GPIO_ALT_B, and NMK_GPIO_ALT_C
- *
- * Sets the mode of the specified pin to one of the alternate functions or
- * plain GPIO.
- */
-int nmk_gpio_set_mode(int gpio, int gpio_mode)
-{
- struct nmk_gpio_chip *nmk_chip;
- unsigned long flags;
- nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
- if (!nmk_chip)
+ switch (param) {
+ case GPIO_CONFIG_BIAS_UNKNOWN:
+ case GPIO_CONFIG_BIAS_FLOAT:
+ case GPIO_CONFIG_BIAS_PULL_UP:
+ case GPIO_CONFIG_BIAS_PULL_DOWN:
+ __nmk_gpio_set_pull(nmk_chip, offset, param);
+ break;
+ case GPIO_CONFIG_NMK_ALTF_GPIO:
+ case GPIO_CONFIG_NMK_ALTF_A:
+ case GPIO_CONFIG_NMK_ALTF_B:
+ case GPIO_CONFIG_NMK_ALTF_C:
+ __nmk_gpio_set_mode(nmk_chip, offset, param);
+ break;
+ case GPIO_CONFIG_NMK_GET_ALTF:
+ __nmk_gpio_get_mode(nmk_chip, offset, (u16 *) data);
+ break;
+ case GPIO_CONFIG_NMK_SLPM_INPUT:
+ case GPIO_CONFIG_NMK_WAKEUP_ENABLE:
+ spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+ spin_lock(&nmk_chip->lock);
+ __nmk_gpio_set_slpm(nmk_chip, offset, true);
+ spin_unlock(&nmk_chip->lock);
+ spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ break;
+ case GPIO_CONFIG_NMK_SLPM_NOCHANGE:
+ case GPIO_CONFIG_NMK_WAKEUP_DISABLE:
+ spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+ spin_lock(&nmk_chip->lock);
+ __nmk_gpio_set_slpm(nmk_chip, offset, false);
+ spin_unlock(&nmk_chip->lock);
+ spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+ break;
+ case GPIO_CONFIG_NMK_SETUP_PIN:
+ __nmk_setup_pin(nmk_chip, offset,
+ (struct nmk_gpio_pin_config *) data);
+ break;
+ case GPIO_CONFIG_NMK_SETUP_PIN_LEGACY:
+ __nmk_setup_pin_legacy(nmk_chip, offset, (pin_cfg_t) *data);
+ break;
+ default:
+ dev_err(nmk_chip->chip.dev,
+ "illegal configuration requested\n");
+ clk_disable(nmk_chip->clk);
return -EINVAL;
+ };
- clk_enable(nmk_chip->clk);
- spin_lock_irqsave(&nmk_chip->lock, flags);
- __nmk_gpio_set_mode(nmk_chip, gpio - nmk_chip->chip.base, gpio_mode);
- spin_unlock_irqrestore(&nmk_chip->lock, flags);
clk_disable(nmk_chip->clk);
return 0;
}
-EXPORT_SYMBOL(nmk_gpio_set_mode);
-
-int nmk_gpio_get_mode(int gpio)
-{
- struct nmk_gpio_chip *nmk_chip;
- u32 afunc, bfunc, bit;
-
- nmk_chip = irq_get_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
- if (!nmk_chip)
- return -EINVAL;
-
- bit = 1 << (gpio - nmk_chip->chip.base);
-
- clk_enable(nmk_chip->clk);
-
- afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit;
- bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit;
-
- clk_disable(nmk_chip->clk);
-
- return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0);
-}
-EXPORT_SYMBOL(nmk_gpio_get_mode);
-static int nmk_gpio_config(struct gpio_chip *chip, unsigned offset,
- u16 param, unsigned long *data)
-{
- struct nmk_gpio_chip *nmk_chip = to_nmk_chip(chip);
- return 0;
-}
/* IRQ functions */
static inline int nmk_gpio_get_bitmask(int gpio)
@@ -585,11 +506,8 @@ static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip,
static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip,
int gpio, bool on)
{
- if (nmk_chip->sleepmode) {
- __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base,
- on ? NMK_GPIO_SLPM_WAKEUP_ENABLE
- : NMK_GPIO_SLPM_WAKEUP_DISABLE);
- }
+ if (nmk_chip->sleepmode)
+ __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, on);
__nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on);
}
@@ -650,6 +568,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
spin_lock(&nmk_chip->lock);
+ /* Make sure we wake up on this pin */
if (irqd_irq_disabled(d))
__nmk_gpio_set_wake(nmk_chip, gpio, on);
@@ -884,35 +803,47 @@ static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
- int mode;
+ u16 mode;
unsigned i;
unsigned gpio = chip->base;
int is_out;
struct nmk_gpio_chip *nmk_chip = to_nmk_chip(chip);
- const char *modes[] = {
- [NMK_GPIO_ALT_GPIO] = "gpio",
- [NMK_GPIO_ALT_A] = "altA",
- [NMK_GPIO_ALT_B] = "altB",
- [NMK_GPIO_ALT_C] = "altC",
- };
clk_enable(nmk_chip->clk);
for (i = 0; i < chip->ngpio; i++, gpio++) {
const char *label = gpiochip_is_requested(chip, i);
+ const char *modetxt;
bool pull;
u32 bit = 1 << i;
is_out = readl(nmk_chip->addr + NMK_GPIO_DIR) & bit;
pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & bit);
- mode = nmk_gpio_get_mode(gpio);
+ __nmk_gpio_get_mode(nmk_chip, gpio, &mode);
+ switch (mode) {
+ case GPIO_CONFIG_NMK_ALTF_GPIO:
+ modetxt = "gpio";
+ break;
+ case GPIO_CONFIG_NMK_ALTF_A:
+ modetxt = "altA";
+ break;
+ case GPIO_CONFIG_NMK_ALTF_B:
+ modetxt = "altB";
+ break;
+ case GPIO_CONFIG_NMK_ALTF_C:
+ modetxt = "altC";
+ break;
+ default:
+ modetxt = "unknown";
+ break;
+ }
seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
gpio, label ?: "(none)",
is_out ? "out" : "in ",
chip->get
? (chip->get(chip, i) ? "hi" : "lo")
: "? ",
- (mode < 0) ? "unknown" : modes[mode],
+ (mode < 0) ? "unknown" : modetxt,
pull ? "pull" : "none");
if (label && !is_out) {
@@ -951,7 +882,7 @@ static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
#endif
/* This structure is replicated for each GPIO block allocated at probe time */
-static struct gpio_chip nmk_gpio_template = {
+static struct gpio_chip nmk_gpio_chip = {
.direction_input = nmk_gpio_make_input,
.get = nmk_gpio_get_input,
.direction_output = nmk_gpio_make_output,
@@ -962,6 +893,9 @@ static struct gpio_chip nmk_gpio_template = {
.can_sleep = 0,
};
+/**
+ * nmk_gpio_clocks_enable() - enable the clocks on all GPIO blocks
+ */
void nmk_gpio_clocks_enable(void)
{
int i;
@@ -976,6 +910,9 @@ void nmk_gpio_clocks_enable(void)
}
}
+/**
+ * nmk_gpio_clocks_disable() - disable the clocks on all GPIO blocks
+ */
void nmk_gpio_clocks_disable(void)
{
int i;
@@ -1127,7 +1064,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
nmk_chip->bank = dev->id;
nmk_chip->clk = clk;
nmk_chip->addr = io_p2v(res->start);
- nmk_chip->chip = nmk_gpio_template;
+ nmk_chip->chip = nmk_gpio_chip;
nmk_chip->parent_irq = irq;
nmk_chip->secondary_parent_irq = secondary_irq;
nmk_chip->get_secondary_status = pdata->get_secondary_status;
@@ -4,6 +4,13 @@
* Copyright (C) 2008 STMicroelectronics
* Author: Prafulla WADASKAR <prafulla.wadaskar@st.com>
* Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ * Authot: Linus Walleij <linus.wallej@linaro.org>
+ *
+ * Pin config API based on arch/arm/mach-pxa/include/mach/mfp.h:
+ * Copyright (C) 2007 Marvell International Ltd.
+ * eric miao <eric.miao@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -13,31 +20,90 @@
#define __GPIO_NOMADIK_H
#include <linux/gpio.h>
+#include <asm-generic/gpio.h>
/*
- * "nmk_gpio" and "NMK_GPIO" stand for "Nomadik GPIO", leaving
- * the "gpio" namespace for generic and cross-machine functions
+ * Custom pin configuration options
*/
+#define GPIO_CONFIG_NMK_ALTF_GPIO (GPIO_CONFIG_CUSTOM_BASE+0)
+#define GPIO_CONFIG_NMK_ALTF_A (GPIO_CONFIG_CUSTOM_BASE+1)
+#define GPIO_CONFIG_NMK_ALTF_B (GPIO_CONFIG_CUSTOM_BASE+2)
+#define GPIO_CONFIG_NMK_ALTF_C (GPIO_CONFIG_CUSTOM_BASE+3)
+#define GPIO_CONFIG_NMK_GET_ALTF (GPIO_CONFIG_CUSTOM_BASE+4)
+#define GPIO_CONFIG_NMK_SLPM_INPUT (GPIO_CONFIG_CUSTOM_BASE+5)
+#define GPIO_CONFIG_NMK_WAKEUP_ENABLE (GPIO_CONFIG_CUSTOM_BASE+6)
+#define GPIO_CONFIG_NMK_SLPM_NOCHANGE (GPIO_CONFIG_CUSTOM_BASE+7)
+#define GPIO_CONFIG_NMK_WAKEUP_DISABLE (GPIO_CONFIG_CUSTOM_BASE+8)
+#define GPIO_CONFIG_NMK_SETUP_PIN (GPIO_CONFIG_CUSTOM_BASE+9)
+#define GPIO_CONFIG_NMK_SETUP_PIN_LEGACY (GPIO_CONFIG_CUSTOM_BASE+10)
-/* Register in the logic block */
-#define NMK_GPIO_DAT 0x00
-#define NMK_GPIO_DATS 0x04
-#define NMK_GPIO_DATC 0x08
-#define NMK_GPIO_PDIS 0x0c
-#define NMK_GPIO_DIR 0x10
-#define NMK_GPIO_DIRS 0x14
-#define NMK_GPIO_DIRC 0x18
-#define NMK_GPIO_SLPC 0x1c
-#define NMK_GPIO_AFSLA 0x20
-#define NMK_GPIO_AFSLB 0x24
-
-#define NMK_GPIO_RIMSC 0x40
-#define NMK_GPIO_FIMSC 0x44
-#define NMK_GPIO_IS 0x48
-#define NMK_GPIO_IC 0x4c
-#define NMK_GPIO_RWIMSC 0x50
-#define NMK_GPIO_FWIMSC 0x54
-#define NMK_GPIO_WKS 0x58
+/*
+ * Platform data to register a block: only the initial gpio/irq number.
+ */
+struct nmk_gpio_platform_data {
+ char *name;
+ int first_gpio;
+ int first_irq;
+ int num_gpio;
+ u32 (*get_secondary_status)(unsigned int bank);
+ void (*set_ioforce)(bool enable);
+ bool supports_sleepmode;
+};
+
+/**
+ * nmk_gpio_pin_config - configuration data for a single GPIO pin
+ * @altfunc: the alternate function setting for the pin
+ * @bias_mode: pull up/down/none (float) bias setting for the pin
+ * @output: if this pin is to be configured for output or not
+ * @outval: if configured for output, the default output value (0 or 1)
+ * @sleep_mode: if true, this pin will wake up the system in sleep mode
+ * (DB8500v2) or force the pin to retain its value and not become an
+ * input (as is the default on DB8500v1, pull up/down is retained)
+ */
+struct nmk_gpio_pin_config {
+ int pin;
+ u16 altfunc;
+ u16 bias_mode;
+ bool output;
+ int outval;
+ bool sleep_mode;
+};
+
+/* A horde of exported functions */
+extern void nmk_gpio_wakeups_suspend(void);
+extern void nmk_gpio_wakeups_resume(void);
+extern void nmk_gpio_clocks_enable(void);
+extern void nmk_gpio_clocks_disable(void);
+extern void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up);
+
+/*
+ * pin configurations are represented by 32-bit integers:
+ *
+ * bit 0.. 8 - Pin Number (512 Pins Maximum)
+ * bit 9..10 - Alternate Function Selection
+ * bit 11..12 - Pull up/down state
+ * bit 13 - Sleep mode behaviour
+ * bit 14 - Direction
+ * bit 15 - Value (if output)
+ * bit 16..18 - SLPM pull up/down state
+ * bit 19..20 - SLPM direction
+ * bit 21..22 - SLPM Value (if output)
+ *
+ * to facilitate the definition, the following macros are provided
+ *
+ * PIN_CFG_DEFAULT - default config (0):
+ * pull up/down = disabled
+ * sleep mode = input/wakeup
+ * direction = input
+ * value = low
+ * SLPM direction = same as normal
+ * SLPM pull = same as normal
+ * SLPM value = same as normal
+ *
+ * PIN_CFG - default config with alternate function
+ */
+
+typedef unsigned long pin_cfg_t;
/* Alternate functions: function C is set in hw by setting both A and B */
#define NMK_GPIO_ALT_GPIO 0
@@ -60,30 +126,166 @@ enum nmk_gpio_slpm {
NMK_GPIO_SLPM_WAKEUP_DISABLE = NMK_GPIO_SLPM_NOCHANGE,
};
-extern int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode);
-extern int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull);
-extern int nmk_gpio_set_mode(int gpio, int gpio_mode);
-extern int nmk_gpio_get_mode(int gpio);
+#define PIN_NUM_MASK 0x1ff
+#define PIN_NUM(x) ((x) & PIN_NUM_MASK)
-extern void nmk_gpio_wakeups_suspend(void);
-extern void nmk_gpio_wakeups_resume(void);
+#define PIN_ALT_SHIFT 9
+#define PIN_ALT_MASK (0x3 << PIN_ALT_SHIFT)
+#define PIN_ALT(x) (((x) & PIN_ALT_MASK) >> PIN_ALT_SHIFT)
+#define PIN_GPIO (NMK_GPIO_ALT_GPIO << PIN_ALT_SHIFT)
+#define PIN_ALT_A (NMK_GPIO_ALT_A << PIN_ALT_SHIFT)
+#define PIN_ALT_B (NMK_GPIO_ALT_B << PIN_ALT_SHIFT)
+#define PIN_ALT_C (NMK_GPIO_ALT_C << PIN_ALT_SHIFT)
-extern void nmk_gpio_clocks_enable(void);
-extern void nmk_gpio_clocks_disable(void);
+#define PIN_PULL_SHIFT 11
+#define PIN_PULL_MASK (0x3 << PIN_PULL_SHIFT)
+#define PIN_PULL(x) (((x) & PIN_PULL_MASK) >> PIN_PULL_SHIFT)
+#define PIN_PULL_NONE (NMK_GPIO_PULL_NONE << PIN_PULL_SHIFT)
+#define PIN_PULL_UP (NMK_GPIO_PULL_UP << PIN_PULL_SHIFT)
+#define PIN_PULL_DOWN (NMK_GPIO_PULL_DOWN << PIN_PULL_SHIFT)
-extern void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up);
+#define PIN_SLPM_SHIFT 13
+#define PIN_SLPM_MASK (0x1 << PIN_SLPM_SHIFT)
+#define PIN_SLPM(x) (((x) & PIN_SLPM_MASK) >> PIN_SLPM_SHIFT)
+#define PIN_SLPM_MAKE_INPUT (NMK_GPIO_SLPM_INPUT << PIN_SLPM_SHIFT)
+#define PIN_SLPM_NOCHANGE (NMK_GPIO_SLPM_NOCHANGE << PIN_SLPM_SHIFT)
+/* These two replace the above in DB8500v2+ */
+#define PIN_SLPM_WAKEUP_ENABLE (NMK_GPIO_SLPM_WAKEUP_ENABLE << PIN_SLPM_SHIFT)
+#define PIN_SLPM_WAKEUP_DISABLE (NMK_GPIO_SLPM_WAKEUP_DISABLE << PIN_SLPM_SHIFT)
+
+#define PIN_DIR_SHIFT 14
+#define PIN_DIR_MASK (0x1 << PIN_DIR_SHIFT)
+#define PIN_DIR(x) (((x) & PIN_DIR_MASK) >> PIN_DIR_SHIFT)
+#define PIN_DIR_INPUT (0 << PIN_DIR_SHIFT)
+#define PIN_DIR_OUTPUT (1 << PIN_DIR_SHIFT)
+
+#define PIN_VAL_SHIFT 15
+#define PIN_VAL_MASK (0x1 << PIN_VAL_SHIFT)
+#define PIN_VAL(x) (((x) & PIN_VAL_MASK) >> PIN_VAL_SHIFT)
+#define PIN_VAL_LOW (0 << PIN_VAL_SHIFT)
+#define PIN_VAL_HIGH (1 << PIN_VAL_SHIFT)
+
+#define PIN_SLPM_PULL_SHIFT 16
+#define PIN_SLPM_PULL_MASK (0x7 << PIN_SLPM_PULL_SHIFT)
+#define PIN_SLPM_PULL(x) \
+ (((x) & PIN_SLPM_PULL_MASK) >> PIN_SLPM_PULL_SHIFT)
+#define PIN_SLPM_PULL_NONE \
+ ((1 + NMK_GPIO_PULL_NONE) << PIN_SLPM_PULL_SHIFT)
+#define PIN_SLPM_PULL_UP \
+ ((1 + NMK_GPIO_PULL_UP) << PIN_SLPM_PULL_SHIFT)
+#define PIN_SLPM_PULL_DOWN \
+ ((1 + NMK_GPIO_PULL_DOWN) << PIN_SLPM_PULL_SHIFT)
+
+#define PIN_SLPM_DIR_SHIFT 19
+#define PIN_SLPM_DIR_MASK (0x3 << PIN_SLPM_DIR_SHIFT)
+#define PIN_SLPM_DIR(x) \
+ (((x) & PIN_SLPM_DIR_MASK) >> PIN_SLPM_DIR_SHIFT)
+#define PIN_SLPM_DIR_INPUT ((1 + 0) << PIN_SLPM_DIR_SHIFT)
+#define PIN_SLPM_DIR_OUTPUT ((1 + 1) << PIN_SLPM_DIR_SHIFT)
+
+#define PIN_SLPM_VAL_SHIFT 21
+#define PIN_SLPM_VAL_MASK (0x3 << PIN_SLPM_VAL_SHIFT)
+#define PIN_SLPM_VAL(x) \
+ (((x) & PIN_SLPM_VAL_MASK) >> PIN_SLPM_VAL_SHIFT)
+#define PIN_SLPM_VAL_LOW ((1 + 0) << PIN_SLPM_VAL_SHIFT)
+#define PIN_SLPM_VAL_HIGH ((1 + 1) << PIN_SLPM_VAL_SHIFT)
+
+/* Shortcuts. Use these instead of separate DIR, PULL, and VAL. */
+#define PIN_INPUT_PULLDOWN (PIN_DIR_INPUT | PIN_PULL_DOWN)
+#define PIN_INPUT_PULLUP (PIN_DIR_INPUT | PIN_PULL_UP)
+#define PIN_INPUT_NOPULL (PIN_DIR_INPUT | PIN_PULL_NONE)
+#define PIN_OUTPUT_LOW (PIN_DIR_OUTPUT | PIN_VAL_LOW)
+#define PIN_OUTPUT_HIGH (PIN_DIR_OUTPUT | PIN_VAL_HIGH)
+
+#define PIN_SLPM_INPUT_PULLDOWN (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_DOWN)
+#define PIN_SLPM_INPUT_PULLUP (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_UP)
+#define PIN_SLPM_INPUT_NOPULL (PIN_SLPM_DIR_INPUT | PIN_SLPM_PULL_NONE)
+#define PIN_SLPM_OUTPUT_LOW (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_LOW)
+#define PIN_SLPM_OUTPUT_HIGH (PIN_SLPM_DIR_OUTPUT | PIN_SLPM_VAL_HIGH)
+
+#define PIN_CFG_DEFAULT (0)
+
+#define PIN_CFG(num, alt) \
+ (PIN_CFG_DEFAULT |\
+ (PIN_NUM(num) | PIN_##alt))
+
+#define PIN_CFG_INPUT(num, alt, pull) \
+ (PIN_CFG_DEFAULT |\
+ (PIN_NUM(num) | PIN_##alt | PIN_INPUT_##pull))
+
+#define PIN_CFG_OUTPUT(num, alt, val) \
+ (PIN_CFG_DEFAULT |\
+ (PIN_NUM(num) | PIN_##alt | PIN_OUTPUT_##val))
/*
- * Platform data to register a block: only the initial gpio/irq number.
+ * We want to keep this function outside the driver since it is only using the
+ * externally visible gpio_config() function and merely twisting bits around
+ * for the legacy pin configuration API.
*/
-struct nmk_gpio_platform_data {
- char *name;
- int first_gpio;
- int first_irq;
- int num_gpio;
- u32 (*get_secondary_status)(unsigned int bank);
- void (*set_ioforce)(bool enable);
- bool supports_sleepmode;
-};
+static inline int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep)
+{
+ pin_cfg_t cfg;
+ int ret;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ cfg = cfgs[i];
+
+ if (sleep) {
+ /*
+ * We reconfigure for sleep mode, mask off the
+ * normal configs and replace them with sleep mode
+ * configs and pass into the same legacy config
+ * function, simply. Zero values mean "same as in
+ * normal mode".
+ */
+ int slpm_pull = PIN_SLPM_PULL(cfg);
+ int slpm_output = PIN_SLPM_DIR(cfg);
+ int slpm_val = PIN_SLPM_VAL(cfg);
+
+ /* Override bias mode */
+ if (slpm_pull) {
+ cfg &= ~PIN_PULL_MASK;
+ cfg |= (slpm_pull - 1) << PIN_PULL_SHIFT;
+ }
+
+ /* Override in/output mode */
+ if (slpm_output) {
+ cfg &= ~PIN_DIR_MASK;
+ cfg |= (slpm_output - 1) << PIN_DIR_SHIFT;
+ }
+
+ /* Override output value */
+ if (slpm_val) {
+ cfg &= ~PIN_VAL_MASK;
+ cfg |= (slpm_output - 1) << PIN_VAL_SHIFT;
+ }
+ }
+
+ ret = gpio_config(PIN_NUM(cfg),
+ GPIO_CONFIG_NMK_SETUP_PIN_LEGACY,
+ (unsigned long *) &cfg);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+extern int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep);
+
+static inline int nmk_config_pin(pin_cfg_t cfg, bool sleep)
+{
+ return __nmk_config_pins(&cfg, 1, sleep);
+}
+
+static inline int nmk_config_pins(pin_cfg_t *cfgs, int num)
+{
+ return __nmk_config_pins(cfgs, num, false);
+}
+
+static inline int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num)
+{
+ return __nmk_config_pins(cfgs, num, true);
+}
#endif