summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2012-05-03 15:58:12 +0200
committerLinus Walleij <linus.walleij@linaro.org>2012-05-11 11:14:16 +0200
commitd41af627638a0e1e964d546e385b20650e769ce5 (patch)
tree8d720bf186fe6db5574016a108c1adada1b38751 /drivers/pinctrl
parentpinctrl/nomadik: implement pin multiplexing (diff)
downloadlinux-d41af627638a0e1e964d546e385b20650e769ce5.tar.xz
linux-d41af627638a0e1e964d546e385b20650e769ce5.zip
pinctrl/nomadik: implement pin configuration
This implements the pin configuration interface for the Nomadik pin controller. As part of the exercise we add a bit in the pin_cfg_t for the Nomadik pinctrl driver that indicates if the pin should be forced into GPIO mode. This is not done to go behind the back of the GPIO subsystem, but to ensure that default modes can be set by hogs on boot and system suspend/resume states. It was used implicitly by the old code defining all config settings and modes in a single config word but we now have a split between pinmux and pinconf leading to the need to have this. We also add a bit for explicitly setting sleepmode of the pin. This was previously handled by custom calls with the _sleep() suffix, but we now have one single interface into the configuration so we replace this with a bit indicating that the pin shall be configured into sleep mode. Some of the configuration can be refactored later to use less custom fields on the pin_cfg_t but we are currently leaving the old function calls in place so we stay compatible. ChangeLog v1->v2: - Drop a hunk changing pinmuxing for GPIO and move it over to the preceding pinmux patch. Acked-by: Stephen Warren <swarren@wwwdotorg.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/Kconfig1
-rw-r--r--drivers/pinctrl/pinctrl-nomadik.c113
2 files changed, 114 insertions, 0 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index bbf14dc67ad2..81b19671d193 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -40,6 +40,7 @@ config PINCTRL_NOMADIK
bool "Nomadik pin controller driver"
depends on ARCH_U8500
select PINMUX
+ select PINCONF
config PINCTRL_DB8500
bool "DB8500 pin controller driver"
diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c
index 8c632e931c22..b8e01c3eaa95 100644
--- a/drivers/pinctrl/pinctrl-nomadik.c
+++ b/drivers/pinctrl/pinctrl-nomadik.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
/* Since we request GPIOs from ourself */
#include <linux/pinctrl/consumer.h>
@@ -1568,10 +1569,122 @@ static struct pinmux_ops nmk_pinmux_ops = {
.gpio_disable_free = nmk_gpio_disable_free,
};
+int nmk_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long *config)
+{
+ /* Not implemented */
+ return -EINVAL;
+}
+
+int nmk_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long config)
+{
+ static const char *pullnames[] = {
+ [NMK_GPIO_PULL_NONE] = "none",
+ [NMK_GPIO_PULL_UP] = "up",
+ [NMK_GPIO_PULL_DOWN] = "down",
+ [3] /* illegal */ = "??"
+ };
+ static const char *slpmnames[] = {
+ [NMK_GPIO_SLPM_INPUT] = "input/wakeup",
+ [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup",
+ };
+ struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
+ struct nmk_gpio_chip *nmk_chip;
+ struct pinctrl_gpio_range *range;
+ struct gpio_chip *chip;
+ unsigned bit;
+
+ /*
+ * The pin config contains pin number and altfunction fields, here
+ * we just ignore that part. It's being handled by the framework and
+ * pinmux callback respectively.
+ */
+ pin_cfg_t cfg = (pin_cfg_t) config;
+ int pull = PIN_PULL(cfg);
+ int slpm = PIN_SLPM(cfg);
+ int output = PIN_DIR(cfg);
+ int val = PIN_VAL(cfg);
+ bool lowemi = PIN_LOWEMI(cfg);
+ bool gpiomode = PIN_GPIOMODE(cfg);
+ bool sleep = PIN_SLEEPMODE(cfg);
+
+ range = nmk_match_gpio_range(pctldev, pin);
+ if (!range) {
+ dev_err(npct->dev, "invalid pin offset %d\n", pin);
+ return -EINVAL;
+ }
+ if (!range->gc) {
+ dev_err(npct->dev, "GPIO chip missing in range for pin %d\n",
+ pin);
+ return -EINVAL;
+ }
+ chip = range->gc;
+ nmk_chip = container_of(chip, struct nmk_gpio_chip, chip);
+
+ if (sleep) {
+ int slpm_pull = PIN_SLPM_PULL(cfg);
+ int slpm_output = PIN_SLPM_DIR(cfg);
+ int slpm_val = PIN_SLPM_VAL(cfg);
+
+ /* All pins go into GPIO mode at sleep */
+ gpiomode = true;
+
+ /*
+ * 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 [%#lx]: pull %s, slpm %s (%s%s), lowemi %s\n",
+ pin, cfg, pullnames[pull], slpmnames[slpm],
+ output ? "output " : "input",
+ output ? (val ? "high" : "low") : "",
+ lowemi ? "on" : "off" );
+
+ clk_enable(nmk_chip->clk);
+ bit = pin % NMK_GPIO_PER_CHIP;
+ if (gpiomode)
+ /* No glitch when going to GPIO mode */
+ __nmk_gpio_set_mode(nmk_chip, bit, NMK_GPIO_ALT_GPIO);
+ if (output)
+ __nmk_gpio_make_output(nmk_chip, bit, val);
+ else {
+ __nmk_gpio_make_input(nmk_chip, bit);
+ __nmk_gpio_set_pull(nmk_chip, bit, pull);
+ }
+ /* TODO: isn't this only applicable on output pins? */
+ __nmk_gpio_set_lowemi(nmk_chip, bit, lowemi);
+
+ __nmk_gpio_set_slpm(nmk_chip, bit, slpm);
+ clk_disable(nmk_chip->clk);
+ return 0;
+}
+
+static struct pinconf_ops nmk_pinconf_ops = {
+ .pin_config_get = nmk_pin_config_get,
+ .pin_config_set = nmk_pin_config_set,
+};
+
static struct pinctrl_desc nmk_pinctrl_desc = {
.name = "pinctrl-nomadik",
.pctlops = &nmk_pinctrl_ops,
.pmxops = &nmk_pinmux_ops,
+ .confops = &nmk_pinconf_ops,
.owner = THIS_MODULE,
};