From patchwork Thu Feb 12 17:45:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxime Coquelin X-Patchwork-Id: 5821661 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 6BE639F37F for ; Thu, 12 Feb 2015 19:43:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5D82420253 for ; Thu, 12 Feb 2015 19:43:22 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 158C5201ED for ; Thu, 12 Feb 2015 19:43:19 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YLzbx-0004vb-3G; Thu, 12 Feb 2015 19:39:41 +0000 Received: from mail-wg0-x244.google.com ([2a00:1450:400c:c00::244]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YLzaQ-0003f9-QT for linux-arm-kernel@lists.infradead.org; Thu, 12 Feb 2015 19:38:12 +0000 Received: by mail-wg0-f68.google.com with SMTP id y19so3731733wgg.3 for ; Thu, 12 Feb 2015 11:37:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=LW5ISbLVHLcwFI9oPXJ3a0cGNznQ7++qCAjnsJ2ZUjg=; b=gGhv4xvF+FK1gc9uOjfo6cokfjhC53FVMWCedQTOGnSxI3HYbUhRdoazzOGtLVP9Si TfgVzgvSQBZ1qjwRwvGN/lPFGI1HpaKq/LAxQgK9hBVlrSCafUaukXbtz6hrbGtkSix5 d17ln7sXzkFg87vKokH4OdIxHbfF48n+LuK/xK5mBhU6EHnOH4x81yVd0qrkm9Xpsudm S81U0bOjaNuQZl/JaAogAduhnEDDFYnVTlqR2n5ssRf518mOqJpzg61ZwAvs9vy/Bjf/ tOkaTW7DGhFVOKp1xfx5aT3TqEkWmEoutE0zNdzShWW9xmO9lWEuOI1EHkGC6Ypw4NUt eTWw== X-Received: by 10.194.86.135 with SMTP id p7mr10081046wjz.89.1423763195645; Thu, 12 Feb 2015 09:46:35 -0800 (PST) Received: from lmecul0520.st.com. (157.162.113.78.rev.sfr.net. [78.113.162.157]) by mx.google.com with ESMTPSA id ka1sm6614924wjc.2.2015.02.12.09.46.32 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 12 Feb 2015 09:46:35 -0800 (PST) From: Maxime Coquelin To: Jonathan Corbet , Maxime Coquelin , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Philipp Zabel , Russell King , Daniel Lezcano , Thomas Gleixner , Linus Walleij , Greg Kroah-Hartman , Jiri Slaby , Arnd Bergmann , Andrew Morton , "David S. Miller" , Mauro Carvalho Chehab , Joe Perches , Antti Palosaari , Tejun Heo , Will Deacon , Nikolay Borisov , Rusty Russell , Kees Cook , Michal Marek , linux-doc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org, linux-serial@vger.kernel.org, linux-arch@vger.kernel.org, linux-api@vger.kernel.org Subject: [PATCH 08/14] pinctrl: Add pinctrl driver for STM32 MCUs Date: Thu, 12 Feb 2015 18:45:58 +0100 Message-Id: <1423763164-5606-9-git-send-email-mcoquelin.stm32@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1423763164-5606-1-git-send-email-mcoquelin.stm32@gmail.com> References: <1423763164-5606-1-git-send-email-mcoquelin.stm32@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150212_113807_428147_37243B21 X-CRM114-Status: GOOD ( 21.57 ) X-Spam-Score: -0.6 (/) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- .../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 +... + + 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. + + + +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 + * License terms: GNU General Public License (GPL), version 2 + * + * Heavily based on pinctrl-st.c from Srinivas Kandagatla + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; + + /* */ + 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 */