lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ