]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
pinctrl/nomadik: implement pin configuration
authorLinus Walleij <linus.walleij@linaro.org>
Thu, 3 May 2012 13:58:12 +0000 (15:58 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Fri, 11 May 2012 09:14:16 +0000 (11:14 +0200)
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>
arch/arm/plat-nomadik/include/plat/pincfg.h
drivers/pinctrl/Kconfig
drivers/pinctrl/pinctrl-nomadik.c

index c015133a7ad3f940986b32ad3ff0f803111a18f9..9c949c7c98a73e810558a542cb85608cad152f3c 100644 (file)
@@ -124,6 +124,19 @@ typedef unsigned long pin_cfg_t;
 #define PIN_LOWEMI_DISABLED    (0 << PIN_LOWEMI_SHIFT)
 #define PIN_LOWEMI_ENABLED     (1 << PIN_LOWEMI_SHIFT)
 
+#define PIN_GPIOMODE_SHIFT     26
+#define PIN_GPIOMODE_MASK      (0x1 << PIN_GPIOMODE_SHIFT)
+#define PIN_GPIOMODE(x)                (((x) & PIN_GPIOMODE_MASK) >> PIN_GPIOMODE_SHIFT)
+#define PIN_GPIOMODE_DISABLED  (0 << PIN_GPIOMODE_SHIFT)
+#define PIN_GPIOMODE_ENABLED   (1 << PIN_GPIOMODE_SHIFT)
+
+#define PIN_SLEEPMODE_SHIFT    27
+#define PIN_SLEEPMODE_MASK     (0x1 << PIN_SLEEPMODE_SHIFT)
+#define PIN_SLEEPMODE(x)       (((x) & PIN_SLEEPMODE_MASK) >> PIN_SLEEPMODE_SHIFT)
+#define PIN_SLEEPMODE_DISABLED (0 << PIN_SLEEPMODE_SHIFT)
+#define PIN_SLEEPMODE_ENABLED  (1 << PIN_SLEEPMODE_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)
index bbf14dc67ad2059e7e81dd7bb10eab7651547554..81b19671d193f59a6950585b541464eb9dae5519 100644 (file)
@@ -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"
index 8c632e931c22df5b515c6653c02d1b98cc6735d7..b8e01c3eaa95fdd9186ced5a85a4fb1e45d65c19 100644 (file)
@@ -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,
 };