This driver adds pinctrl and GPIO support to STMicrolectronic's
STM32 family of MCUs.

Pin muxing and GPIO handling have been tested on STM32F429
based Discovery board.

Signed-off-by: Maxime Coquelin <mcoquelin.st...@gmail.com>
---
 .../devicetree/bindings/pinctrl/pinctrl-stm32.txt  |  99 +++
 drivers/pinctrl/Kconfig                            |   9 +
 drivers/pinctrl/Makefile                           |   1 +
 drivers/pinctrl/pinctrl-stm32.c                    | 779 +++++++++++++++++++++
 include/dt-bindings/pinctrl/pinctrl-stm32.h        |  43 ++
 5 files changed, 931 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
 create mode 100644 drivers/pinctrl/pinctrl-stm32.c
 create mode 100644 include/dt-bindings/pinctrl/pinctrl-stm32.h

diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt 
b/Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
new file mode 100644
index 0000000..0fb5b24
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
@@ -0,0 +1,99 @@
+* STM32 GPIO and Pin Mux/Config controller
+
+STMicroelectronics's STM32 MCUs intregrate a GPIO and Pin mux/config hardware
+controller. It controls the input/output settings on the available pins and
+also provides ability to multiplex and configure the output of various on-chip
+controllers onto these pads.
+
+Pin controller node:
+Required properies:
+- compatible   : "st,stm32-pinctrl"
+- #address-cells: The value of this property must be 1
+- #size-cells  : The value of this property must be 1
+- ranges       : defines mapping between pin controller node (parent) to
+  gpio-bank node (children).
+
+GPIO controller/bank node:
+Required properties:
+- gpio-controller : Indicates this device is a GPIO controller
+- #gpio-cells    : Should be two.
+                       The first cell is the pin number
+                       The second one is the polarity:
+                               - 0 for active high
+                               - 1 for active low
+- reg            : The gpio address range, relative to the pinctrl range
+- st,bank-name   : Should be a name string for this bank as specified in
+  the datasheet
+
+Optional properties:
+- reset:         : Reference to the reset controller
+
+Example:
+#include <dt-bindings/pinctrl/pinctrl-stm32.h>
+...
+
+       pin-controller {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "st,stm32-pinctrl";
+               ranges = <0 0x40020000 0x3000>;
+
+               gpioa: gpio@40020000 {
+                       gpio-controller;
+                       #gpio-cells = <2>;
+                       reg = <0x0 0x400>;
+                       resets = <&reset_ahb1 0>;
+                       st,bank-name = "GPIOA";
+               };
+               ...
+               pin-functions nodes follow...
+       };
+
+Contents of function subnode node:
+----------------------------------
+
+Required properties for pin configuration node:
+- st,pins      : Child node with list of pins with configuration.
+
+Below is the format of how each pin conf should look like.
+
+<bank offset altmode pull type speed>
+
+Every PIO is represented with 4 to 6 parameters.
+Each parameter is explained as below.
+
+- bank   : Should be bank phandle to which this PIO belongs.
+- offset  : Offset in the PIO bank.
+- altmode : Should be mode or alternate function number associated this pin, as
+described in the datasheet (IN, OUT, ALT0...ALT15, ANALOG)
+- pull   : Should be either NO_PULL, PULL_UP or PULL_DOWN
+- type   : Should be either PUSH_PULL or OPEN_DRAIN.
+           Setting it is not needed for IN and ANALOG modes, or alternate
+           functions acting as inputs.
+- speed          : Value taken from the datasheet, depending on the function
+(LOW_SPEED, MEDIUM_SPEED, FAST_SPEED, HIGH_SPEED)
+           Setting it is not needed for IN and ANALOG modes, or alternate
+           functions acting as inputs.
+
+usart1 {
+       pinctrl_usart1: usart1-0 {
+               st,pins {
+                       tx = <&gpioa 9 ALT7 NO_PULL PUSH_PULL LOW_SPEED>;
+                       rx = <&gpioa 10 ALT7 NO_PULL PUSH_PULL LOW_SPEED>;
+               };
+       };
+};
+
+adc2 {
+       pinctrl_adc2: adc2-0 {
+               st,pins {
+                       adc0 = <&gpioe 4 ANALOG NO_PULL>;
+               };
+       };
+};
+
+usart1: usart@40011000 {
+       ...
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_usart1>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d014f22..af242bb 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -125,6 +125,15 @@ config PINCTRL_ST
        select PINCONF
        select GPIOLIB_IRQCHIP
 
+config PINCTRL_STM32
+       bool "STMicroelectronics STM32 pinctrl driver"
+       depends on OF
+       select PINMUX
+       select PINCONF
+       select GPIOLIB_IRQCHIP
+       help
+         This selects the device tree based generic pinctrl driver for STM32.
+
 config PINCTRL_TEGRA
        bool
        select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c030b3d..06ef8ab 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_PINCTRL_XWAY)    += pinctrl-xway.o
 obj-$(CONFIG_PINCTRL_LANTIQ)   += pinctrl-lantiq.o
 obj-$(CONFIG_PINCTRL_TB10X)    += pinctrl-tb10x.o
 obj-$(CONFIG_PINCTRL_ST)       += pinctrl-st.o
+obj-$(CONFIG_PINCTRL_STM32)    += pinctrl-stm32.o
 
 obj-$(CONFIG_ARCH_BERLIN)      += berlin/
 obj-y                          += freescale/
diff --git a/drivers/pinctrl/pinctrl-stm32.c b/drivers/pinctrl/pinctrl-stm32.c
new file mode 100644
index 0000000..5c474b0
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-stm32.c
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author:  Maxime Coquelin <mcoquelin.st...@gmail.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Heavily based on pinctrl-st.c from Srinivas Kandagatla
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_address.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include "core.h"
+
+#define STM32_GPIO_MODER       0x00
+#define STM32_GPIO_TYPER       0x04
+#define STM32_GPIO_SPEEDR      0x08
+#define STM32_GPIO_PUPDR       0x0c
+#define STM32_GPIO_IDR         0x10
+#define STM32_GPIO_ODR         0x14
+#define STM32_GPIO_BSRR                0x18
+#define STM32_GPIO_LCKR                0x1c
+#define STM32_GPIO_AFRL                0x20
+#define STM32_GPIO_AFRH                0x24
+
+#define STM32_GPIO_PINS_PER_BANK 16
+#define OF_GPIO_ARGS_MIN 4
+
+#define STM32_PINCONF_UNPACK(conf, param)\
+                               ((conf >> STM32_PINCONF_ ##param ##_SHIFT) \
+                               & STM32_PINCONF_ ##param ##_MASK)
+
+#define STM32_PINCONF_PACK(conf, val, param)   (conf |=\
+                               ((val & STM32_PINCONF_ ##param ##_MASK) << \
+                                       STM32_PINCONF_ ##param ##_SHIFT))
+
+#define STM32_PINCONF_SPEED_MASK               0x3
+#define STM32_PINCONF_SPEED_SHIFT              3
+#define STM32_PINCONF_UNPACK_SPEED(conf)\
+                               STM32_PINCONF_UNPACK(conf, SPEED)
+#define STM32_PINCONF_PACK_SPEED(conf, val)\
+                               STM32_PINCONF_PACK(conf, val, SPEED)
+
+#define STM32_PINCONF_TYPE_MASK                        0x1
+#define STM32_PINCONF_TYPE_SHIFT               2
+#define STM32_PINCONF_UNPACK_TYPE(conf)\
+                               STM32_PINCONF_UNPACK(conf, TYPE)
+#define STM32_PINCONF_PACK_TYPE(conf, val)\
+                               STM32_PINCONF_PACK(conf, val, TYPE)
+
+#define STM32_PINCONF_PUPD_MASK                        0x3
+#define STM32_PINCONF_PUPD_SHIFT               0
+#define STM32_PINCONF_UNPACK_PUPD(conf)\
+                               STM32_PINCONF_UNPACK(conf, PUPD)
+#define STM32_PINCONF_PACK_PUPD(conf, val)\
+                               STM32_PINCONF_PACK(conf, val, PUPD)
+
+
+#define STM32_PINCONF_ALT_MASK                 0xf
+#define STM32_PINCONF_ALT_SHIFT                2
+#define STM32_PINCONF_UNPACK_ALT(conf)\
+                               STM32_PINCONF_UNPACK(conf, ALT)
+#define STM32_PINCONF_PACK_ALT(conf, val)\
+                               STM32_PINCONF_PACK(conf, val, ALT)
+
+#define STM32_PINCONF_MODE_MASK                        0x3
+#define STM32_PINCONF_MODE_SHIFT               0
+#define STM32_PINCONF_UNPACK_MODE(conf)\
+                               STM32_PINCONF_UNPACK(conf, MODE)
+#define STM32_PINCONF_PACK_MODE(conf, val)\
+                               STM32_PINCONF_PACK(conf, val, MODE)
+
+
+
+#define gpio_range_to_bank(chip) \
+               container_of(chip, struct stm32_gpio_bank, range)
+
+#define gpio_chip_to_bank(chip) \
+               container_of(chip, struct stm32_gpio_bank, gpio_chip)
+
+struct stm32_pinconf {
+       int             pin;
+       const char      *name;
+       unsigned long   config;
+       int             altfunc;
+};
+
+struct stm32_pmx_func {
+       const char      *name;
+       const char      **groups;
+       unsigned        ngroups;
+};
+
+struct stm32_pctl_group {
+       const char              *name;
+       unsigned int            *pins;
+       unsigned                npins;
+       struct stm32_pinconf    *pin_conf;
+};
+
+struct stm32_gpio_bank {
+       void __iomem *base;
+       struct gpio_chip                gpio_chip;
+       struct pinctrl_gpio_range       range;
+       spinlock_t                      lock;
+};
+
+struct stm32_pinctrl {
+       struct device           *dev;
+       struct pinctrl_dev              *pctl;
+       struct stm32_gpio_bank  *banks;
+       int                     nbanks;
+       struct stm32_pmx_func   *functions;
+       int                     nfunctions;
+       struct stm32_pctl_group *groups;
+       int                     ngroups;
+};
+
+static inline int stm32_gpio_pin(int gpio)
+{
+       return gpio % STM32_GPIO_PINS_PER_BANK;
+}
+
+/* Pinconf  */
+static void stm32_pinconf_set_config(struct stm32_gpio_bank *bank,
+                               int pin, unsigned long config)
+{
+       u32 type, speed, pupd, val;
+       unsigned long flags;
+
+       type = STM32_PINCONF_UNPACK_TYPE(config);
+       spin_lock_irqsave(&bank->lock, flags);
+       val = readl_relaxed(bank->base + STM32_GPIO_TYPER);
+       val &= ~BIT(pin);
+       val |= type << pin;
+       writel_relaxed(val, bank->base + STM32_GPIO_TYPER);
+       spin_unlock_irqrestore(&bank->lock, flags);
+
+       speed = STM32_PINCONF_UNPACK_SPEED(config);
+       spin_lock_irqsave(&bank->lock, flags);
+       val = readl_relaxed(bank->base + STM32_GPIO_SPEEDR);
+       val &= ~GENMASK(pin * 2 + 1, pin * 2);
+       val |= speed << (pin * 2);
+       writel_relaxed(val, bank->base + STM32_GPIO_SPEEDR);
+       spin_unlock_irqrestore(&bank->lock, flags);
+
+       pupd = STM32_PINCONF_UNPACK_PUPD(config);
+       spin_lock_irqsave(&bank->lock, flags);
+       val = readl_relaxed(bank->base + STM32_GPIO_PUPDR);
+       val &= ~GENMASK(pin * 2 + 1, pin * 2);
+       val |= pupd << (pin * 2);
+       writel_relaxed(val, bank->base + STM32_GPIO_PUPDR);
+       spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+static void stm32_pinconf_get_config(struct stm32_gpio_bank *bank,
+                               int pin, unsigned long *config)
+{
+       u32 val;
+
+       val = readl_relaxed(bank->base + STM32_GPIO_TYPER);
+       val = val >> pin;
+       STM32_PINCONF_PACK_TYPE(*config, val);
+
+       val = readl_relaxed(bank->base + STM32_GPIO_SPEEDR);
+       val = val >> (pin * 2);
+       STM32_PINCONF_PACK_SPEED(*config, val);
+
+       val = readl_relaxed(bank->base + STM32_GPIO_PUPDR);
+       val = val >> (pin * 2);
+       STM32_PINCONF_PACK_PUPD(*config, val);
+}
+
+static int stm32_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin_id,
+                       unsigned long *configs, unsigned num_configs)
+{
+       struct pinctrl_gpio_range *range =
+                        pinctrl_find_gpio_range_from_pin(pctldev, pin_id);
+       struct stm32_gpio_bank *bank = gpio_range_to_bank(range);
+       int pin = stm32_gpio_pin(pin_id);
+       int i;
+
+       for (i = 0; i < num_configs; i++)
+               stm32_pinconf_set_config(bank, pin, configs[i]);
+
+       return 0;
+}
+
+static int stm32_pinconf_get(struct pinctrl_dev *pctldev,
+                            unsigned pin_id, unsigned long *config)
+{
+       struct pinctrl_gpio_range *range =
+                        pinctrl_find_gpio_range_from_pin(pctldev, pin_id);
+       struct stm32_gpio_bank *bank = gpio_range_to_bank(range);
+       int pin = stm32_gpio_pin(pin_id);
+
+       *config = 0;
+       stm32_pinconf_get_config(bank, pin, config);
+
+       return 0;
+}
+
+static void stm32_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+                                  struct seq_file *s, unsigned pin_id)
+{
+       unsigned long config;
+
+       stm32_pinconf_get(pctldev, pin_id, &config);
+
+       seq_printf(s, "[PUPD:%ld,TYPE:%ld,SPEED:%ld]\n",
+               STM32_PINCONF_UNPACK_PUPD(config),
+               STM32_PINCONF_UNPACK_TYPE(config),
+               STM32_PINCONF_UNPACK_SPEED(config));
+}
+
+static struct pinconf_ops stm32_confops = {
+       .pin_config_get         = stm32_pinconf_get,
+       .pin_config_set         = stm32_pinconf_set,
+       .pin_config_dbg_show    = stm32_pinconf_dbg_show,
+};
+
+static void stm32_pctl_set_function(struct stm32_gpio_bank *bank,
+               int pin_id, int function)
+{
+       u32 mode, alt, val;
+       int pin = stm32_gpio_pin(pin_id);
+       int alt_shift = (pin % 8) * 4;
+       int alt_offset = STM32_GPIO_AFRL + (pin / 8) * 4;
+       unsigned long flags;
+
+       mode = STM32_PINCONF_UNPACK_MODE(function);
+       alt = STM32_PINCONF_UNPACK_ALT(function);
+
+       spin_lock_irqsave(&bank->lock, flags);
+
+       val = readl_relaxed(bank->base + alt_offset);
+       val &= ~GENMASK(alt_shift + 3, alt_shift);
+       val |= (alt << alt_shift);
+       writel_relaxed(val, bank->base + alt_offset);
+
+       val = readl_relaxed(bank->base + STM32_GPIO_MODER);
+       val &= ~GENMASK(pin * 2 + 1, pin * 2);
+       val |= mode << (pin * 2);
+       writel_relaxed(val, bank->base + STM32_GPIO_MODER);
+
+       spin_unlock_irqrestore(&bank->lock, flags);
+}
+
+/* Pinmux */
+static int stm32_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+       return info->nfunctions;
+}
+
+static const char *stm32_pmx_get_fname(struct pinctrl_dev *pctldev,
+       unsigned selector)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+       return info->functions[selector].name;
+}
+
+static int stm32_pmx_get_groups(struct pinctrl_dev *pctldev,
+       unsigned selector, const char * const **grps, unsigned * const ngrps)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+       *grps = info->functions[selector].groups;
+       *ngrps = info->functions[selector].ngroups;
+
+       return 0;
+}
+
+static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,
+                       unsigned group)
+{
+       struct pinctrl_gpio_range *range;
+       struct stm32_gpio_bank *bank;
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+       struct stm32_pinconf *conf = info->groups[group].pin_conf;
+       int i;
+
+       for (i = 0; i < info->groups[group].npins; i++) {
+               range = pinctrl_find_gpio_range_from_pin(pctldev, conf[i].pin);
+               bank = gpio_range_to_bank(range);
+               stm32_pctl_set_function(bank, conf[i].pin, conf[i].altfunc);
+       }
+
+       return 0;
+}
+
+static int stm32_pmx_set_gpio_direction(struct pinctrl_dev *pctldev,
+                       struct pinctrl_gpio_range *range, unsigned gpio,
+                       bool input)
+{
+       struct stm32_gpio_bank *bank = gpio_range_to_bank(range);
+
+       stm32_pctl_set_function(bank, gpio, !input);
+
+       return 0;
+}
+
+static struct pinmux_ops stm32_pmxops = {
+       .get_functions_count    = stm32_pmx_get_funcs_count,
+       .get_function_name      = stm32_pmx_get_fname,
+       .get_function_groups    = stm32_pmx_get_groups,
+       .set_mux                = stm32_pmx_set_mux,
+       .gpio_set_direction     = stm32_pmx_set_gpio_direction,
+};
+
+/* Pinctrl Groups */
+static int stm32_pctl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+       return info->ngroups;
+}
+
+static const char *stm32_pctl_get_group_name(struct pinctrl_dev *pctldev,
+                                      unsigned selector)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+       return info->groups[selector].name;
+}
+
+static int stm32_pctl_get_group_pins(struct pinctrl_dev *pctldev,
+       unsigned selector, const unsigned **pins, unsigned *npins)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+       if (selector >= info->ngroups)
+               return -EINVAL;
+
+       *pins = info->groups[selector].pins;
+       *npins = info->groups[selector].npins;
+
+       return 0;
+}
+
+static const inline struct stm32_pctl_group *stm32_pctl_find_group_by_name(
+       const struct stm32_pinctrl *info, const char *name)
+{
+       int i;
+
+       for (i = 0; i < info->ngroups; i++) {
+               if (!strcmp(info->groups[i].name, name))
+                       return &info->groups[i];
+       }
+
+       return NULL;
+}
+
+static int stm32_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
+       struct device_node *np, struct pinctrl_map **map, unsigned *num_maps)
+{
+       struct stm32_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+       const struct stm32_pctl_group *grp;
+       struct pinctrl_map *new_map;
+       struct device_node *parent;
+       int map_num, i;
+
+       grp = stm32_pctl_find_group_by_name(info, np->name);
+       if (!grp) {
+               dev_err(info->dev, "unable to find group for node %s\n",
+                       np->name);
+               return -EINVAL;
+       }
+
+       map_num = grp->npins + 1;
+       new_map = devm_kzalloc(pctldev->dev,
+                               sizeof(*new_map) * map_num, GFP_KERNEL);
+       if (!new_map)
+               return -ENOMEM;
+
+       parent = of_get_parent(np);
+       if (!parent) {
+               devm_kfree(pctldev->dev, new_map);
+               return -EINVAL;
+       }
+
+       *map = new_map;
+       *num_maps = map_num;
+       new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+       new_map[0].data.mux.function = parent->name;
+       new_map[0].data.mux.group = np->name;
+       of_node_put(parent);
+
+       /* create config map per pin */
+       new_map++;
+       for (i = 0; i < grp->npins; i++) {
+               new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+               new_map[i].data.configs.group_or_pin =
+                               pin_get_name(pctldev, grp->pins[i]);
+               new_map[i].data.configs.configs = &grp->pin_conf[i].config;
+               new_map[i].data.configs.num_configs = 1;
+       }
+       dev_info(pctldev->dev, "maps: function %s group %s num %d\n",
+               (*map)->data.mux.function, grp->name, map_num);
+
+       return 0;
+}
+
+static void stm32_pctl_dt_free_map(struct pinctrl_dev *pctldev,
+                       struct pinctrl_map *map, unsigned num_maps)
+{
+}
+
+static struct pinctrl_ops stm32_pctlops = {
+       .get_groups_count       = stm32_pctl_get_groups_count,
+       .get_group_pins         = stm32_pctl_get_group_pins,
+       .get_group_name         = stm32_pctl_get_group_name,
+       .dt_node_to_map         = stm32_pctl_dt_node_to_map,
+       .dt_free_map            = stm32_pctl_dt_free_map,
+};
+
+static void stm32_pctl_dt_child_count(struct stm32_pinctrl *info,
+                                    struct device_node *np)
+{
+       struct device_node *child;
+
+       for_each_child_of_node(np, child) {
+               if (of_property_read_bool(child, "gpio-controller")) {
+                       info->nbanks++;
+               } else {
+                       info->nfunctions++;
+                       info->ngroups += of_get_child_count(child);
+               }
+       }
+}
+
+static int stm32_pctl_dt_parse_groups(struct device_node *np,
+       struct stm32_pctl_group *grp, struct stm32_pinctrl *info, int idx)
+{
+       const __be32 *list;
+       struct property *pp;
+       struct stm32_pinconf *conf;
+       struct device_node *pins;
+       int i = 0, npins = 0, nr_props;
+
+       pins = of_get_child_by_name(np, "st,pins");
+       if (!pins)
+               return -ENODATA;
+
+       for_each_property_of_node(pins, pp) {
+               /* Skip those we do not want to proceed */
+               if (!strcmp(pp->name, "name"))
+                       continue;
+
+               if (pp  && (pp->length / sizeof(__be32)) >= OF_GPIO_ARGS_MIN) {
+                       npins++;
+               } else {
+                       pr_warn("Invalid st,pins in %s node\n", np->name);
+                       return -EINVAL;
+               }
+       }
+
+       grp->npins = npins;
+       grp->name = np->name;
+       grp->pins = devm_kzalloc(info->dev, npins * sizeof(u32), GFP_KERNEL);
+       grp->pin_conf = devm_kzalloc(info->dev,
+                                       npins * sizeof(*conf), GFP_KERNEL);
+
+       if (!grp->pins || !grp->pin_conf)
+               return -ENOMEM;
+
+       /* <bank offset mux pull type speed> */
+       for_each_property_of_node(pins, pp) {
+               if (!strcmp(pp->name, "name"))
+                       continue;
+               nr_props = pp->length / sizeof(u32);
+               list = pp->value;
+               conf = &grp->pin_conf[i];
+
+               /* bank & offset */
+               be32_to_cpup(list++);
+               be32_to_cpup(list++);
+               conf->pin = of_get_named_gpio(pins, pp->name, 0);
+               conf->name = pp->name;
+               grp->pins[i] = conf->pin;
+               /* mux */
+               conf->altfunc = be32_to_cpup(list++);
+               conf->config = 0;
+               /* pull-up/down */
+               conf->config |= be32_to_cpup(list++);
+               if (nr_props > OF_GPIO_ARGS_MIN) {
+                       /* push-pull/open-drain */
+                       conf->config |= be32_to_cpup(list++);
+                       /* speed */
+                       conf->config |= be32_to_cpup(list++);
+               }
+               i++;
+       }
+       of_node_put(pins);
+
+       return 0;
+}
+
+static int stm32_pctl_parse_functions(struct device_node *np,
+                       struct stm32_pinctrl *info, u32 index, int *grp_index)
+{
+       struct device_node *child;
+       struct stm32_pmx_func *func;
+       struct stm32_pctl_group *grp;
+       int i = 0, ret;
+
+       func = &info->functions[index];
+       func->name = np->name;
+       func->ngroups = of_get_child_count(np);
+       if (func->ngroups == 0) {
+               dev_err(info->dev, "No groups defined\n");
+               return -EINVAL;
+       }
+       func->groups = devm_kzalloc(info->dev,
+                       func->ngroups * sizeof(char *), GFP_KERNEL);
+       if (!func->groups)
+               return -ENOMEM;
+
+       for_each_child_of_node(np, child) {
+               func->groups[i] = child->name;
+               grp = &info->groups[*grp_index];
+               *grp_index += 1;
+               ret = stm32_pctl_dt_parse_groups(child, grp, info, i++);
+               if (ret)
+                       return ret;
+       }
+       dev_info(info->dev, "Function[%d\t name:%s,\tgroups:%d]\n",
+                               index, func->name, func->ngroups);
+
+       return 0;
+}
+
+static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank,
+       unsigned offset, int value)
+{
+       if (!value)
+               offset += STM32_GPIO_PINS_PER_BANK;
+
+       writel_relaxed(BIT(offset), bank->base + STM32_GPIO_BSRR);
+}
+
+static int stm32_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+       return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void stm32_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+       pinctrl_free_gpio(chip->base + offset);
+}
+
+static int stm32_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip);
+
+       return !!(readl_relaxed(bank->base + STM32_GPIO_IDR) & BIT(offset));
+}
+
+static void stm32_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip);
+
+       __stm32_gpio_set(bank, offset, value);
+}
+
+static int stm32_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+       pinctrl_gpio_direction_input(chip->base + offset);
+
+       return 0;
+}
+
+static int stm32_gpio_direction_output(struct gpio_chip *chip,
+       unsigned offset, int value)
+{
+       struct stm32_gpio_bank *bank = gpio_chip_to_bank(chip);
+
+       __stm32_gpio_set(bank, offset, value);
+       pinctrl_gpio_direction_output(chip->base + offset);
+
+       return 0;
+}
+
+static struct gpio_chip stm32_gpio_template = {
+       .request                = stm32_gpio_request,
+       .free                   = stm32_gpio_free,
+       .get                    = stm32_gpio_get,
+       .set                    = stm32_gpio_set,
+       .direction_input        = stm32_gpio_direction_input,
+       .direction_output       = stm32_gpio_direction_output,
+       .ngpio                  = STM32_GPIO_PINS_PER_BANK,
+};
+
+static int stm32_gpiolib_register_bank(struct stm32_pinctrl *info,
+       int bank_nr, struct device_node *np)
+{
+       struct stm32_gpio_bank *bank = &info->banks[bank_nr];
+       struct pinctrl_gpio_range *range = &bank->range;
+       struct device *dev = info->dev;
+       struct resource res;
+       struct reset_control *rstc;
+       int bank_num = of_alias_get_id(np, "gpio");
+       int err;
+
+       rstc = of_reset_control_get(np, NULL);
+       if (!IS_ERR(rstc))
+               reset_control_deassert(rstc);
+
+       if (of_address_to_resource(np, 0, &res))
+               return -ENODEV;
+
+       bank->base = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(bank->base))
+               return PTR_ERR(bank->base);
+
+       bank->gpio_chip = stm32_gpio_template;
+       bank->gpio_chip.base = bank_num * STM32_GPIO_PINS_PER_BANK;
+       bank->gpio_chip.of_node = np;
+       bank->gpio_chip.dev = dev;
+       spin_lock_init(&bank->lock);
+
+       of_property_read_string(np, "st,bank-name", &range->name);
+       bank->gpio_chip.label = range->name;
+
+       range->id = bank_num;
+       range->pin_base = range->base = range->id * STM32_GPIO_PINS_PER_BANK;
+       range->npins = bank->gpio_chip.ngpio;
+       range->gc = &bank->gpio_chip;
+       err  = gpiochip_add(&bank->gpio_chip);
+       if (err) {
+               dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_num);
+               return err;
+       }
+       dev_info(dev, "%s bank added.\n", range->name);
+
+       return 0;
+}
+
+static int stm32_pctl_probe_dt(struct platform_device *pdev,
+       struct pinctrl_desc *pctl_desc, struct stm32_pinctrl *info)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct pinctrl_pin_desc *pdesc;
+       struct device_node *child;
+       int grp_index = 0;
+       int i = 0, j = 0, k = 0, bank = 0,  ret = 0;
+
+       stm32_pctl_dt_child_count(info, np);
+       if (!info->nbanks) {
+               dev_err(&pdev->dev, "you need atleast one gpio bank\n");
+               return -EINVAL;
+       }
+
+       dev_info(&pdev->dev, "nbanks = %d\n", info->nbanks);
+       dev_info(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+       dev_info(&pdev->dev, "ngroups = %d\n", info->ngroups);
+
+       info->functions = devm_kzalloc(&pdev->dev,
+               info->nfunctions * sizeof(*info->functions), GFP_KERNEL);
+
+       info->groups = devm_kzalloc(&pdev->dev,
+                       info->ngroups * sizeof(*info->groups), GFP_KERNEL);
+
+       info->banks = devm_kzalloc(&pdev->dev,
+                       info->nbanks * sizeof(*info->banks), GFP_KERNEL);
+
+       if (!info->functions || !info->groups || !info->banks)
+               return -ENOMEM;
+
+       pctl_desc->npins = info->nbanks * STM32_GPIO_PINS_PER_BANK;
+       pdesc = devm_kzalloc(&pdev->dev,
+                       sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL);
+       if (!pdesc)
+               return -ENOMEM;
+
+       pctl_desc->pins = pdesc;
+
+       for_each_child_of_node(np, child) {
+               if (of_property_read_bool(child, "gpio-controller")) {
+                       const char *bank_name;
+
+                       ret = stm32_gpiolib_register_bank(info, bank, child);
+                       if (ret)
+                               return ret;
+
+                       k = info->banks[bank].range.pin_base;
+                       bank_name = info->banks[bank].range.name;
+                       for (j = 0; j < STM32_GPIO_PINS_PER_BANK; j++, k++) {
+                               pdesc->number = k;
+                               pdesc->name = kasprintf(GFP_KERNEL, "%s[%d]",
+                                                       bank_name, j);
+                               pdesc++;
+                       }
+                       bank++;
+               } else {
+                       ret = stm32_pctl_parse_functions(child, info,
+                                                       i++, &grp_index);
+                       if (ret) {
+                               dev_err(&pdev->dev, "No functions found.\n");
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int stm32_pctl_probe(struct platform_device *pdev)
+{
+       struct stm32_pinctrl *info;
+       struct pinctrl_desc *pctl_desc;
+       int ret, i;
+
+       if (!pdev->dev.of_node) {
+               dev_err(&pdev->dev, "device not found.\n");
+               return -EINVAL;
+       }
+
+       pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
+       if (!pctl_desc)
+               return -ENOMEM;
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->dev = &pdev->dev;
+       platform_set_drvdata(pdev, info);
+       ret = stm32_pctl_probe_dt(pdev, pctl_desc, info);
+       if (ret)
+               return ret;
+
+       pctl_desc->owner        = THIS_MODULE;
+       pctl_desc->pctlops      = &stm32_pctlops;
+       pctl_desc->pmxops       = &stm32_pmxops;
+       pctl_desc->confops      = &stm32_confops;
+       pctl_desc->name         = dev_name(&pdev->dev);
+
+       info->pctl = pinctrl_register(pctl_desc, &pdev->dev, info);
+       if (!info->pctl) {
+               dev_err(&pdev->dev, "Failed pinctrl registration\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < info->nbanks; i++)
+               pinctrl_add_gpio_range(info->pctl, &info->banks[i].range);
+
+       return 0;
+}
+
+static struct of_device_id stm32_pctl_of_match[] = {
+       { .compatible = "st,stm32-pinctrl" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver stm32_pctl_driver = {
+       .driver = {
+               .name = "stm32-pinctrl",
+               .owner = THIS_MODULE,
+               .of_match_table = stm32_pctl_of_match,
+       },
+       .probe = stm32_pctl_probe,
+};
+
+static int __init stm32_pctl_init(void)
+{
+       return platform_driver_register(&stm32_pctl_driver);
+}
+arch_initcall(stm32_pctl_init);
diff --git a/include/dt-bindings/pinctrl/pinctrl-stm32.h 
b/include/dt-bindings/pinctrl/pinctrl-stm32.h
new file mode 100644
index 0000000..3e93a86
--- /dev/null
+++ b/include/dt-bindings/pinctrl/pinctrl-stm32.h
@@ -0,0 +1,43 @@
+#ifndef _DT_BINDINGS_PINCTRL_STM32_H
+#define _DT_BINDINGS_PINCTRL_STM32_H
+
+/* Modes */
+#define IN             0
+#define OUT            1
+#define ALT            2
+#define ANALOG         3
+
+/* Alternate functions */
+#define ALT0           ((0 << 2) | ALT)
+#define ALT1           ((1 << 2) | ALT)
+#define ALT2           ((2 << 2) | ALT)
+#define ALT3           ((3 << 2) | ALT)
+#define ALT4           ((4 << 2) | ALT)
+#define ALT5           ((5 << 2) | ALT)
+#define ALT6           ((6 << 2) | ALT)
+#define ALT7           ((7 << 2) | ALT)
+#define ALT8           ((8 << 2) | ALT)
+#define ALT9           ((9 << 2) | ALT)
+#define ALT10          ((10 << 2) | ALT)
+#define ALT11          ((11 << 2) | ALT)
+#define ALT12          ((12 << 2) | ALT)
+#define ALT13          ((13 << 2) | ALT)
+#define ALT14          ((14 << 2) | ALT)
+#define ALT15          ((15 << 2) | ALT)
+
+/* Pull-Up/Down */
+#define NO_PULL                0
+#define PULL_UP                1
+#define PULL_DOWN      2
+
+/* Type */
+#define PUSH_PULL      (0 << 2)
+#define OPEN_DRAIN     (1 << 2)
+
+/* Speed */
+#define LOW_SPEED      (0 << 3)
+#define MEDIUM_SPEED   (1 << 3)
+#define FAST_SPEED     (2 << 3)
+#define HIGH_SPEED     (3 << 3)
+
+#endif /* _DT_BINDINGS_PINCTRL_STM32_H */
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to