[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1423763164-5606-9-git-send-email-mcoquelin.stm32@gmail.com>
Date: Thu, 12 Feb 2015 18:45:58 +0100
From: Maxime Coquelin <mcoquelin.stm32@...il.com>
To: Jonathan Corbet <corbet@....net>,
Maxime Coquelin <mcoquelin.stm32@...il.com>,
Rob Herring <robh+dt@...nel.org>,
Pawel Moll <pawel.moll@....com>,
Mark Rutland <mark.rutland@....com>,
Ian Campbell <ijc+devicetree@...lion.org.uk>,
Kumar Gala <galak@...eaurora.org>,
Philipp Zabel <p.zabel@...gutronix.de>,
Russell King <linux@....linux.org.uk>,
Daniel Lezcano <daniel.lezcano@...aro.org>,
Thomas Gleixner <tglx@...utronix.de>,
Linus Walleij <linus.walleij@...aro.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Jiri Slaby <jslaby@...e.cz>, Arnd Bergmann <arnd@...db.de>,
Andrew Morton <akpm@...ux-foundation.org>,
"David S. Miller" <davem@...emloft.net>,
Mauro Carvalho Chehab <mchehab@....samsung.com>,
Joe Perches <joe@...ches.com>, Antti Palosaari <crope@....fi>,
Tejun Heo <tj@...nel.org>, Will Deacon <will.deacon@....com>,
Nikolay Borisov <Nikolay.Borisov@....com>,
Rusty Russell <rusty@...tcorp.com.au>,
Kees Cook <keescook@...omium.org>,
Michal Marek <mmarek@...e.cz>, linux-doc@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org, linux-kernel@...r.kernel.org,
devicetree@...r.kernel.org, linux-gpio@...r.kernel.org,
linux-serial@...r.kernel.org, linux-arch@...r.kernel.org,
linux-api@...r.kernel.org
Subject: [PATCH 08/14] pinctrl: Add pinctrl driver for STM32 MCUs
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.stm32@...il.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@...20000 {
+ 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@...11000 {
+ ...
+ 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.stm32@...il.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 majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists