From 78bafc66180d42f972b443b0b573a1b6ff9aa522 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Tue, 28 May 2013 17:32:08 +0800 Subject: pinctrl: add VF610 pinctrl driver Adds Freescale Vybrid VF610 pin controller driver to IMX common pinctrl driver framework. Signed-off-by: Jingchang Lu Acked-by: Shawn Guo Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/pinctrl/Kconfig') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 8f6692438149..47cb923cd2df 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -108,6 +108,14 @@ config PINCTRL_IMX6SL help Say Y here to enable the imx6sl pinctrl driver +config PINCTRL_VF610 + bool "Freescale Vybrid VF610 pinctrl driver" + depends on OF + depends on SOC_VF610 + select PINCTRL_IMX + help + Say Y here to enable the Freescale Vybrid VF610 pinctrl driver + config PINCTRL_LANTIQ bool depends on LANTIQ -- cgit v1.2.3 From d3e5116119bd02ea7716bbe04b39c21bba4bcf42 Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Mon, 10 Jun 2013 22:16:22 +0200 Subject: pinctrl: add pinctrl driver for Rockchip SoCs This driver adds support the Cortex-A9 based SoCs from Rockchip, so at least the RK2928, RK3066 (a and b) and RK3188. Earlier Rockchip SoCs seem to use similar mechanics for gpio handling so should be supportable with relative small changes. Pull handling on the rk3188 is currently a stub, due to it being a bit different to the earlier SoCs. Pinmuxing as well as gpio (and interrupt-) handling tested on a rk3066a based machine. Signed-off-by: Heiko Stuebner Signed-off-by: Linus Walleij --- .../bindings/pinctrl/rockchip,pinctrl.txt | 97 ++ drivers/pinctrl/Kconfig | 6 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-rockchip.c | 1360 ++++++++++++++++++++ include/dt-bindings/pinctrl/rockchip.h | 32 + 5 files changed, 1496 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-rockchip.c create mode 100644 include/dt-bindings/pinctrl/rockchip.h (limited to 'drivers/pinctrl/Kconfig') diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt new file mode 100644 index 000000000000..b0fb1018d7ad --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt @@ -0,0 +1,97 @@ +* Rockchip Pinmux Controller + +The Rockchip Pinmux Controller, enables the IC +to share one PAD to several functional blocks. The sharing is done by +multiplexing the PAD input/output signals. For each PAD there are up to +4 muxing options with option 0 being the use as a GPIO. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +The Rockchip pin configuration node is a node of a group of pins which can be +used for a specific device or function. This node represents both mux and +config of the pins in that group. The 'pins' selects the function mode(also +named pin mode) this pin can work on and the 'config' configures various pad +settings such as pull-up, etc. + +The pins are grouped into up to 5 individual pin banks which need to be +defined as gpio sub-nodes of the pinmux controller. + +Required properties for iomux controller: + - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl" + "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl" + +Required properties for gpio sub nodes: + - compatible: "rockchip,gpio-bank" + - reg: register of the gpio bank (different than the iomux registerset) + - interrupts: base interrupt of the gpio bank in the interrupt controller + - clocks: clock that drives this bank + - gpio-controller: identifies the node as a gpio controller and pin bank. + - #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO + binding is used, the amount of cells must be specified as 2. See generic + GPIO binding documentation for description of particular cells. + - interrupt-controller: identifies the controller node as interrupt-parent. + - #interrupt-cells: the value of this property should be 2 and the interrupt + cells should use the standard two-cell scheme described in + bindings/interrupt-controller/interrupts.txt + +Required properties for pin configuration node: + - rockchip,pins: 3 integers array, represents a group of pins mux and config + setting. The format is rockchip,pins = . + The MUX 0 means gpio and MUX 1 to 3 mean the specific device function. + The phandle of a node containing the generic pinconfig options + to use, as described in pinctrl-bindings.txt in this directory. + +Examples: + +#include + +... + +pinctrl@20008000 { + compatible = "rockchip,rk3066a-pinctrl"; + reg = <0x20008000 0x150>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + gpio0: gpio0@20034000 { + compatible = "rockchip,gpio-bank"; + reg = <0x20034000 0x100>; + interrupts = ; + clocks = <&clk_gates8 9>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + ... + + pcfg_pull_default: pcfg_pull_default { + bias-pull-pin-default + }; + + uart2 { + uart2_xfer: uart2-xfer { + rockchip,pins = , + ; + }; + }; +}; + +uart2: serial@20064000 { + compatible = "snps,dw-apb-uart"; + reg = <0x20064000 0x400>; + interrupts = ; + reg-shift = <2>; + reg-io-width = <1>; + clocks = <&mux_uart2>; + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&uart2_xfer>; +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 47cb923cd2df..269c0406dce4 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -158,6 +158,12 @@ config PINCTRL_DB8540 bool "DB8540 pin controller driver" depends on PINCTRL_NOMADIK && ARCH_U8500 +config PINCTRL_ROCKCHIP + bool + select PINMUX + select GENERIC_PINCONF + select GENERIC_IRQ_CHIP + config PINCTRL_SINGLE tristate "One-register-per-pin type device tree based pinctrl driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 08f2b3e02d42..ff38aca65689 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o +obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SIRF) += sirf/ obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c new file mode 100644 index 000000000000..c605b63b5a6b --- /dev/null +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -0,0 +1,1360 @@ +/* + * Pinctrl driver for Rockchip SoCs + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner + * + * With some ideas taken from pinctrl-samsung: + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Copyright (c) 2012 Linaro Ltd + * http://www.linaro.org + * + * and pinctrl-at91: + * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" + +/* GPIO control registers */ +#define GPIO_SWPORT_DR 0x00 +#define GPIO_SWPORT_DDR 0x04 +#define GPIO_INTEN 0x30 +#define GPIO_INTMASK 0x34 +#define GPIO_INTTYPE_LEVEL 0x38 +#define GPIO_INT_POLARITY 0x3c +#define GPIO_INT_STATUS 0x40 +#define GPIO_INT_RAWSTATUS 0x44 +#define GPIO_DEBOUNCE 0x48 +#define GPIO_PORTS_EOI 0x4c +#define GPIO_EXT_PORT 0x50 +#define GPIO_LS_SYNC 0x60 + +/** + * @reg_base: register base of the gpio bank + * @clk: clock of the gpio bank + * @irq: interrupt of the gpio bank + * @pin_base: first pin number + * @nr_pins: number of pins in this bank + * @name: name of the bank + * @bank_num: number of the bank, to account for holes + * @valid: are all necessary informations present + * @of_node: dt node of this bank + * @drvdata: common pinctrl basedata + * @domain: irqdomain of the gpio bank + * @gpio_chip: gpiolib chip + * @grange: gpio range + * @slock: spinlock for the gpio bank + */ +struct rockchip_pin_bank { + void __iomem *reg_base; + struct clk *clk; + int irq; + u32 pin_base; + u8 nr_pins; + char *name; + u8 bank_num; + bool valid; + struct device_node *of_node; + struct rockchip_pinctrl *drvdata; + struct irq_domain *domain; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range grange; + spinlock_t slock; + +}; + +#define PIN_BANK(id, pins, label) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + } + +/** + * @pull_auto: some SoCs don't allow pulls to be specified as up or down, but + * instead decide this automatically based on the pad-type. + */ +struct rockchip_pin_ctrl { + struct rockchip_pin_bank *pin_banks; + u32 nr_banks; + u32 nr_pins; + char *label; + int mux_offset; + int pull_offset; + bool pull_auto; + int pull_bank_stride; +}; + +struct rockchip_pin_config { + unsigned int func; + unsigned long *configs; + unsigned int nconfigs; +}; + +/** + * struct rockchip_pin_group: represent group of pins of a pinmux function. + * @name: name of the pin group, used to lookup the group. + * @pins: the pins included in this group. + * @npins: number of pins included in this group. + * @func: the mux function number to be programmed when selected. + * @configs: the config values to be set for each pin + * @nconfigs: number of configs for each pin + */ +struct rockchip_pin_group { + const char *name; + unsigned int npins; + unsigned int *pins; + struct rockchip_pin_config *data; +}; + +/** + * struct rockchip_pmx_func: represent a pin function. + * @name: name of the pin function, used to lookup the function. + * @groups: one or more names of pin groups that provide this function. + * @num_groups: number of groups included in @groups. + */ +struct rockchip_pmx_func { + const char *name; + const char **groups; + u8 ngroups; +}; + +struct rockchip_pinctrl { + void __iomem *reg_base; + struct device *dev; + struct rockchip_pin_ctrl *ctrl; + struct pinctrl_desc pctl; + struct pinctrl_dev *pctl_dev; + struct rockchip_pin_group *groups; + unsigned int ngroups; + struct rockchip_pmx_func *functions; + unsigned int nfunctions; +}; + +static inline struct rockchip_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) +{ + return container_of(gc, struct rockchip_pin_bank, gpio_chip); +} + +static const inline struct rockchip_pin_group *pinctrl_name_to_group( + const struct rockchip_pinctrl *info, + const char *name) +{ + const struct rockchip_pin_group *grp = NULL; + int i; + + for (i = 0; i < info->ngroups; i++) { + if (strcmp(info->groups[i].name, name)) + continue; + + grp = &info->groups[i]; + break; + } + + return grp; +} + +/* + * given a pin number that is local to a pin controller, find out the pin bank + * and the register base of the pin bank. + */ +static struct rockchip_pin_bank *pin_to_bank(struct rockchip_pinctrl *info, + unsigned pin) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + + while ((pin >= b->pin_base) && + ((b->pin_base + b->nr_pins - 1) < pin)) + b++; + + return b; +} + +static struct rockchip_pin_bank *bank_num_to_bank( + struct rockchip_pinctrl *info, + unsigned num) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + int i; + + for (i = 0; i < info->ctrl->nr_banks; i++) { + if (b->bank_num == num) + break; + + b++; + } + + if (b->bank_num != num) + return ERR_PTR(-EINVAL); + + return b; +} + +/* + * Pinctrl_ops handling + */ + +static int rockchip_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int rockchip_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, const unsigned **pins, + unsigned *npins) +{ + struct rockchip_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 int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct rockchip_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i; + + /* + * first find the group of this node and check if we need to create + * config maps for pins + */ + grp = pinctrl_name_to_group(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; + new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + devm_kfree(pctldev->dev, new_map); + return -EINVAL; + } + 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 */ + 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->data[i].configs; + new_map[i].data.configs.num_configs = grp->data[i].nconfigs; + } + + dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, (*map)->data.mux.group, map_num); + + return 0; +} + +static void rockchip_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static const struct pinctrl_ops rockchip_pctrl_ops = { + .get_groups_count = rockchip_get_groups_count, + .get_group_name = rockchip_get_group_name, + .get_group_pins = rockchip_get_group_pins, + .dt_node_to_map = rockchip_dt_node_to_map, + .dt_free_map = rockchip_dt_free_map, +}; + +/* + * Hardware access + */ + +/* + * Set a new mux function for a pin. + * + * The register is divided into the upper and lower 16 bit. When changing + * a value, the previous register value is not read and changed. Instead + * it seems the changed bits are marked in the upper 16 bit, while the + * changed value gets set in the same offset in the lower 16 bit. + * All pin settings seem to be 2 bit wide in both the upper and lower + * parts. + * @bank: pin bank to change + * @pin: pin to change + * @mux: new mux function to set + */ +static void rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl *info = bank->drvdata; + void __iomem *reg = info->reg_base + info->ctrl->mux_offset; + unsigned long flags; + u8 bit; + u32 data; + + dev_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n", + bank->bank_num, pin, mux); + + /* get basic quadrupel of mux registers and the correct reg inside */ + reg += bank->bank_num * 0x10; + reg += (pin / 8) * 4; + bit = (pin % 8) * 2; + + spin_lock_irqsave(&bank->slock, flags); + + data = (3 << (bit + 16)); + data |= (mux & 3) << bit; + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + void __iomem *reg; + u8 bit; + + /* rk3066b does support any pulls */ + if (!ctrl->pull_offset) + return PIN_CONFIG_BIAS_DISABLE; + + reg = info->reg_base + ctrl->pull_offset; + + if (ctrl->pull_auto) { + reg += bank->bank_num * ctrl->pull_bank_stride; + reg += (pin_num / 16) * 4; + bit = pin_num % 16; + + return !(readl_relaxed(reg) & BIT(bit)) + ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT + : PIN_CONFIG_BIAS_DISABLE; + } else { + dev_err(info->dev, "pull support for rk31xx not implemented\n"); + return -EIO; + } +} + +static int rockchip_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + void __iomem *reg; + unsigned long flags; + u8 bit; + u32 data; + + dev_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n", + bank->bank_num, pin_num, pull); + + /* rk3066b does support any pulls */ + if (!ctrl->pull_offset) + return pull ? -EINVAL : 0; + + reg = info->reg_base + ctrl->pull_offset; + + if (ctrl->pull_auto) { + if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && + pull != PIN_CONFIG_BIAS_DISABLE) { + dev_err(info->dev, "only PIN_DEFAULT and DISABLE allowed\n"); + return -EINVAL; + } + + reg += bank->bank_num * ctrl->pull_bank_stride; + reg += (pin_num / 16) * 4; + bit = pin_num % 16; + + spin_lock_irqsave(&bank->slock, flags); + + data = BIT(bit + 16); + if (pull == PIN_CONFIG_BIAS_DISABLE) + data |= BIT(bit); + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); + } else { + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) { + dev_err(info->dev, "pull direction (up/down) needs to be specified\n"); + return -EINVAL; + } + + dev_err(info->dev, "pull support for rk31xx not implemented\n"); + return -EIO; + } + + return 0; +} + +/* + * Pinmux_ops handling + */ + +static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev, + unsigned selector, const char * const **groups, + unsigned * const num_groups) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + const struct rockchip_pin_config *data = info->groups[group].data; + struct rockchip_pin_bank *bank; + int cnt; + + dev_dbg(info->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + /* + * for each pin in the pin group selected, program the correspoding pin + * pin function number in the config register. + */ + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, + data[cnt].func); + } + + return 0; +} + +static void rockchip_pmx_disable(struct pinctrl_dev *pctldev, + unsigned selector, unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + struct rockchip_pin_bank *bank; + int cnt; + + dev_dbg(info->dev, "disable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); + } +} + +/* + * The calls to gpio_direction_output() and gpio_direction_input() + * leads to this function call (via the pinctrl_gpio_direction_{input|output}() + * function called from the gpiolib interface). + */ +static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank; + struct gpio_chip *chip; + int pin; + u32 data; + + chip = range->gc; + bank = gc_to_pin_bank(chip); + pin = offset - chip->base; + + dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n", + offset, range->name, pin, input ? "input" : "output"); + + rockchip_set_mux(bank, pin, RK_FUNC_GPIO); + + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + /* set bit to 1 for output, 0 for input */ + if (!input) + data |= BIT(pin); + else + data &= ~BIT(pin); + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + return 0; +} + +static const struct pinmux_ops rockchip_pmx_ops = { + .get_functions_count = rockchip_pmx_get_funcs_count, + .get_function_name = rockchip_pmx_get_func_name, + .get_function_groups = rockchip_pmx_get_groups, + .enable = rockchip_pmx_enable, + .disable = rockchip_pmx_disable, + .gpio_set_direction = rockchip_pmx_gpio_set_direction, +}; + +/* + * Pinconf_ops handling + */ + +/* set the pin config settings for a specified pin */ +static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long config) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param = pinconf_to_config_param(config); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + return rockchip_set_pull(bank, pin - bank->pin_base, param); + break; + default: + return -ENOTSUPP; + break; + } + + return 0; +} + +/* get the pin config settings for a specified pin */ +static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned int pull; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + pull = rockchip_get_pull(bank, pin - bank->pin_base); + + if (pull != param) + return -EINVAL; + + *config = 0; + break; + default: + return -ENOTSUPP; + break; + } + + return 0; +} + +static const struct pinconf_ops rockchip_pinconf_ops = { + .pin_config_get = rockchip_pinconf_get, + .pin_config_set = rockchip_pinconf_set, +}; + +static const char *gpio_compat = "rockchip,gpio-bank"; + +static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, + struct device_node *np) +{ + struct device_node *child; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) + continue; + + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } +} + +static int rockchip_pinctrl_parse_groups(struct device_node *np, + struct rockchip_pin_group *grp, + struct rockchip_pinctrl *info, + u32 index) +{ + struct rockchip_pin_bank *bank; + int size; + const __be32 *list; + int num; + int i, j; + int ret; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is rockchip,pins = , + * do sanity check and calculate pins number + */ + list = of_get_property(np, "rockchip,pins", &size); + /* we do not check return since it's safe node passed down */ + size /= sizeof(*list); + if (!size || size % 4) { + dev_err(info->dev, "wrong pins number or pins and configs should be by 4\n"); + return -EINVAL; + } + + grp->npins = size / 4; + + grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + GFP_KERNEL); + grp->data = devm_kzalloc(info->dev, grp->npins * + sizeof(struct rockchip_pin_config), + GFP_KERNEL); + if (!grp->pins || !grp->data) + return -ENOMEM; + + for (i = 0, j = 0; i < size; i += 4, j++) { + const __be32 *phandle; + struct device_node *np_config; + + num = be32_to_cpu(*list++); + bank = bank_num_to_bank(info, num); + if (IS_ERR(bank)) + return PTR_ERR(bank); + + grp->pins[j] = bank->pin_base + be32_to_cpu(*list++); + grp->data[j].func = be32_to_cpu(*list++); + + phandle = list++; + if (!phandle) + return -EINVAL; + + np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); + ret = pinconf_generic_parse_dt_config(np_config, + &grp->data[j].configs, &grp->data[j].nconfigs); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_functions(struct device_node *np, + struct rockchip_pinctrl *info, + u32 index) +{ + struct device_node *child; + struct rockchip_pmx_func *func; + struct rockchip_pin_group *grp; + int ret; + static u32 grp_index; + u32 i = 0; + + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); + + func = &info->functions[index]; + + /* Initialise function */ + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) + return 0; + + 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++]; + ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + int ret; + int i; + + rockchip_pinctrl_child_count(info, np); + + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + + info->functions = devm_kzalloc(dev, info->nfunctions * + sizeof(struct rockchip_pmx_func), + GFP_KERNEL); + if (!info->functions) { + dev_err(dev, "failed to allocate memory for function list\n"); + return -EINVAL; + } + + info->groups = devm_kzalloc(dev, info->ngroups * + sizeof(struct rockchip_pin_group), + GFP_KERNEL); + if (!info->groups) { + dev_err(dev, "failed allocate memory for ping group list\n"); + return -EINVAL; + } + + i = 0; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) + continue; + ret = rockchip_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(&pdev->dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int rockchip_pinctrl_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct pinctrl_desc *ctrldesc = &info->pctl; + struct pinctrl_pin_desc *pindesc, *pdesc; + struct rockchip_pin_bank *pin_bank; + int pin, bank, ret; + int k; + + ctrldesc->name = "rockchip-pinctrl"; + ctrldesc->owner = THIS_MODULE; + ctrldesc->pctlops = &rockchip_pctrl_ops; + ctrldesc->pmxops = &rockchip_pmx_ops; + ctrldesc->confops = &rockchip_pinconf_ops; + + pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * + info->ctrl->nr_pins, GFP_KERNEL); + if (!pindesc) { + dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n"); + return -ENOMEM; + } + ctrldesc->pins = pindesc; + ctrldesc->npins = info->ctrl->nr_pins; + + pdesc = pindesc; + for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) { + pin_bank = &info->ctrl->pin_banks[bank]; + for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "%s-%d", + pin_bank->name, pin); + pdesc++; + } + } + + info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info); + if (!info->pctl_dev) { + dev_err(&pdev->dev, "could not register pinctrl driver\n"); + return -EINVAL; + } + + for (bank = 0; bank < info->ctrl->nr_banks; ++bank) { + pin_bank = &info->ctrl->pin_banks[bank]; + pin_bank->grange.name = pin_bank->name; + pin_bank->grange.id = bank; + pin_bank->grange.pin_base = pin_bank->pin_base; + pin_bank->grange.base = pin_bank->gpio_chip.base; + pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; + pin_bank->grange.gc = &pin_bank->gpio_chip; + pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange); + } + + ret = rockchip_pinctrl_parse_dt(pdev, info); + if (ret) { + pinctrl_unregister(info->pctl_dev); + return ret; + } + + return 0; +} + +/* + * GPIO handling + */ + +static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR; + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bank->slock, flags); + + data = readl(reg); + data &= ~BIT(offset); + if (value) + data |= BIT(offset); + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +/* + * Returns the level of the pin for input direction and setting of the DR + * register for output gpios. + */ +static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + u32 data; + + data = readl(bank->reg_base + GPIO_EXT_PORT); + data >>= offset; + data &= 1; + return data; +} + +/* + * gpiolib gpio_direction_input callback function. The setting of the pin + * mux function as 'gpio input' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(gc->base + offset); +} + +/* + * gpiolib gpio_direction_output callback function. The setting of the pin + * mux function as 'gpio output' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + rockchip_gpio_set(gc, offset, value); + return pinctrl_gpio_direction_output(gc->base + offset); +} + +/* + * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin + * and a virtual IRQ, if not already present. + */ +static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + unsigned int virq; + + if (!bank->domain) + return -ENXIO; + + virq = irq_create_mapping(bank->domain, offset); + + return (virq) ? : -ENXIO; +} + +static const struct gpio_chip rockchip_gpiolib_chip = { + .set = rockchip_gpio_set, + .get = rockchip_gpio_get, + .direction_input = rockchip_gpio_direction_input, + .direction_output = rockchip_gpio_direction_output, + .to_irq = rockchip_gpio_to_irq, + .owner = THIS_MODULE, +}; + +/* + * Interrupt handling + */ + +static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct rockchip_pin_bank *bank = irq_get_handler_data(irq); + u32 pend; + + dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); + + chained_irq_enter(chip, desc); + + pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); + + while (pend) { + unsigned int virq; + + irq = __ffs(pend); + pend &= ~BIT(irq); + virq = irq_linear_revmap(bank->domain, irq); + + if (!virq) { + dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq); + continue; + } + + dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq); + + generic_handle_irq(virq); + } + + chained_irq_exit(chip, desc); +} + +static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct rockchip_pin_bank *bank = gc->private; + u32 mask = BIT(d->hwirq); + u32 polarity; + u32 level; + u32 data; + + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(d->irq, handle_edge_irq); + else + __irq_set_handler_locked(d->irq, handle_level_irq); + + irq_gc_lock(gc); + + level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL); + polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + level |= mask; + polarity |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + level |= mask; + polarity &= ~mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + level &= ~mask; + polarity |= mask; + break; + case IRQ_TYPE_LEVEL_LOW: + level &= ~mask; + polarity &= ~mask; + break; + default: + return -EINVAL; + } + + writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL); + writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY); + + irq_gc_unlock(gc); + + /* make sure the pin is configured as gpio input */ + rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + data &= ~mask; + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + return 0; +} + +static int rockchip_interrupts_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + struct irq_chip_generic *gc; + int ret; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + bank->domain = irq_domain_add_linear(bank->of_node, 32, + &irq_generic_chip_ops, NULL); + if (!bank->domain) { + dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n", + bank->name); + continue; + } + + ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1, + "rockchip_gpio_irq", handle_level_irq, + clr, 0, IRQ_GC_INIT_MASK_CACHE); + if (ret) { + dev_err(&pdev->dev, "could not alloc generic chips for bank %s\n", + bank->name); + irq_domain_remove(bank->domain); + continue; + } + + gc = irq_get_domain_generic_chip(bank->domain, 0); + gc->reg_base = bank->reg_base; + gc->private = bank; + gc->chip_types[0].regs.mask = GPIO_INTEN; + gc->chip_types[0].regs.ack = GPIO_PORTS_EOI; + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; + gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type; + + irq_set_handler_data(bank->irq, bank); + irq_set_chained_handler(bank->irq, rockchip_irq_demux); + } + + return 0; +} + +static int rockchip_gpiolib_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + struct gpio_chip *gc; + int ret; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + bank->gpio_chip = rockchip_gpiolib_chip; + + gc = &bank->gpio_chip; + gc->base = bank->pin_base; + gc->ngpio = bank->nr_pins; + gc->dev = &pdev->dev; + gc->of_node = bank->of_node; + gc->label = bank->name; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", + gc->label, ret); + goto fail; + } + } + + rockchip_interrupts_register(pdev, info); + + return 0; + +fail: + for (--i, --bank; i >= 0; --i, --bank) { + if (!bank->valid) + continue; + + if (gpiochip_remove(&bank->gpio_chip)) + dev_err(&pdev->dev, "gpio chip %s remove failed\n", + bank->gpio_chip.label); + } + return ret; +} + +static int rockchip_gpiolib_unregister(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int ret = 0; + int i; + + for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) + continue; + + ret = gpiochip_remove(&bank->gpio_chip); + } + + if (ret) + dev_err(&pdev->dev, "gpio chip remove failed\n"); + + return ret; +} + +static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, + struct device *dev) +{ + struct resource res; + + if (of_address_to_resource(bank->of_node, 0, &res)) { + dev_err(dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + bank->reg_base = devm_ioremap_resource(dev, &res); + if (IS_ERR(bank->reg_base)) + return PTR_ERR(bank->reg_base); + + bank->irq = irq_of_parse_and_map(bank->of_node, 0); + + bank->clk = of_clk_get(bank->of_node, 0); + if (IS_ERR(bank->clk)) + return PTR_ERR(bank->clk); + + return clk_prepare_enable(bank->clk); +} + +static const struct of_device_id rockchip_pinctrl_dt_match[]; + +/* retrieve the soc specific data */ +static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( + struct rockchip_pinctrl *d, + struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + struct device_node *np; + struct rockchip_pin_ctrl *ctrl; + struct rockchip_pin_bank *bank; + int i; + + match = of_match_node(rockchip_pinctrl_dt_match, node); + ctrl = (struct rockchip_pin_ctrl *)match->data; + + for_each_child_of_node(node, np) { + if (!of_find_property(np, "gpio-controller", NULL)) + continue; + + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!strcmp(bank->name, np->name)) { + bank->of_node = np; + + if (!rockchip_get_bank_data(bank, &pdev->dev)) + bank->valid = true; + + break; + } + } + } + + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + spin_lock_init(&bank->slock); + bank->drvdata = d; + bank->pin_base = ctrl->nr_pins; + ctrl->nr_pins += bank->nr_pins; + } + + return ctrl; +} + +static int rockchip_pinctrl_probe(struct platform_device *pdev) +{ + struct rockchip_pinctrl *info; + struct device *dev = &pdev->dev; + struct rockchip_pin_ctrl *ctrl; + struct resource *res; + int ret; + + if (!dev->of_node) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + info = devm_kzalloc(dev, sizeof(struct rockchip_pinctrl), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ctrl = rockchip_pinctrl_get_soc_data(info, pdev); + if (!ctrl) { + dev_err(dev, "driver data not available\n"); + return -EINVAL; + } + info->ctrl = ctrl; + info->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "cannot find IO resource\n"); + return -ENOENT; + } + + info->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->reg_base)) + return PTR_ERR(info->reg_base); + + ret = rockchip_gpiolib_register(pdev, info); + if (ret) + return ret; + + ret = rockchip_pinctrl_register(pdev, info); + if (ret) { + rockchip_gpiolib_unregister(pdev, info); + return ret; + } + + platform_set_drvdata(pdev, info); + + return 0; +} + +static struct rockchip_pin_bank rk2928_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk2928_pin_ctrl = { + .pin_banks = rk2928_pin_banks, + .nr_banks = ARRAY_SIZE(rk2928_pin_banks), + .label = "RK2928-GPIO", + .mux_offset = 0xa8, + .pull_offset = 0x118, + .pull_auto = 1, + .pull_bank_stride = 8, +}; + +static struct rockchip_pin_bank rk3066a_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), + PIN_BANK(4, 32, "gpio4"), + PIN_BANK(6, 16, "gpio6"), +}; + +static struct rockchip_pin_ctrl rk3066a_pin_ctrl = { + .pin_banks = rk3066a_pin_banks, + .nr_banks = ARRAY_SIZE(rk3066a_pin_banks), + .label = "RK3066a-GPIO", + .mux_offset = 0xa8, + .pull_offset = 0x118, + .pull_auto = 1, + .pull_bank_stride = 8, +}; + +static struct rockchip_pin_bank rk3066b_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3066b_pin_ctrl = { + .pin_banks = rk3066b_pin_banks, + .nr_banks = ARRAY_SIZE(rk3066b_pin_banks), + .label = "RK3066b-GPIO", + .mux_offset = 0x60, + .pull_offset = -EINVAL, +}; + +static struct rockchip_pin_bank rk3188_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3188_pin_ctrl = { + .pin_banks = rk3188_pin_banks, + .nr_banks = ARRAY_SIZE(rk3188_pin_banks), + .label = "RK3188-GPIO", + .mux_offset = 0x68, + .pull_offset = 0x164, + .pull_bank_stride = 16, +}; + +static const struct of_device_id rockchip_pinctrl_dt_match[] = { + { .compatible = "rockchip,rk2928-pinctrl", + .data = (void *)&rk2928_pin_ctrl }, + { .compatible = "rockchip,rk3066a-pinctrl", + .data = (void *)&rk3066a_pin_ctrl }, + { .compatible = "rockchip,rk3066b-pinctrl", + .data = (void *)&rk3066b_pin_ctrl }, + { .compatible = "rockchip,rk3188-pinctrl", + .data = (void *)&rk3188_pin_ctrl }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); + +static struct platform_driver rockchip_pinctrl_driver = { + .probe = rockchip_pinctrl_probe, + .driver = { + .name = "rockchip-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rockchip_pinctrl_dt_match), + }, +}; + +static int __init rockchip_pinctrl_drv_register(void) +{ + return platform_driver_register(&rockchip_pinctrl_driver); +} +postcore_initcall(rockchip_pinctrl_drv_register); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Rockchip pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/pinctrl/rockchip.h b/include/dt-bindings/pinctrl/rockchip.h new file mode 100644 index 000000000000..cd5788be82ce --- /dev/null +++ b/include/dt-bindings/pinctrl/rockchip.h @@ -0,0 +1,32 @@ +/* + * Header providing constants for Rockchip pinctrl bindings. + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ +#define __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ + +#define RK_GPIO0 0 +#define RK_GPIO1 1 +#define RK_GPIO2 2 +#define RK_GPIO3 3 +#define RK_GPIO4 4 +#define RK_GPIO6 6 + +#define RK_FUNC_GPIO 0 +#define RK_FUNC_1 1 +#define RK_FUNC_2 2 + +#endif -- cgit v1.2.3 From a5d811bbf1c6df86cfe23948059ea614554b9f19 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 18 Jun 2013 14:33:02 +0300 Subject: pinctrl: add Intel BayTrail GPIO/pinctrl support Add support for gpio on Intel BayTrail platforms. BayTrail supports 3 banks of gpios called SCORE, NCORE ans SUS with 102, 28 and 44 gpio pins. Supports gpio interrupts and ACPI gpio events Pins may be muxed to alternate function instead of gpio by firmware. This driver does not touch the pin muxing and expect firmare to set pin muxing and pullup/down properties properly. Signed-off-by: Mathias Nyman Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 12 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-baytrail.c | 543 +++++++++++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-baytrail.c (limited to 'drivers/pinctrl/Kconfig') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 269c0406dce4..e01976fb1175 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -58,6 +58,18 @@ config PINCTRL_AT91 help Say Y here to enable the at91 pinctrl driver +config PINCTRL_BAYTRAIL + bool "Intel Baytrail GPIO pin control" + depends on GPIOLIB && ACPI && X86 + select IRQ_DOMAIN + help + driver for memory mapped GPIO functionality on Intel Baytrail + platforms. Supports 3 banks with 102, 28 and 44 gpios. + Most pins are usually muxed to some other functionality by firmware, + so only a small amount is available for gpio use. + + Requires ACPI device enumeration code to set up a platform device. + config PINCTRL_BCM2835 bool select PINMUX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index ff38aca65689..9031afddb9ad 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_PINCTRL_AB9540) += pinctrl-ab9540.o obj-$(CONFIG_PINCTRL_AB8505) += pinctrl-ab8505.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o +obj-$(CONFIG_PINCTRL_BAYTRAIL) += pinctrl-baytrail.o obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o obj-$(CONFIG_PINCTRL_IMX51) += pinctrl-imx51.o diff --git a/drivers/pinctrl/pinctrl-baytrail.c b/drivers/pinctrl/pinctrl-baytrail.c new file mode 100644 index 000000000000..e9d735dcebfb --- /dev/null +++ b/drivers/pinctrl/pinctrl-baytrail.c @@ -0,0 +1,543 @@ +/* + * Pinctrl GPIO driver for Intel Baytrail + * Copyright (c) 2012-2013, Intel Corporation. + * + * Author: Mathias Nyman + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* memory mapped register offsets */ +#define BYT_CONF0_REG 0x000 +#define BYT_CONF1_REG 0x004 +#define BYT_VAL_REG 0x008 +#define BYT_DFT_REG 0x00c +#define BYT_INT_STAT_REG 0x800 + +/* BYT_CONF0_REG register bits */ +#define BYT_TRIG_NEG BIT(26) +#define BYT_TRIG_POS BIT(25) +#define BYT_TRIG_LVL BIT(24) +#define BYT_PIN_MUX 0x07 + +/* BYT_VAL_REG register bits */ +#define BYT_INPUT_EN BIT(2) /* 0: input enabled (active low)*/ +#define BYT_OUTPUT_EN BIT(1) /* 0: output enabled (active low)*/ +#define BYT_LEVEL BIT(0) + +#define BYT_DIR_MASK (BIT(1) | BIT(2)) +#define BYT_TRIG_MASK (BIT(26) | BIT(25) | BIT(24)) + +#define BYT_NGPIO_SCORE 102 +#define BYT_NGPIO_NCORE 28 +#define BYT_NGPIO_SUS 44 + +/* + * Baytrail gpio controller consist of three separate sub-controllers called + * SCORE, NCORE and SUS. The sub-controllers are identified by their acpi UID. + * + * GPIO numbering is _not_ ordered meaning that gpio # 0 in ACPI namespace does + * _not_ correspond to the first gpio register at controller's gpio base. + * There is no logic or pattern in mapping gpio numbers to registers (pads) so + * each sub-controller needs to have its own mapping table + */ + +/* score_pins[gpio_nr] = pad_nr */ + +static unsigned const score_pins[BYT_NGPIO_SCORE] = { + 85, 89, 93, 96, 99, 102, 98, 101, 34, 37, + 36, 38, 39, 35, 40, 84, 62, 61, 64, 59, + 54, 56, 60, 55, 63, 57, 51, 50, 53, 47, + 52, 49, 48, 43, 46, 41, 45, 42, 58, 44, + 95, 105, 70, 68, 67, 66, 69, 71, 65, 72, + 86, 90, 88, 92, 103, 77, 79, 83, 78, 81, + 80, 82, 13, 12, 15, 14, 17, 18, 19, 16, + 2, 1, 0, 4, 6, 7, 9, 8, 33, 32, + 31, 30, 29, 27, 25, 28, 26, 23, 21, 20, + 24, 22, 5, 3, 10, 11, 106, 87, 91, 104, + 97, 100, +}; + +static unsigned const ncore_pins[BYT_NGPIO_NCORE] = { + 19, 18, 17, 20, 21, 22, 24, 25, 23, 16, + 14, 15, 12, 26, 27, 1, 4, 8, 11, 0, + 3, 6, 10, 13, 2, 5, 9, 7, +}; + +static unsigned const sus_pins[BYT_NGPIO_SUS] = { + 29, 33, 30, 31, 32, 34, 36, 35, 38, 37, + 18, 7, 11, 20, 17, 1, 8, 10, 19, 12, + 0, 2, 23, 39, 28, 27, 22, 21, 24, 25, + 26, 51, 56, 54, 49, 55, 48, 57, 50, 58, + 52, 53, 59, 40, +}; + +static struct pinctrl_gpio_range byt_ranges[] = { + { + .name = "1", /* match with acpi _UID in probe */ + .npins = BYT_NGPIO_SCORE, + .pins = score_pins, + }, + { + .name = "2", + .npins = BYT_NGPIO_NCORE, + .pins = ncore_pins, + }, + { + .name = "3", + .npins = BYT_NGPIO_SUS, + .pins = sus_pins, + }, + { + }, +}; + +struct byt_gpio { + struct gpio_chip chip; + struct irq_domain *domain; + struct platform_device *pdev; + spinlock_t lock; + void __iomem *reg_base; + struct pinctrl_gpio_range *range; +}; + +static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset, + int reg) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + u32 reg_offset; + void __iomem *ptr; + + if (reg == BYT_INT_STAT_REG) + reg_offset = (offset / 32) * 4; + else + reg_offset = vg->range->pins[offset] * 16; + + ptr = (void __iomem *) (vg->reg_base + reg_offset + reg); + return ptr; +} + +static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + + pm_runtime_get(&vg->pdev->dev); + + return 0; +} + +static void byt_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); + u32 value; + + /* clear interrupt triggering */ + value = readl(reg); + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + writel(value, reg); + + pm_runtime_put(&vg->pdev->dev); +} + +static int byt_irq_type(struct irq_data *d, unsigned type) +{ + struct byt_gpio *vg = irq_data_get_irq_chip_data(d); + u32 offset = irqd_to_hwirq(d); + u32 value; + unsigned long flags; + void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); + + if (offset >= vg->chip.ngpio) + return -EINVAL; + + spin_lock_irqsave(&vg->lock, flags); + value = readl(reg); + + /* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits + * are used to indicate high and low level triggering + */ + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + value |= BYT_TRIG_LVL; + case IRQ_TYPE_EDGE_RISING: + value |= BYT_TRIG_POS; + break; + case IRQ_TYPE_LEVEL_LOW: + value |= BYT_TRIG_LVL; + case IRQ_TYPE_EDGE_FALLING: + value |= BYT_TRIG_NEG; + break; + case IRQ_TYPE_EDGE_BOTH: + value |= (BYT_TRIG_NEG | BYT_TRIG_POS); + break; + } + writel(value, reg); + + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static int byt_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); + return readl(reg) & BYT_LEVEL; +} + +static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); + unsigned long flags; + u32 old_val; + + spin_lock_irqsave(&vg->lock, flags); + + old_val = readl(reg); + + if (value) + writel(old_val | BYT_LEVEL, reg); + else + writel(old_val & ~BYT_LEVEL, reg); + + spin_unlock_irqrestore(&vg->lock, flags); +} + +static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); + unsigned long flags; + u32 value; + + spin_lock_irqsave(&vg->lock, flags); + + value = readl(reg) | BYT_DIR_MASK; + value = value & (~BYT_INPUT_EN); /* active low */ + writel(value, reg); + + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static int byt_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(chip, gpio, BYT_VAL_REG); + unsigned long flags; + u32 reg_val; + + spin_lock_irqsave(&vg->lock, flags); + + reg_val = readl(reg) | (BYT_DIR_MASK | !!value); + reg_val &= ~(BYT_OUTPUT_EN | !value); + writel(reg_val, reg); + + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + int i; + unsigned long flags; + u32 conf0, val, offs; + + spin_lock_irqsave(&vg->lock, flags); + + for (i = 0; i < vg->chip.ngpio; i++) { + offs = vg->range->pins[i] * 16; + conf0 = readl(vg->reg_base + offs + BYT_CONF0_REG); + val = readl(vg->reg_base + offs + BYT_VAL_REG); + + seq_printf(s, + " gpio-%-3d %s %s %s pad-%-3d offset:0x%03x mux:%d %s%s%s\n", + i, + val & BYT_INPUT_EN ? " " : "in", + val & BYT_OUTPUT_EN ? " " : "out", + val & BYT_LEVEL ? "hi" : "lo", + vg->range->pins[i], offs, + conf0 & 0x7, + conf0 & BYT_TRIG_NEG ? "fall " : "", + conf0 & BYT_TRIG_POS ? "rise " : "", + conf0 & BYT_TRIG_LVL ? "lvl " : ""); + } + spin_unlock_irqrestore(&vg->lock, flags); +} + +static int byt_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + return irq_create_mapping(vg->domain, offset); +} + +static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct byt_gpio *vg = irq_data_get_irq_handler_data(data); + struct irq_chip *chip = irq_data_get_irq_chip(data); + u32 base, pin, mask; + void __iomem *reg; + u32 pending; + unsigned virq; + int looplimit = 0; + + /* check from GPIO controller which pin triggered the interrupt */ + for (base = 0; base < vg->chip.ngpio; base += 32) { + + reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); + + while ((pending = readl(reg))) { + pin = __ffs(pending); + mask = BIT(pin); + /* Clear before handling so we can't lose an edge */ + writel(mask, reg); + + virq = irq_find_mapping(vg->domain, base + pin); + generic_handle_irq(virq); + + /* In case bios or user sets triggering incorretly a pin + * might remain in "interrupt triggered" state. + */ + if (looplimit++ > 32) { + dev_err(&vg->pdev->dev, + "Gpio %d interrupt flood, disabling\n", + base + pin); + + reg = byt_gpio_reg(&vg->chip, base + pin, + BYT_CONF0_REG); + mask = readl(reg); + mask &= ~(BYT_TRIG_NEG | BYT_TRIG_POS | + BYT_TRIG_LVL); + writel(mask, reg); + mask = readl(reg); /* flush */ + break; + } + } + } + chip->irq_eoi(data); +} + +static void byt_irq_unmask(struct irq_data *d) +{ +} + +static void byt_irq_mask(struct irq_data *d) +{ +} + +static struct irq_chip byt_irqchip = { + .name = "BYT-GPIO", + .irq_mask = byt_irq_mask, + .irq_unmask = byt_irq_unmask, + .irq_set_type = byt_irq_type, +}; + +static void byt_gpio_irq_init_hw(struct byt_gpio *vg) +{ + void __iomem *reg; + u32 base, value; + + /* clear interrupt status trigger registers */ + for (base = 0; base < vg->chip.ngpio; base += 32) { + reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); + writel(0xffffffff, reg); + /* make sure trigger bits are cleared, if not then a pin + might be misconfigured in bios */ + value = readl(reg); + if (value) + dev_err(&vg->pdev->dev, + "GPIO interrupt error, pins misconfigured\n"); + } +} + +static int byt_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct byt_gpio *vg = d->host_data; + + irq_set_chip_and_handler_name(virq, &byt_irqchip, handle_simple_irq, + "demux"); + irq_set_chip_data(virq, vg); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static const struct irq_domain_ops byt_gpio_irq_ops = { + .map = byt_gpio_irq_map, +}; + +static int byt_gpio_probe(struct platform_device *pdev) +{ + struct byt_gpio *vg; + struct gpio_chip *gc; + struct resource *mem_rc, *irq_rc; + struct device *dev = &pdev->dev; + struct acpi_device *acpi_dev; + struct pinctrl_gpio_range *range; + acpi_handle handle = ACPI_HANDLE(dev); + unsigned hwirq; + int ret; + + if (acpi_bus_get_device(handle, &acpi_dev)) + return -ENODEV; + + vg = devm_kzalloc(dev, sizeof(struct byt_gpio), GFP_KERNEL); + if (!vg) { + dev_err(&pdev->dev, "can't allocate byt_gpio chip data\n"); + return -ENOMEM; + } + + for (range = byt_ranges; range->name; range++) { + if (!strcmp(acpi_dev->pnp.unique_id, range->name)) { + vg->chip.ngpio = range->npins; + vg->range = range; + break; + } + } + + if (!vg->chip.ngpio || !vg->range) + return -ENODEV; + + vg->pdev = pdev; + platform_set_drvdata(pdev, vg); + + mem_rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vg->reg_base = devm_ioremap_resource(dev, mem_rc); + if (IS_ERR(vg->reg_base)) + return PTR_ERR(vg->reg_base); + + spin_lock_init(&vg->lock); + + gc = &vg->chip; + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->request = byt_gpio_request; + gc->free = byt_gpio_free; + gc->direction_input = byt_gpio_direction_input; + gc->direction_output = byt_gpio_direction_output; + gc->get = byt_gpio_get; + gc->set = byt_gpio_set; + gc->dbg_show = byt_gpio_dbg_show; + gc->base = -1; + gc->can_sleep = 0; + gc->dev = dev; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed adding byt-gpio chip\n"); + return ret; + } + + /* set up interrupts */ + irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq_rc && irq_rc->start) { + hwirq = irq_rc->start; + gc->to_irq = byt_gpio_to_irq; + + vg->domain = irq_domain_add_linear(NULL, gc->ngpio, + &byt_gpio_irq_ops, vg); + if (!vg->domain) + return -ENXIO; + + byt_gpio_irq_init_hw(vg); + + irq_set_handler_data(hwirq, vg); + irq_set_chained_handler(hwirq, byt_gpio_irq_handler); + + /* Register interrupt handlers for gpio signaled acpi events */ + acpi_gpiochip_request_interrupts(gc); + } + + pm_runtime_enable(dev); + + return 0; +} + +static int byt_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int byt_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops byt_gpio_pm_ops = { + .runtime_suspend = byt_gpio_runtime_suspend, + .runtime_resume = byt_gpio_runtime_resume, +}; + +static const struct acpi_device_id byt_gpio_acpi_match[] = { + { "INT33B2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, byt_gpio_acpi_match); + +static int byt_gpio_remove(struct platform_device *pdev) +{ + struct byt_gpio *vg = platform_get_drvdata(pdev); + int err; + pm_runtime_disable(&pdev->dev); + err = gpiochip_remove(&vg->chip); + if (err) + dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); + + return 0; +} + +static struct platform_driver byt_gpio_driver = { + .probe = byt_gpio_probe, + .remove = byt_gpio_remove, + .driver = { + .name = "byt_gpio", + .owner = THIS_MODULE, + .pm = &byt_gpio_pm_ops, + .acpi_match_table = ACPI_PTR(byt_gpio_acpi_match), + }, +}; + +static int __init byt_gpio_init(void) +{ + return platform_driver_register(&byt_gpio_driver); +} + +subsys_initcall(byt_gpio_init); -- cgit v1.2.3 From d5025f9f535108ba44c7f00573a2a34c18a3ea2b Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 20 Jun 2013 10:26:27 +0100 Subject: pinctrl-tz1090: add TZ1090 pinctrl driver Add a pin control driver for the main pins on the TZ1090 SoC. This doesn't include the low-power pins as they're controlled separately via the Powerdown Controller (PDC) registers. Signed-off-by: James Hogan Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Cc: Linus Walleij Cc: linux-doc@vger.kernel.org Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Linus Walleij --- .../bindings/pinctrl/img,tz1090-pinctrl.txt | 232 +++ drivers/pinctrl/Kconfig | 6 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-tz1090.c | 2077 ++++++++++++++++++++ 4 files changed, 2316 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-tz1090.c (limited to 'drivers/pinctrl/Kconfig') diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt new file mode 100644 index 000000000000..39bfd9cd6bba --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt @@ -0,0 +1,232 @@ +ImgTec TZ1090 pin controller + +Required properties: +- compatible: "img,tz1090-pinctrl" +- reg: Should contain the register physical address and length of the pad + configuration registers (CR_PADS_* and CR_IF_CTL0). + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +TZ1090's pin configuration nodes act as a container for an abitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, drive strength, etc. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. For this reason, even seemingly boolean +values are actually tristates in this binding: unspecified, off, or on. +Unspecified is represented as an absent property, and off/on are represented as +integer values 0 and 1. + +Required subnode-properties: +- tz1090,pins : An array of strings. Each string contains the name of a pin or + group. Valid values for these names are listed below. + +Optional subnode-properties: +- tz1090,function: A string containing the name of the function to mux to the + pin or group. Valid values for function names are listed below, including + which pingroups can be muxed to them. +- supported generic pinconfig properties (for further details see + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt): + - bias-disable + - bias-high-impedance + - bias-bus-hold + - bias-pull-up + - bias-pull-down + - input-schmitt-enable + - input-schmitt-disable + - slew-rate: Integer, control slew rate of pins. + 0: slow (half frequency) + 1: fast + - drive-strength: Integer, control drive strength of pins in mA. + 2: 2mA + 4: 4mA + 8: 8mA + 12: 12mA + + +Note that many of these properties are only valid for certain specific pins +or groups. See the TZ1090 TRM for complete details regarding which groups +support which functionality. The Linux pinctrl driver may also be a useful +reference. + +Valid values for pin and group names are: + + gpio pins: + + These all support bias-high-impediance, bias-pull-up, bias-pull-down, and + bias-bus-hold (which can also be provided to any of the groups below to set + it for all pins in that group). + + They also all support the some form of muxing. Any pins which are contained + in one of the mux groups (see below) can be muxed only to the functions + supported by the mux group. All other pins can be muxed to the "perip" + function which which enables them with their intended peripheral. + + Different pins in the same mux group cannot be muxed to different functions, + however it is possible to mux only a subset of the pins in a mux group to a + particular function and leave the remaining pins unmuxed. This is useful if + the board connects certain pins in a group to other devices to be controlled + by GPIO, and you don't want the usual peripheral to have any control of the + pin. + + ant_sel0, ant_sel1, gain0, gain1, gain2, gain3, gain4, gain5, gain6, gain7, + i2s_bclk_out, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, i2s_lrclk_out, + i2s_mclk, pa_on, pdm_a, pdm_b, pdm_c, pdm_d, pll_on, rx_hp, rx_on, + scb0_sclk, scb0_sdat, scb1_sclk, scb1_sdat, scb2_sclk, scb2_sdat, sdh_cd, + sdh_clk_in, sdh_wp, sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3, + spi0_cs0, spi0_cs1, spi0_cs2, spi0_din, spi0_dout, spi0_mclk, spi1_cs0, + spi1_cs1, spi1_cs2, spi1_din, spi1_dout, spi1_mclk, tft_blank_ls, tft_blue0, + tft_blue1, tft_blue2, tft_blue3, tft_blue4, tft_blue5, tft_blue6, tft_blue7, + tft_green0, tft_green1, tft_green2, tft_green3, tft_green4, tft_green5, + tft_green6, tft_green7, tft_hsync_nr, tft_panelclk, tft_pwrsave, tft_red0, + tft_red1, tft_red2, tft_red3, tft_red4, tft_red5, tft_red6, tft_red7, + tft_vd12acb, tft_vdden_gd, tft_vsync_ns, tx_on, uart0_cts, uart0_rts, + uart0_rxd, uart0_txd, uart1_rxd, uart1_txd. + + bias-high-impediance: supported. + bias-pull-up: supported. + bias-pull-down: supported. + bias-bus-hold: supported. + function: perip or those supported by pin's mux group. + + other pins: + + These other pins are part of various pin groups below, but can't be + controlled as GPIOs. They do however support bias-high-impediance, + bias-pull-up, bias-pull-down, and bias-bus-hold (which can also be provided + to any of the groups below to set it for all pins in that group). + + clk_out0, clk_out1, tck, tdi, tdo, tms, trst. + + bias-high-impediance: supported. + bias-pull-up: supported. + bias-pull-down: supported. + bias-bus-hold: supported. + + mux groups: + + These all support function, and some support drive configs. + + afe + pins: tx_on, rx_on, pll_on, pa_on, rx_hp, ant_sel0, + ant_sel1, gain0, gain1, gain2, gain3, gain4, + gain5, gain6, gain7. + function: afe, ts_out_0. + input-schmitt-enable: supported. + input-schmitt-disable: supported. + slew-rate: supported. + drive-strength: supported. + pdm_d + pins: pdm_d. + function: pdm_dac, usb_vbus. + sdh + pins: sdh_cd, sdh_wp, sdh_clk_in. + function: sdh, sdio. + sdio + pins: sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, + sdio_d3. + function: sdio, sdh. + spi1_cs2 + pins: spi1_cs2. + function: spi1_cs2, usb_vbus. + tft + pins: tft_red0, tft_red1, tft_red2, tft_red3, + tft_red4, tft_red5, tft_red6, tft_red7, + tft_green0, tft_green1, tft_green2, tft_green3, + tft_green4, tft_green5, tft_green6, tft_green7, + tft_blue0, tft_blue1, tft_blue2, tft_blue3, + tft_blue4, tft_blue5, tft_blue6, tft_blue7, + tft_vdden_gd, tft_panelclk, tft_blank_ls, + tft_vsync_ns, tft_hsync_nr, tft_vd12acb, + tft_pwrsave. + function: tft, ext_dac, not_iqadc_stb, iqdac_stb, ts_out_1, + lcd_trace, phy_ringosc. + input-schmitt-enable: supported. + input-schmitt-disable: supported. + slew-rate: supported. + drive-strength: supported. + + drive groups: + + These all support input-schmitt-enable, input-schmitt-disable, slew-rate, + and drive-strength. + + jtag + pins: tck, trst, tdi, tdo, tms. + scb1 + pins: scb1_sdat, scb1_sclk. + scb2 + pins: scb2_sdat, scb2_sclk. + spi0 + pins: spi0_mclk, spi0_cs0, spi0_cs1, spi0_cs2, spi0_dout, spi0_din. + spi1 + pins: spi1_mclk, spi1_cs0, spi1_cs1, spi1_cs2, spi1_dout, spi1_din. + uart + pins: uart0_txd, uart0_rxd, uart0_rts, uart0_cts, + uart1_txd, uart1_rxd. + drive_i2s + pins: clk_out1, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, + i2s_lrclk_out, i2s_bclk_out, i2s_mclk. + drive_pdm + pins: clk_out0, pdm_b, pdm_a. + drive_scb0 + pins: scb0_sclk, scb0_sdat, pdm_d, pdm_c. + drive_sdio + pins: sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3, + sdh_wp, sdh_cd, sdh_clk_in. + + convenience groups: + + These are just convenient groupings of pins and don't support any drive + configs. + + uart0 + pins: uart0_cts, uart0_rts, uart0_rxd, uart0_txd. + uart1 + pins: uart1_rxd, uart1_txd. + scb0 + pins: scb0_sclk, scb0_sdat. + i2s + pins: i2s_bclk_out, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, + i2s_lrclk_out, i2s_mclk. + +Example: + + pinctrl: pinctrl@02005800 { + #gpio-range-cells = <3>; + compatible = "img,tz1090-pinctrl"; + reg = <0x02005800 0xe4>; + }; + +Example board file extract: + + &pinctrl { + uart0_default: uart0 { + uart0_cfg { + tz1090,pins = "uart0_rxd", + "uart0_txd"; + tz1090,function = "perip"; + }; + }; + tft_default: tft { + tft_cfg { + tz1090,pins = "tft"; + tz1090,function = "tft"; + }; + }; + }; + + uart@02004b00 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_default>; + }; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index e01976fb1175..acdaa08b325c 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -212,6 +212,12 @@ config PINCTRL_TEGRA114 bool select PINCTRL_TEGRA +config PINCTRL_TZ1090 + bool "Toumaz Xenif TZ1090 pin control driver" + depends on SOC_TZ1090 + select PINMUX + select GENERIC_PINCONF + config PINCTRL_U300 bool "U300 pin controller driver" depends on ARCH_U300 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 9031afddb9ad..37ff29e1bba3 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o +obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c new file mode 100644 index 000000000000..02ff3a238168 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tz1090.c @@ -0,0 +1,2077 @@ +/* + * Pinctrl driver for the Toumaz Xenif TZ1090 SoC + * + * Copyright (c) 2013, Imagination Technologies Ltd. + * + * Derived from Tegra code: + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * + * Derived from code: + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2009-2011 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The registers may be shared with other threads/cores, so we need to use the + * metag global lock2 for atomicity. + */ +#include + +#include "core.h" +#include "pinconf.h" + +/* Register offsets from bank base address */ +#define REG_PINCTRL_SELECT 0x10 +#define REG_PINCTRL_SCHMITT 0x90 +#define REG_PINCTRL_PU_PD 0xa0 +#define REG_PINCTRL_SR 0xc0 +#define REG_PINCTRL_DR 0xd0 +#define REG_PINCTRL_IF_CTL 0xe0 + +/* REG_PINCTRL_PU_PD field values */ +#define REG_PU_PD_TRISTATE 0 +#define REG_PU_PD_UP 1 +#define REG_PU_PD_DOWN 2 +#define REG_PU_PD_REPEATER 3 + +/* REG_PINCTRL_DR field values */ +#define REG_DR_2mA 0 +#define REG_DR_4mA 1 +#define REG_DR_8mA 2 +#define REG_DR_12mA 3 + +/** + * struct tz1090_function - TZ1090 pinctrl mux function + * @name: The name of the function, exported to pinctrl core. + * @groups: An array of pin groups that may select this function. + * @ngroups: The number of entries in @groups. + */ +struct tz1090_function { + const char *name; + const char * const *groups; + unsigned int ngroups; +}; + +/** + * struct tz1090_muxdesc - TZ1090 individual mux description + * @funcs: Function for each mux value. + * @reg: Mux register offset. 0 if unsupported. + * @bit: Mux register bit. 0 if unsupported. + * @width: Mux field width. 0 if unsupported. + * + * A representation of a group of signals (possibly just one signal) in the + * TZ1090 which can be muxed to a set of functions or sub muxes. + */ +struct tz1090_muxdesc { + int funcs[5]; + u16 reg; + u8 bit; + u8 width; +}; + +/** + * struct tz1090_pingroup - TZ1090 pin group + * @name: Name of pin group. + * @pins: Array of pin numbers in this pin group. + * @npins: Number of pins in this pin group. + * @mux: Top level mux. + * @drv: Drive control supported, 0 if unsupported. + * This means Schmitt, Slew, and Drive strength. + * @slw_bit: Slew register bit. 0 if unsupported. + * The same bit is used for Schmitt, and Drive (*2). + * @func: Currently muxed function. + * @func_count: Number of pins using current mux function. + * + * A representation of a group of pins (possibly just one pin) in the TZ1090 + * pin controller. Each group allows some parameter or parameters to be + * configured. The most common is mux function selection. + */ +struct tz1090_pingroup { + const char *name; + const unsigned int *pins; + unsigned int npins; + struct tz1090_muxdesc mux; + + bool drv; + u8 slw_bit; + + int func; + unsigned int func_count; +}; + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ + +enum tz1090_pin { + /* GPIO pins */ + TZ1090_PIN_SDIO_CLK, + TZ1090_PIN_SDIO_CMD, + TZ1090_PIN_SDIO_D0, + TZ1090_PIN_SDIO_D1, + TZ1090_PIN_SDIO_D2, + TZ1090_PIN_SDIO_D3, + TZ1090_PIN_SDH_CD, + TZ1090_PIN_SDH_WP, + TZ1090_PIN_SPI0_MCLK, + TZ1090_PIN_SPI0_CS0, + TZ1090_PIN_SPI0_CS1, + TZ1090_PIN_SPI0_CS2, + TZ1090_PIN_SPI0_DOUT, + TZ1090_PIN_SPI0_DIN, + TZ1090_PIN_SPI1_MCLK, + TZ1090_PIN_SPI1_CS0, + TZ1090_PIN_SPI1_CS1, + TZ1090_PIN_SPI1_CS2, + TZ1090_PIN_SPI1_DOUT, + TZ1090_PIN_SPI1_DIN, + TZ1090_PIN_UART0_RXD, + TZ1090_PIN_UART0_TXD, + TZ1090_PIN_UART0_CTS, + TZ1090_PIN_UART0_RTS, + TZ1090_PIN_UART1_RXD, + TZ1090_PIN_UART1_TXD, + TZ1090_PIN_SCB0_SDAT, + TZ1090_PIN_SCB0_SCLK, + TZ1090_PIN_SCB1_SDAT, + TZ1090_PIN_SCB1_SCLK, + TZ1090_PIN_SCB2_SDAT, + TZ1090_PIN_SCB2_SCLK, + TZ1090_PIN_I2S_MCLK, + TZ1090_PIN_I2S_BCLK_OUT, + TZ1090_PIN_I2S_LRCLK_OUT, + TZ1090_PIN_I2S_DOUT0, + TZ1090_PIN_I2S_DOUT1, + TZ1090_PIN_I2S_DOUT2, + TZ1090_PIN_I2S_DIN, + TZ1090_PIN_PDM_A, + TZ1090_PIN_PDM_B, + TZ1090_PIN_PDM_C, + TZ1090_PIN_PDM_D, + TZ1090_PIN_TFT_RED0, + TZ1090_PIN_TFT_RED1, + TZ1090_PIN_TFT_RED2, + TZ1090_PIN_TFT_RED3, + TZ1090_PIN_TFT_RED4, + TZ1090_PIN_TFT_RED5, + TZ1090_PIN_TFT_RED6, + TZ1090_PIN_TFT_RED7, + TZ1090_PIN_TFT_GREEN0, + TZ1090_PIN_TFT_GREEN1, + TZ1090_PIN_TFT_GREEN2, + TZ1090_PIN_TFT_GREEN3, + TZ1090_PIN_TFT_GREEN4, + TZ1090_PIN_TFT_GREEN5, + TZ1090_PIN_TFT_GREEN6, + TZ1090_PIN_TFT_GREEN7, + TZ1090_PIN_TFT_BLUE0, + TZ1090_PIN_TFT_BLUE1, + TZ1090_PIN_TFT_BLUE2, + TZ1090_PIN_TFT_BLUE3, + TZ1090_PIN_TFT_BLUE4, + TZ1090_PIN_TFT_BLUE5, + TZ1090_PIN_TFT_BLUE6, + TZ1090_PIN_TFT_BLUE7, + TZ1090_PIN_TFT_VDDEN_GD, + TZ1090_PIN_TFT_PANELCLK, + TZ1090_PIN_TFT_BLANK_LS, + TZ1090_PIN_TFT_VSYNC_NS, + TZ1090_PIN_TFT_HSYNC_NR, + TZ1090_PIN_TFT_VD12ACB, + TZ1090_PIN_TFT_PWRSAVE, + TZ1090_PIN_TX_ON, + TZ1090_PIN_RX_ON, + TZ1090_PIN_PLL_ON, + TZ1090_PIN_PA_ON, + TZ1090_PIN_RX_HP, + TZ1090_PIN_GAIN0, + TZ1090_PIN_GAIN1, + TZ1090_PIN_GAIN2, + TZ1090_PIN_GAIN3, + TZ1090_PIN_GAIN4, + TZ1090_PIN_GAIN5, + TZ1090_PIN_GAIN6, + TZ1090_PIN_GAIN7, + TZ1090_PIN_ANT_SEL0, + TZ1090_PIN_ANT_SEL1, + TZ1090_PIN_SDH_CLK_IN, + + /* Non-GPIO pins */ + TZ1090_PIN_TCK, + TZ1090_PIN_TRST, + TZ1090_PIN_TDI, + TZ1090_PIN_TDO, + TZ1090_PIN_TMS, + TZ1090_PIN_CLK_OUT0, + TZ1090_PIN_CLK_OUT1, + + NUM_GPIOS = TZ1090_PIN_TCK, +}; + +/* Pin names */ + +static const struct pinctrl_pin_desc tz1090_pins[] = { + /* GPIO pins */ + PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"), + PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D0, "sdio_d0"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D1, "sdio_d1"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D2, "sdio_d2"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D3, "sdio_d3"), + PINCTRL_PIN(TZ1090_PIN_SDH_CD, "sdh_cd"), + PINCTRL_PIN(TZ1090_PIN_SDH_WP, "sdh_wp"), + PINCTRL_PIN(TZ1090_PIN_SPI0_MCLK, "spi0_mclk"), + PINCTRL_PIN(TZ1090_PIN_SPI0_CS0, "spi0_cs0"), + PINCTRL_PIN(TZ1090_PIN_SPI0_CS1, "spi0_cs1"), + PINCTRL_PIN(TZ1090_PIN_SPI0_CS2, "spi0_cs2"), + PINCTRL_PIN(TZ1090_PIN_SPI0_DOUT, "spi0_dout"), + PINCTRL_PIN(TZ1090_PIN_SPI0_DIN, "spi0_din"), + PINCTRL_PIN(TZ1090_PIN_SPI1_MCLK, "spi1_mclk"), + PINCTRL_PIN(TZ1090_PIN_SPI1_CS0, "spi1_cs0"), + PINCTRL_PIN(TZ1090_PIN_SPI1_CS1, "spi1_cs1"), + PINCTRL_PIN(TZ1090_PIN_SPI1_CS2, "spi1_cs2"), + PINCTRL_PIN(TZ1090_PIN_SPI1_DOUT, "spi1_dout"), + PINCTRL_PIN(TZ1090_PIN_SPI1_DIN, "spi1_din"), + PINCTRL_PIN(TZ1090_PIN_UART0_RXD, "uart0_rxd"), + PINCTRL_PIN(TZ1090_PIN_UART0_TXD, "uart0_txd"), + PINCTRL_PIN(TZ1090_PIN_UART0_CTS, "uart0_cts"), + PINCTRL_PIN(TZ1090_PIN_UART0_RTS, "uart0_rts"), + PINCTRL_PIN(TZ1090_PIN_UART1_RXD, "uart1_rxd"), + PINCTRL_PIN(TZ1090_PIN_UART1_TXD, "uart1_txd"), + PINCTRL_PIN(TZ1090_PIN_SCB0_SDAT, "scb0_sdat"), + PINCTRL_PIN(TZ1090_PIN_SCB0_SCLK, "scb0_sclk"), + PINCTRL_PIN(TZ1090_PIN_SCB1_SDAT, "scb1_sdat"), + PINCTRL_PIN(TZ1090_PIN_SCB1_SCLK, "scb1_sclk"), + PINCTRL_PIN(TZ1090_PIN_SCB2_SDAT, "scb2_sdat"), + PINCTRL_PIN(TZ1090_PIN_SCB2_SCLK, "scb2_sclk"), + PINCTRL_PIN(TZ1090_PIN_I2S_MCLK, "i2s_mclk"), + PINCTRL_PIN(TZ1090_PIN_I2S_BCLK_OUT, "i2s_bclk_out"), + PINCTRL_PIN(TZ1090_PIN_I2S_LRCLK_OUT, "i2s_lrclk_out"), + PINCTRL_PIN(TZ1090_PIN_I2S_DOUT0, "i2s_dout0"), + PINCTRL_PIN(TZ1090_PIN_I2S_DOUT1, "i2s_dout1"), + PINCTRL_PIN(TZ1090_PIN_I2S_DOUT2, "i2s_dout2"), + PINCTRL_PIN(TZ1090_PIN_I2S_DIN, "i2s_din"), + PINCTRL_PIN(TZ1090_PIN_PDM_A, "pdm_a"), + PINCTRL_PIN(TZ1090_PIN_PDM_B, "pdm_b"), + PINCTRL_PIN(TZ1090_PIN_PDM_C, "pdm_c"), + PINCTRL_PIN(TZ1090_PIN_PDM_D, "pdm_d"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED0, "tft_red0"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED1, "tft_red1"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED2, "tft_red2"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED3, "tft_red3"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED4, "tft_red4"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED5, "tft_red5"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED6, "tft_red6"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED7, "tft_red7"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN0, "tft_green0"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN1, "tft_green1"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN2, "tft_green2"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN3, "tft_green3"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN4, "tft_green4"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN5, "tft_green5"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN6, "tft_green6"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN7, "tft_green7"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE0, "tft_blue0"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE1, "tft_blue1"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE2, "tft_blue2"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE3, "tft_blue3"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE4, "tft_blue4"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE5, "tft_blue5"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE6, "tft_blue6"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE7, "tft_blue7"), + PINCTRL_PIN(TZ1090_PIN_TFT_VDDEN_GD, "tft_vdden_gd"), + PINCTRL_PIN(TZ1090_PIN_TFT_PANELCLK, "tft_panelclk"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLANK_LS, "tft_blank_ls"), + PINCTRL_PIN(TZ1090_PIN_TFT_VSYNC_NS, "tft_vsync_ns"), + PINCTRL_PIN(TZ1090_PIN_TFT_HSYNC_NR, "tft_hsync_nr"), + PINCTRL_PIN(TZ1090_PIN_TFT_VD12ACB, "tft_vd12acb"), + PINCTRL_PIN(TZ1090_PIN_TFT_PWRSAVE, "tft_pwrsave"), + PINCTRL_PIN(TZ1090_PIN_TX_ON, "tx_on"), + PINCTRL_PIN(TZ1090_PIN_RX_ON, "rx_on"), + PINCTRL_PIN(TZ1090_PIN_PLL_ON, "pll_on"), + PINCTRL_PIN(TZ1090_PIN_PA_ON, "pa_on"), + PINCTRL_PIN(TZ1090_PIN_RX_HP, "rx_hp"), + PINCTRL_PIN(TZ1090_PIN_GAIN0, "gain0"), + PINCTRL_PIN(TZ1090_PIN_GAIN1, "gain1"), + PINCTRL_PIN(TZ1090_PIN_GAIN2, "gain2"), + PINCTRL_PIN(TZ1090_PIN_GAIN3, "gain3"), + PINCTRL_PIN(TZ1090_PIN_GAIN4, "gain4"), + PINCTRL_PIN(TZ1090_PIN_GAIN5, "gain5"), + PINCTRL_PIN(TZ1090_PIN_GAIN6, "gain6"), + PINCTRL_PIN(TZ1090_PIN_GAIN7, "gain7"), + PINCTRL_PIN(TZ1090_PIN_ANT_SEL0, "ant_sel0"), + PINCTRL_PIN(TZ1090_PIN_ANT_SEL1, "ant_sel1"), + PINCTRL_PIN(TZ1090_PIN_SDH_CLK_IN, "sdh_clk_in"), + + /* Non-GPIO pins */ + PINCTRL_PIN(TZ1090_PIN_TCK, "tck"), + PINCTRL_PIN(TZ1090_PIN_TRST, "trst"), + PINCTRL_PIN(TZ1090_PIN_TDI, "tdi"), + PINCTRL_PIN(TZ1090_PIN_TDO, "tdo"), + PINCTRL_PIN(TZ1090_PIN_TMS, "tms"), + PINCTRL_PIN(TZ1090_PIN_CLK_OUT0, "clk_out0"), + PINCTRL_PIN(TZ1090_PIN_CLK_OUT1, "clk_out1"), +}; + +/* Pins in each pin group */ + +static const unsigned int spi1_cs2_pins[] = { + TZ1090_PIN_SPI1_CS2, +}; + +static const unsigned int pdm_d_pins[] = { + TZ1090_PIN_PDM_D, +}; + +static const unsigned int tft_pins[] = { + TZ1090_PIN_TFT_RED0, + TZ1090_PIN_TFT_RED1, + TZ1090_PIN_TFT_RED2, + TZ1090_PIN_TFT_RED3, + TZ1090_PIN_TFT_RED4, + TZ1090_PIN_TFT_RED5, + TZ1090_PIN_TFT_RED6, + TZ1090_PIN_TFT_RED7, + TZ1090_PIN_TFT_GREEN0, + TZ1090_PIN_TFT_GREEN1, + TZ1090_PIN_TFT_GREEN2, + TZ1090_PIN_TFT_GREEN3, + TZ1090_PIN_TFT_GREEN4, + TZ1090_PIN_TFT_GREEN5, + TZ1090_PIN_TFT_GREEN6, + TZ1090_PIN_TFT_GREEN7, + TZ1090_PIN_TFT_BLUE0, + TZ1090_PIN_TFT_BLUE1, + TZ1090_PIN_TFT_BLUE2, + TZ1090_PIN_TFT_BLUE3, + TZ1090_PIN_TFT_BLUE4, + TZ1090_PIN_TFT_BLUE5, + TZ1090_PIN_TFT_BLUE6, + TZ1090_PIN_TFT_BLUE7, + TZ1090_PIN_TFT_VDDEN_GD, + TZ1090_PIN_TFT_PANELCLK, + TZ1090_PIN_TFT_BLANK_LS, + TZ1090_PIN_TFT_VSYNC_NS, + TZ1090_PIN_TFT_HSYNC_NR, + TZ1090_PIN_TFT_VD12ACB, + TZ1090_PIN_TFT_PWRSAVE, +}; + +static const unsigned int afe_pins[] = { + TZ1090_PIN_TX_ON, + TZ1090_PIN_RX_ON, + TZ1090_PIN_PLL_ON, + TZ1090_PIN_PA_ON, + TZ1090_PIN_RX_HP, + TZ1090_PIN_ANT_SEL0, + TZ1090_PIN_ANT_SEL1, + TZ1090_PIN_GAIN0, + TZ1090_PIN_GAIN1, + TZ1090_PIN_GAIN2, + TZ1090_PIN_GAIN3, + TZ1090_PIN_GAIN4, + TZ1090_PIN_GAIN5, + TZ1090_PIN_GAIN6, + TZ1090_PIN_GAIN7, +}; + +static const unsigned int sdio_pins[] = { + TZ1090_PIN_SDIO_CLK, + TZ1090_PIN_SDIO_CMD, + TZ1090_PIN_SDIO_D0, + TZ1090_PIN_SDIO_D1, + TZ1090_PIN_SDIO_D2, + TZ1090_PIN_SDIO_D3, +}; + +static const unsigned int sdh_pins[] = { + TZ1090_PIN_SDH_CD, + TZ1090_PIN_SDH_WP, + TZ1090_PIN_SDH_CLK_IN, +}; + +static const unsigned int spi0_pins[] = { + TZ1090_PIN_SPI0_MCLK, + TZ1090_PIN_SPI0_CS0, + TZ1090_PIN_SPI0_CS1, + TZ1090_PIN_SPI0_CS2, + TZ1090_PIN_SPI0_DOUT, + TZ1090_PIN_SPI0_DIN, +}; + +static const unsigned int spi1_pins[] = { + TZ1090_PIN_SPI1_MCLK, + TZ1090_PIN_SPI1_CS0, + TZ1090_PIN_SPI1_CS1, + TZ1090_PIN_SPI1_CS2, + TZ1090_PIN_SPI1_DOUT, + TZ1090_PIN_SPI1_DIN, +}; + +static const unsigned int uart0_pins[] = { + TZ1090_PIN_UART0_RTS, + TZ1090_PIN_UART0_CTS, + TZ1090_PIN_UART0_TXD, + TZ1090_PIN_UART0_RXD, +}; + +static const unsigned int uart1_pins[] = { + TZ1090_PIN_UART1_TXD, + TZ1090_PIN_UART1_RXD, +}; + +static const unsigned int uart_pins[] = { + TZ1090_PIN_UART1_TXD, + TZ1090_PIN_UART1_RXD, + TZ1090_PIN_UART0_RTS, + TZ1090_PIN_UART0_CTS, + TZ1090_PIN_UART0_TXD, + TZ1090_PIN_UART0_RXD, +}; + +static const unsigned int scb0_pins[] = { + TZ1090_PIN_SCB0_SDAT, + TZ1090_PIN_SCB0_SCLK, +}; + +static const unsigned int scb1_pins[] = { + TZ1090_PIN_SCB1_SDAT, + TZ1090_PIN_SCB1_SCLK, +}; + +static const unsigned int scb2_pins[] = { + TZ1090_PIN_SCB2_SDAT, + TZ1090_PIN_SCB2_SCLK, +}; + +static const unsigned int i2s_pins[] = { + TZ1090_PIN_I2S_MCLK, + TZ1090_PIN_I2S_BCLK_OUT, + TZ1090_PIN_I2S_LRCLK_OUT, + TZ1090_PIN_I2S_DOUT0, + TZ1090_PIN_I2S_DOUT1, + TZ1090_PIN_I2S_DOUT2, + TZ1090_PIN_I2S_DIN, +}; + +static const unsigned int jtag_pins[] = { + TZ1090_PIN_TCK, + TZ1090_PIN_TRST, + TZ1090_PIN_TDI, + TZ1090_PIN_TDO, + TZ1090_PIN_TMS, +}; + +/* Pins in each drive pin group */ + +static const unsigned int drive_sdio_pins[] = { + TZ1090_PIN_SDIO_CLK, + TZ1090_PIN_SDIO_CMD, + TZ1090_PIN_SDIO_D0, + TZ1090_PIN_SDIO_D1, + TZ1090_PIN_SDIO_D2, + TZ1090_PIN_SDIO_D3, + TZ1090_PIN_SDH_WP, + TZ1090_PIN_SDH_CD, + TZ1090_PIN_SDH_CLK_IN, +}; + +static const unsigned int drive_i2s_pins[] = { + TZ1090_PIN_CLK_OUT1, + TZ1090_PIN_I2S_DIN, + TZ1090_PIN_I2S_DOUT0, + TZ1090_PIN_I2S_DOUT1, + TZ1090_PIN_I2S_DOUT2, + TZ1090_PIN_I2S_LRCLK_OUT, + TZ1090_PIN_I2S_BCLK_OUT, + TZ1090_PIN_I2S_MCLK, +}; + +static const unsigned int drive_scb0_pins[] = { + TZ1090_PIN_SCB0_SCLK, + TZ1090_PIN_SCB0_SDAT, + TZ1090_PIN_PDM_D, + TZ1090_PIN_PDM_C, +}; + +static const unsigned int drive_pdm_pins[] = { + TZ1090_PIN_CLK_OUT0, + TZ1090_PIN_PDM_B, + TZ1090_PIN_PDM_A, +}; + +/* Pin groups each function can be muxed to */ + +/* + * The magic "perip" function allows otherwise non-muxing pins to be enabled in + * peripheral mode. + */ +static const char * const perip_groups[] = { + /* non-muxing convenient gpio pingroups */ + "uart", + "uart0", + "uart1", + "spi0", + "spi1", + "scb0", + "scb1", + "scb2", + "i2s", + /* individual pins not part of a pin mux group */ + "spi0_mclk", + "spi0_cs0", + "spi0_cs1", + "spi0_cs2", + "spi0_dout", + "spi0_din", + "spi1_mclk", + "spi1_cs0", + "spi1_cs1", + "spi1_dout", + "spi1_din", + "uart0_rxd", + "uart0_txd", + "uart0_cts", + "uart0_rts", + "uart1_rxd", + "uart1_txd", + "scb0_sdat", + "scb0_sclk", + "scb1_sdat", + "scb1_sclk", + "scb2_sdat", + "scb2_sclk", + "i2s_mclk", + "i2s_bclk_out", + "i2s_lrclk_out", + "i2s_dout0", + "i2s_dout1", + "i2s_dout2", + "i2s_din", + "pdm_a", + "pdm_b", + "pdm_c", +}; + +static const char * const sdh_sdio_groups[] = { + "sdh", + "sdio", + /* sdh pins */ + "sdh_cd", + "sdh_wp", + "sdh_clk_in", + /* sdio pins */ + "sdio_clk", + "sdio_cmd", + "sdio_d0", + "sdio_d1", + "sdio_d2", + "sdio_d3", +}; + +static const char * const spi1_cs2_groups[] = { + "spi1_cs2", +}; + +static const char * const pdm_dac_groups[] = { + "pdm_d", +}; + +static const char * const usb_vbus_groups[] = { + "spi1_cs2", + "pdm_d", +}; + +static const char * const afe_groups[] = { + "afe", + /* afe pins */ + "tx_on", + "rx_on", + "pll_on", + "pa_on", + "rx_hp", + "ant_sel0", + "ant_sel1", + "gain0", + "gain1", + "gain2", + "gain3", + "gain4", + "gain5", + "gain6", + "gain7", +}; + +static const char * const tft_groups[] = { + "tft", + /* tft pins */ + "tft_red0", + "tft_red1", + "tft_red2", + "tft_red3", + "tft_red4", + "tft_red5", + "tft_red6", + "tft_red7", + "tft_green0", + "tft_green1", + "tft_green2", + "tft_green3", + "tft_green4", + "tft_green5", + "tft_green6", + "tft_green7", + "tft_blue0", + "tft_blue1", + "tft_blue2", + "tft_blue3", + "tft_blue4", + "tft_blue5", + "tft_blue6", + "tft_blue7", + "tft_vdden_gd", + "tft_panelclk", + "tft_blank_ls", + "tft_vsync_ns", + "tft_hsync_nr", + "tft_vd12acb", + "tft_pwrsave", +}; + +/* Mux functions that can be used by a mux */ + +enum tz1090_mux { + /* internal placeholder */ + TZ1090_MUX_NA = -1, + /* magic per-non-muxing-GPIO-pin peripheral mode mux */ + TZ1090_MUX_PERIP, + /* SDH/SDIO mux */ + TZ1090_MUX_SDH, + TZ1090_MUX_SDIO, + /* USB_VBUS muxes */ + TZ1090_MUX_SPI1_CS2, + TZ1090_MUX_PDM_DAC, + TZ1090_MUX_USB_VBUS, + /* AFE mux */ + TZ1090_MUX_AFE, + TZ1090_MUX_TS_OUT_0, + /* EXT_DAC mux */ + TZ1090_MUX_DAC, + TZ1090_MUX_NOT_IQADC_STB, + TZ1090_MUX_IQDAC_STB, + /* TFT mux */ + TZ1090_MUX_TFT, + TZ1090_MUX_EXT_DAC, + TZ1090_MUX_TS_OUT_1, + TZ1090_MUX_LCD_TRACE, + TZ1090_MUX_PHY_RINGOSC, +}; + +#define FUNCTION(mux, fname, group) \ + [(TZ1090_MUX_ ## mux)] = { \ + .name = #fname, \ + .groups = group##_groups, \ + .ngroups = ARRAY_SIZE(group##_groups), \ + } +/* For intermediate functions with submuxes */ +#define NULL_FUNCTION(mux, fname) \ + [(TZ1090_MUX_ ## mux)] = { \ + .name = #fname, \ + } + +/* Must correlate with enum tz1090_mux */ +static const struct tz1090_function tz1090_functions[] = { + /* FUNCTION function name pingroups */ + FUNCTION(PERIP, perip, perip), + FUNCTION(SDH, sdh, sdh_sdio), + FUNCTION(SDIO, sdio, sdh_sdio), + FUNCTION(SPI1_CS2, spi1_cs2, spi1_cs2), + FUNCTION(PDM_DAC, pdm_dac, pdm_dac), + FUNCTION(USB_VBUS, usb_vbus, usb_vbus), + FUNCTION(AFE, afe, afe), + FUNCTION(TS_OUT_0, ts_out_0, afe), + FUNCTION(DAC, ext_dac, tft), + FUNCTION(NOT_IQADC_STB, not_iqadc_stb, tft), + FUNCTION(IQDAC_STB, iqdac_stb, tft), + FUNCTION(TFT, tft, tft), + NULL_FUNCTION(EXT_DAC, _ext_dac), + FUNCTION(TS_OUT_1, ts_out_1, tft), + FUNCTION(LCD_TRACE, lcd_trace, tft), + FUNCTION(PHY_RINGOSC, phy_ringosc, tft), +}; + +/* Sub muxes */ + +/** + * MUX() - Initialise a mux description. + * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none) + * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none) + * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none) + * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none) + * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register that the mux field begins + * @mux_w: Width of mux field in register + */ +#define MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \ + { \ + .funcs = { \ + TZ1090_MUX_ ## f0, \ + TZ1090_MUX_ ## f1, \ + TZ1090_MUX_ ## f2, \ + TZ1090_MUX_ ## f3, \ + TZ1090_MUX_ ## f4, \ + }, \ + .reg = (REG_PINCTRL_ ## mux_r), \ + .bit = (mux_b), \ + .width = (mux_w), \ + } + +/** + * DEFINE_SUBMUX() - Defines a submux description separate from a pin group. + * @mux: Mux name (_submux is appended) + * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none) + * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none) + * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none) + * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none) + * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register that the mux field begins + * @mux_w: Width of mux field in register + * + * A sub mux is a nested mux that can be bound to a magic function number used + * by another mux description. For example value 4 of the top level mux might + * correspond to a function which has a submux pointed to in tz1090_submux[]. + * The outer mux can then take on any function in the top level mux or the + * submux, and if a submux function is chosen both muxes are updated to route + * the signal from the submux. + * + * The submux can be defined with DEFINE_SUBMUX and pointed to from + * tz1090_submux[] using SUBMUX. + */ +#define DEFINE_SUBMUX(mux, f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \ + static struct tz1090_muxdesc mux ## _submux = \ + MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) + +/** + * SUBMUX() - Link a submux to a function number. + * @f: Function name (TZ1090_MUX_ is prepended) + * @submux: Submux name (_submux is appended) + * + * For use in tz1090_submux[] initialisation to link an intermediate function + * number to a particular submux description. It indicates that when the + * function is chosen the signal is connected to the submux. + */ +#define SUBMUX(f, submux) [(TZ1090_MUX_ ## f)] = &(submux ## _submux) + +/** + * MUX_PG() - Initialise a pin group with mux control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none) + * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none) + * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none) + * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none) + * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register that the mux field begins + * @mux_w: Width of mux field in register + */ +#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \ + mux_r, mux_b, mux_w) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .mux = MUX(f0, f1, f2, f3, f4, \ + mux_r, mux_b, mux_w), \ + } + +/** + * SIMPLE_PG() - Initialise a simple convenience pin group + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * + * A simple pin group is simply used for binding pins together so they can be + * referred to by a single name instead of having to list every pin + * individually. + */ +#define SIMPLE_PG(pg_name) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + } + +/** + * DRV_PG() - Initialise a pin group with drive control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * @slw_b: Slew register bit. + * The same bit is used for Schmitt, and Drive (*2). + */ +#define DRV_PG(pg_name, slw_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .drv = true, \ + .slw_bit = (slw_b), \ + } + +/* + * Define main muxing pin groups + */ + +/* submuxes */ + +/* name f0, f1, f2, f3, f4, mux r/b/w */ +DEFINE_SUBMUX(ext_dac, DAC, NOT_IQADC_STB, IQDAC_STB, NA, NA, IF_CTL, 6, 2); + +/* bind submuxes to internal functions */ +static struct tz1090_muxdesc *tz1090_submux[] = { + SUBMUX(EXT_DAC, ext_dac), +}; + +/* + * These are the pin mux groups. Pin muxing can be enabled and disabled for each + * pin individually so these groups are internal. The mapping of pins to pin mux + * group is below (tz1090_mux_pins). + */ +static struct tz1090_pingroup tz1090_mux_groups[] = { + /* Muxing pin groups */ + /* pg_name, f0, f1, f2, f3, f4, mux r/b/w */ + MUX_PG(sdh, SDH, SDIO, NA, NA, NA, IF_CTL, 20, 2), + MUX_PG(sdio, SDIO, SDH, NA, NA, NA, IF_CTL, 16, 2), + MUX_PG(spi1_cs2, SPI1_CS2, USB_VBUS, NA, NA, NA, IF_CTL, 10, 2), + MUX_PG(pdm_d, PDM_DAC, USB_VBUS, NA, NA, NA, IF_CTL, 8, 2), + MUX_PG(afe, AFE, TS_OUT_0, NA, NA, NA, IF_CTL, 4, 2), + MUX_PG(tft, TFT, EXT_DAC, TS_OUT_1, LCD_TRACE, PHY_RINGOSC, IF_CTL, 0, 3), +}; + +/* + * This is the mapping from GPIO pins to pin mux groups in tz1090_mux_groups[]. + * Pins which aren't muxable to multiple peripherals are set to + * TZ1090_MUX_GROUP_MAX to enable the "perip" function to enable/disable + * peripheral control of the pin. + * + * This array is initialised in tz1090_init_mux_pins(). + */ +static u8 tz1090_mux_pins[NUM_GPIOS]; + +/* TZ1090_MUX_GROUP_MAX is used in tz1090_mux_pins[] for non-muxing pins */ +#define TZ1090_MUX_GROUP_MAX ARRAY_SIZE(tz1090_mux_groups) + +/** + * tz1090_init_mux_pins() - Initialise GPIO pin to mux group mapping. + * + * Initialises the tz1090_mux_pins[] array to be the inverse of the pin lists in + * each pin mux group in tz1090_mux_groups[]. + * + * It is assumed that no pin mux groups overlap (share pins). + */ +static void __init tz1090_init_mux_pins(void) +{ + unsigned int g, p; + const struct tz1090_pingroup *grp; + const unsigned int *pin; + + for (p = 0; p < NUM_GPIOS; ++p) + tz1090_mux_pins[p] = TZ1090_MUX_GROUP_MAX; + + grp = tz1090_mux_groups; + for (g = 0, grp = tz1090_mux_groups; + g < ARRAY_SIZE(tz1090_mux_groups); ++g, ++grp) + for (pin = grp->pins, p = 0; p < grp->npins; ++p, ++pin) + tz1090_mux_pins[*pin] = g; +} + +/* + * These are the externally visible pin groups. Some of them allow group control + * of drive configuration. Some are just simple convenience pingroups. All the + * internal pin mux groups in tz1090_mux_groups[] are mirrored here with the + * same pins. + * Pseudo pin groups follow in the group numbers after this array for each GPIO + * pin. Any group used for muxing must have all pins belonging to the same pin + * mux group. + */ +static struct tz1090_pingroup tz1090_groups[] = { + /* Pin groups with drive control (with no out of place pins) */ + /* pg_name, slw/schmitt/drv b */ + DRV_PG(jtag, 11 /* 11, 22 */), + DRV_PG(tft, 10 /* 10, 20 */), + DRV_PG(scb2, 9 /* 9, 18 */), + DRV_PG(spi0, 7 /* 7, 14 */), + DRV_PG(uart, 5 /* 5, 10 */), + DRV_PG(scb1, 4 /* 4, 8 */), + DRV_PG(spi1, 3 /* 3, 6 */), + DRV_PG(afe, 0 /* 0, 0 */), + + /* + * Drive specific pin groups (with odd combinations of pins which makes + * the pin group naming somewhat arbitrary) + */ + /* pg_name, slw/schmitt/drv b */ + DRV_PG(drive_sdio, 8 /* 8, 16 */), /* sdio_* + sdh_* */ + DRV_PG(drive_i2s, 6 /* 6, 12 */), /* i2s_* + clk_out1 */ + DRV_PG(drive_scb0, 2 /* 2, 4 */), /* scb0_* + pdm_{c,d} */ + DRV_PG(drive_pdm, 1 /* 1, 2 */), /* pdm_{a,b} + clk_out0 */ + + /* Convenience pin groups */ + /* pg_name */ + SIMPLE_PG(uart0), + SIMPLE_PG(uart1), + SIMPLE_PG(scb0), + SIMPLE_PG(i2s), + SIMPLE_PG(sdh), + SIMPLE_PG(sdio), + + /* pseudo-pingroups for each GPIO pin follow */ +}; + +/** + * struct tz1090_pmx - Private pinctrl data + * @dev: Platform device + * @pctl: Pin control device + * @regs: Register region + * @lock: Lock protecting coherency of pin_en, gpio_en, and SELECT regs + * @pin_en: Pins that have been enabled (32 pins packed into each element) + * @gpio_en: GPIOs that have been enabled (32 pins packed into each element) + */ +struct tz1090_pmx { + struct device *dev; + struct pinctrl_dev *pctl; + void __iomem *regs; + spinlock_t lock; + u32 pin_en[3]; + u32 gpio_en[3]; +}; + +static inline u32 pmx_read(struct tz1090_pmx *pmx, u32 reg) +{ + return ioread32(pmx->regs + reg); +} + +static inline void pmx_write(struct tz1090_pmx *pmx, u32 val, u32 reg) +{ + iowrite32(val, pmx->regs + reg); +} + +/* + * Pin control operations + */ + +/* each GPIO pin has it's own pseudo pingroup containing only itself */ + +static int tz1090_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_groups) + NUM_GPIOS; +} + +static const char *tz1090_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group) +{ + if (group < ARRAY_SIZE(tz1090_groups)) { + /* normal pingroup */ + return tz1090_groups[group].name; + } else { + /* individual gpio pin pseudo-pingroup */ + unsigned int pin = group - ARRAY_SIZE(tz1090_groups); + return tz1090_pins[pin].name; + } +} + +static int tz1090_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) +{ + if (group < ARRAY_SIZE(tz1090_groups)) { + /* normal pingroup */ + *pins = tz1090_groups[group].pins; + *num_pins = tz1090_groups[group].npins; + } else { + /* individual gpio pin pseudo-pingroup */ + unsigned int pin = group - ARRAY_SIZE(tz1090_groups); + *pins = &tz1090_pins[pin].number; + *num_pins = 1; + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void tz1090_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int offset) +{ + seq_printf(s, " %s", dev_name(pctldev->dev)); +} +#endif + +static int reserve_map(struct device *dev, struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + unsigned int reserve) +{ + unsigned int old_num = *reserved_maps; + unsigned int new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) { + dev_err(dev, "krealloc(map) failed\n"); + return -ENOMEM; + } + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + const char *function) +{ + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +static int add_map_configs(struct device *dev, + struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + const char *group, unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *dup_configs; + + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) { + dev_err(dev, "kmemdup(configs) failed\n"); + return -ENOMEM; + } + + (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +static void tz1090_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned int num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + + kfree(map); +} + +static int tz1090_pinctrl_dt_subnode_to_map(struct device *dev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *reserved_maps, + unsigned int *num_maps) +{ + int ret; + const char *function; + unsigned long *configs = NULL; + unsigned int num_configs = 0; + unsigned int reserve; + struct property *prop; + const char *group; + + ret = of_property_read_string(np, "tz1090,function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, "could not parse property function\n"); + function = NULL; + } + + ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + if (ret) + return ret; + + reserve = 0; + if (function != NULL) + reserve++; + if (num_configs) + reserve++; + ret = of_property_count_strings(np, "tz1090,pins"); + if (ret < 0) { + dev_err(dev, "could not parse property pins\n"); + goto exit; + } + reserve *= ret; + + ret = reserve_map(dev, map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "tz1090,pins", prop, group) { + if (function) { + ret = add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + + if (num_configs) { + ret = add_map_configs(dev, map, reserved_maps, + num_maps, group, configs, + num_configs); + if (ret < 0) + goto exit; + } + } + + ret = 0; + +exit: + kfree(configs); + return ret; +} + +static int tz1090_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + unsigned int reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = tz1090_pinctrl_dt_subnode_to_map(pctldev->dev, np, map, + &reserved_maps, + num_maps); + if (ret < 0) { + tz1090_pinctrl_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + +static struct pinctrl_ops tz1090_pinctrl_ops = { + .get_groups_count = tz1090_pinctrl_get_groups_count, + .get_group_name = tz1090_pinctrl_get_group_name, + .get_group_pins = tz1090_pinctrl_get_group_pins, +#ifdef CONFIG_DEBUG_FS + .pin_dbg_show = tz1090_pinctrl_pin_dbg_show, +#endif + .dt_node_to_map = tz1090_pinctrl_dt_node_to_map, + .dt_free_map = tz1090_pinctrl_dt_free_map, +}; + +/* + * Pin mux operations + */ + +static int tz1090_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_functions); +} + +static const char *tz1090_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return tz1090_functions[function].name; +} + +static int tz1090_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char * const **groups, + unsigned int * const num_groups) +{ + /* pingroup functions */ + *groups = tz1090_functions[function].groups; + *num_groups = tz1090_functions[function].ngroups; + return 0; +} + +/** + * tz1090_pinctrl_select() - update bit in SELECT register + * @pmx: Pinmux data + * @pin: Pin number (must be within GPIO range) + */ +static void tz1090_pinctrl_select(struct tz1090_pmx *pmx, + unsigned int pin) +{ + u32 reg, reg_shift, select, val; + unsigned int pmx_index, pmx_shift; + unsigned long flags; + + /* uses base 32 instead of base 30 */ + pmx_index = pin >> 5; + pmx_shift = pin & 0x1f; + + /* select = !perip || gpio */ + select = ((~pmx->pin_en[pmx_index] | + pmx->gpio_en[pmx_index]) >> pmx_shift) & 1; + + /* find register and bit offset (base 30) */ + reg = REG_PINCTRL_SELECT + 4*(pin / 30); + reg_shift = pin % 30; + + /* modify gpio select bit */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~BIT(reg_shift); + val |= select << reg_shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); +} + +/** + * tz1090_pinctrl_gpio_select() - enable/disable GPIO usage for a pin + * @pmx: Pinmux data + * @pin: Pin number + * @gpio_select: true to enable pin as GPIO, + * false to leave control to whatever function is enabled + * + * Records that GPIO usage is enabled/disabled so that enabling a function + * doesn't override the SELECT register bit. + */ +static void tz1090_pinctrl_gpio_select(struct tz1090_pmx *pmx, + unsigned int pin, + bool gpio_select) +{ + unsigned int index, shift; + u32 gpio_en; + + if (pin >= NUM_GPIOS) + return; + + /* uses base 32 instead of base 30 */ + index = pin >> 5; + shift = pin & 0x1f; + + spin_lock(&pmx->lock); + + /* keep a record whether gpio is selected */ + gpio_en = pmx->gpio_en[index]; + gpio_en &= ~BIT(shift); + if (gpio_select) + gpio_en |= BIT(shift); + pmx->gpio_en[index] = gpio_en; + + /* update the select bit */ + tz1090_pinctrl_select(pmx, pin); + + spin_unlock(&pmx->lock); +} + +/** + * tz1090_pinctrl_perip_select() - enable/disable peripheral interface for a pin + * @pmx: Pinmux data + * @pin: Pin number + * @perip_select: true to enable peripheral interface when not GPIO, + * false to leave pin in GPIO mode + * + * Records that peripheral usage is enabled/disabled so that SELECT register can + * be set appropriately when GPIO is disabled. + */ +static void tz1090_pinctrl_perip_select(struct tz1090_pmx *pmx, + unsigned int pin, + bool perip_select) +{ + unsigned int index, shift; + u32 pin_en; + + if (pin >= NUM_GPIOS) + return; + + /* uses base 32 instead of base 30 */ + index = pin >> 5; + shift = pin & 0x1f; + + spin_lock(&pmx->lock); + + /* keep a record whether peripheral is selected */ + pin_en = pmx->pin_en[index]; + pin_en &= ~BIT(shift); + if (perip_select) + pin_en |= BIT(shift); + pmx->pin_en[index] = pin_en; + + /* update the select bit */ + tz1090_pinctrl_select(pmx, pin); + + spin_unlock(&pmx->lock); +} + +/** + * tz1090_pinctrl_enable_mux() - Switch a pin mux group to a function. + * @pmx: Pinmux data + * @desc: Pinmux description + * @function: Function to switch to + * + * Enable a particular function on a pin mux group. Since pin mux descriptions + * are nested this function is recursive. + */ +static int tz1090_pinctrl_enable_mux(struct tz1090_pmx *pmx, + const struct tz1090_muxdesc *desc, + unsigned int function) +{ + const int *fit; + unsigned long flags; + int mux; + unsigned int func, ret; + u32 reg, mask; + + /* find the mux value for this function, searching recursively */ + for (mux = 0, fit = desc->funcs; + mux < ARRAY_SIZE(desc->funcs); ++mux, ++fit) { + func = *fit; + if (func == function) + goto found_mux; + + /* maybe it's a sub-mux */ + if (func < ARRAY_SIZE(tz1090_submux) && tz1090_submux[func]) { + ret = tz1090_pinctrl_enable_mux(pmx, + tz1090_submux[func], + function); + if (!ret) + goto found_mux; + } + } + + return -EINVAL; +found_mux: + + /* Set up the mux */ + if (desc->width) { + mask = (BIT(desc->width) - 1) << desc->bit; + __global_lock2(flags); + reg = pmx_read(pmx, desc->reg); + reg &= ~mask; + reg |= (mux << desc->bit) & mask; + pmx_write(pmx, reg, desc->reg); + __global_unlock2(flags); + } + + return 0; +} + +/** + * tz1090_pinctrl_enable() - Enable a function on a pin group. + * @pctldev: Pin control data + * @function: Function index to enable + * @group: Group index to enable + * + * Enable a particular function on a group of pins. The per GPIO pin pseudo pin + * groups can be used (in which case the pin will be enabled in peripheral mode + * and if it belongs to a pin mux group the mux will be switched if it isn't + * already in use. Some convenience pin groups can also be used in which case + * the effect is the same as enabling the function on each individual pin in the + * group. + */ +static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + struct tz1090_pingroup *grp; + int ret; + unsigned int pin_num, mux_group, i, npins; + const unsigned int *pins; + + /* group of pins? */ + if (group < ARRAY_SIZE(tz1090_groups)) { + grp = &tz1090_groups[group]; + npins = grp->npins; + pins = grp->pins; + /* + * All pins in the group must belong to the same mux group, + * which allows us to just use the mux group of the first pin. + * By explicitly listing permitted pingroups for each function + * the pinmux core should ensure this is always the case. + */ + } else { + pin_num = group - ARRAY_SIZE(tz1090_groups); + npins = 1; + pins = &pin_num; + } + mux_group = tz1090_mux_pins[*pins]; + + /* no mux group, but can still be individually muxed to peripheral */ + if (mux_group >= TZ1090_MUX_GROUP_MAX) { + if (function == TZ1090_MUX_PERIP) + goto mux_pins; + return -EINVAL; + } + + /* mux group already set to a different function? */ + grp = &tz1090_mux_groups[mux_group]; + if (grp->func_count && grp->func != function) { + dev_err(pctldev->dev, + "%s: can't mux pin(s) to '%s', group already muxed to '%s'\n", + __func__, tz1090_functions[function].name, + tz1090_functions[grp->func].name); + return -EBUSY; + } + + dev_dbg(pctldev->dev, "%s: muxing %u pin(s) in '%s' to '%s'\n", + __func__, npins, grp->name, tz1090_functions[function].name); + + /* if first pin in mux group to be enabled, enable the group mux */ + if (!grp->func_count) { + grp->func = function; + ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function); + if (ret) + return ret; + } + /* add pins to ref count and mux individually to peripheral */ + grp->func_count += npins; +mux_pins: + for (i = 0; i < npins; ++i) + tz1090_pinctrl_perip_select(pmx, pins[i], true); + + return 0; +} + +/** + * tz1090_pinctrl_disable() - Disable a function on a pin group. + * @pctldev: Pin control data + * @function: Function index to disable + * @group: Group index to disable + * + * Disable a particular function on a group of pins. The per GPIO pin pseudo pin + * groups can be used (in which case the pin will be taken out of peripheral + * mode. Some convenience pin groups can also be used in which case the effect + * is the same as enabling the function on each individual pin in the group. + */ +static void tz1090_pinctrl_disable(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + struct tz1090_pingroup *grp; + unsigned int pin_num, mux_group, i, npins; + const unsigned int *pins; + + /* group of pins? */ + if (group < ARRAY_SIZE(tz1090_groups)) { + grp = &tz1090_groups[group]; + npins = grp->npins; + pins = grp->pins; + /* + * All pins in the group must belong to the same mux group, + * which allows us to just use the mux group of the first pin. + * By explicitly listing permitted pingroups for each function + * the pinmux core should ensure this is always the case. + */ + } else { + pin_num = group - ARRAY_SIZE(tz1090_groups); + npins = 1; + pins = &pin_num; + } + mux_group = tz1090_mux_pins[*pins]; + + /* no mux group, but can still be individually muxed to peripheral */ + if (mux_group >= TZ1090_MUX_GROUP_MAX) { + if (function == TZ1090_MUX_PERIP) + goto unmux_pins; + return; + } + + /* mux group already set to a different function? */ + grp = &tz1090_mux_groups[mux_group]; + dev_dbg(pctldev->dev, "%s: unmuxing %u pin(s) in '%s' from '%s'\n", + __func__, npins, grp->name, tz1090_functions[function].name); + + /* subtract pins from ref count and unmux individually */ + WARN_ON(grp->func_count < npins); + grp->func_count -= npins; +unmux_pins: + for (i = 0; i < npins; ++i) + tz1090_pinctrl_perip_select(pmx, pins[i], false); +} + +/** + * tz1090_pinctrl_gpio_request_enable() - Put pin in GPIO mode. + * @pctldev: Pin control data + * @range: GPIO range + * @pin: Pin number + * + * Puts a particular pin into GPIO mode, disabling peripheral control until it's + * disabled again. + */ +static int tz1090_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + tz1090_pinctrl_gpio_select(pmx, pin, true); + return 0; +} + +/** + * tz1090_pinctrl_gpio_disable_free() - Take pin out of GPIO mode. + * @pctldev: Pin control data + * @range: GPIO range + * @pin: Pin number + * + * Take a particular pin out of GPIO mode. If the pin is enabled for a + * peripheral it will return to peripheral mode. + */ +static void tz1090_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + tz1090_pinctrl_gpio_select(pmx, pin, false); +} + +static struct pinmux_ops tz1090_pinmux_ops = { + .get_functions_count = tz1090_pinctrl_get_funcs_count, + .get_function_name = tz1090_pinctrl_get_func_name, + .get_function_groups = tz1090_pinctrl_get_func_groups, + .enable = tz1090_pinctrl_enable, + .disable = tz1090_pinctrl_disable, + .gpio_request_enable = tz1090_pinctrl_gpio_request_enable, + .gpio_disable_free = tz1090_pinctrl_gpio_disable_free, +}; + +/* + * Pin config operations + */ + +struct tz1090_pinconf_pullup { + unsigned char index; + unsigned char shift; +}; + +/* The mapping of pin to pull up/down register index and shift */ +static struct tz1090_pinconf_pullup tz1090_pinconf_pullup[] = { + {5, 22}, /* 0 - TZ1090_PIN_SDIO_CLK */ + {0, 14}, /* 1 - TZ1090_PIN_SDIO_CMD */ + {0, 6}, /* 2 - TZ1090_PIN_SDIO_D0 */ + {0, 8}, /* 3 - TZ1090_PIN_SDIO_D1 */ + {0, 10}, /* 4 - TZ1090_PIN_SDIO_D2 */ + {0, 12}, /* 5 - TZ1090_PIN_SDIO_D3 */ + {0, 2}, /* 6 - TZ1090_PIN_SDH_CD */ + {0, 4}, /* 7 - TZ1090_PIN_SDH_WP */ + {0, 16}, /* 8 - TZ1090_PIN_SPI0_MCLK */ + {0, 18}, /* 9 - TZ1090_PIN_SPI0_CS0 */ + {0, 20}, /* 10 - TZ1090_PIN_SPI0_CS1 */ + {0, 22}, /* 11 - TZ1090_PIN_SPI0_CS2 */ + {0, 24}, /* 12 - TZ1090_PIN_SPI0_DOUT */ + {0, 26}, /* 13 - TZ1090_PIN_SPI0_DIN */ + {0, 28}, /* 14 - TZ1090_PIN_SPI1_MCLK */ + {0, 30}, /* 15 - TZ1090_PIN_SPI1_CS0 */ + {1, 0}, /* 16 - TZ1090_PIN_SPI1_CS1 */ + {1, 2}, /* 17 - TZ1090_PIN_SPI1_CS2 */ + {1, 4}, /* 18 - TZ1090_PIN_SPI1_DOUT */ + {1, 6}, /* 19 - TZ1090_PIN_SPI1_DIN */ + {1, 8}, /* 20 - TZ1090_PIN_UART0_RXD */ + {1, 10}, /* 21 - TZ1090_PIN_UART0_TXD */ + {1, 12}, /* 22 - TZ1090_PIN_UART0_CTS */ + {1, 14}, /* 23 - TZ1090_PIN_UART0_RTS */ + {1, 16}, /* 24 - TZ1090_PIN_UART1_RXD */ + {1, 18}, /* 25 - TZ1090_PIN_UART1_TXD */ + {1, 20}, /* 26 - TZ1090_PIN_SCB0_SDAT */ + {1, 22}, /* 27 - TZ1090_PIN_SCB0_SCLK */ + {1, 24}, /* 28 - TZ1090_PIN_SCB1_SDAT */ + {1, 26}, /* 29 - TZ1090_PIN_SCB1_SCLK */ + + {1, 28}, /* 30 - TZ1090_PIN_SCB2_SDAT */ + {1, 30}, /* 31 - TZ1090_PIN_SCB2_SCLK */ + {2, 0}, /* 32 - TZ1090_PIN_I2S_MCLK */ + {2, 2}, /* 33 - TZ1090_PIN_I2S_BCLK_OUT */ + {2, 4}, /* 34 - TZ1090_PIN_I2S_LRCLK_OUT */ + {2, 6}, /* 35 - TZ1090_PIN_I2S_DOUT0 */ + {2, 8}, /* 36 - TZ1090_PIN_I2S_DOUT1 */ + {2, 10}, /* 37 - TZ1090_PIN_I2S_DOUT2 */ + {2, 12}, /* 38 - TZ1090_PIN_I2S_DIN */ + {4, 12}, /* 39 - TZ1090_PIN_PDM_A */ + {4, 14}, /* 40 - TZ1090_PIN_PDM_B */ + {4, 18}, /* 41 - TZ1090_PIN_PDM_C */ + {4, 20}, /* 42 - TZ1090_PIN_PDM_D */ + {2, 14}, /* 43 - TZ1090_PIN_TFT_RED0 */ + {2, 16}, /* 44 - TZ1090_PIN_TFT_RED1 */ + {2, 18}, /* 45 - TZ1090_PIN_TFT_RED2 */ + {2, 20}, /* 46 - TZ1090_PIN_TFT_RED3 */ + {2, 22}, /* 47 - TZ1090_PIN_TFT_RED4 */ + {2, 24}, /* 48 - TZ1090_PIN_TFT_RED5 */ + {2, 26}, /* 49 - TZ1090_PIN_TFT_RED6 */ + {2, 28}, /* 50 - TZ1090_PIN_TFT_RED7 */ + {2, 30}, /* 51 - TZ1090_PIN_TFT_GREEN0 */ + {3, 0}, /* 52 - TZ1090_PIN_TFT_GREEN1 */ + {3, 2}, /* 53 - TZ1090_PIN_TFT_GREEN2 */ + {3, 4}, /* 54 - TZ1090_PIN_TFT_GREEN3 */ + {3, 6}, /* 55 - TZ1090_PIN_TFT_GREEN4 */ + {3, 8}, /* 56 - TZ1090_PIN_TFT_GREEN5 */ + {3, 10}, /* 57 - TZ1090_PIN_TFT_GREEN6 */ + {3, 12}, /* 58 - TZ1090_PIN_TFT_GREEN7 */ + {3, 14}, /* 59 - TZ1090_PIN_TFT_BLUE0 */ + + {3, 16}, /* 60 - TZ1090_PIN_TFT_BLUE1 */ + {3, 18}, /* 61 - TZ1090_PIN_TFT_BLUE2 */ + {3, 20}, /* 62 - TZ1090_PIN_TFT_BLUE3 */ + {3, 22}, /* 63 - TZ1090_PIN_TFT_BLUE4 */ + {3, 24}, /* 64 - TZ1090_PIN_TFT_BLUE5 */ + {3, 26}, /* 65 - TZ1090_PIN_TFT_BLUE6 */ + {3, 28}, /* 66 - TZ1090_PIN_TFT_BLUE7 */ + {3, 30}, /* 67 - TZ1090_PIN_TFT_VDDEN_GD */ + {4, 0}, /* 68 - TZ1090_PIN_TFT_PANELCLK */ + {4, 2}, /* 69 - TZ1090_PIN_TFT_BLANK_LS */ + {4, 4}, /* 70 - TZ1090_PIN_TFT_VSYNC_NS */ + {4, 6}, /* 71 - TZ1090_PIN_TFT_HSYNC_NR */ + {4, 8}, /* 72 - TZ1090_PIN_TFT_VD12ACB */ + {4, 10}, /* 73 - TZ1090_PIN_TFT_PWRSAVE */ + {4, 24}, /* 74 - TZ1090_PIN_TX_ON */ + {4, 26}, /* 75 - TZ1090_PIN_RX_ON */ + {4, 28}, /* 76 - TZ1090_PIN_PLL_ON */ + {4, 30}, /* 77 - TZ1090_PIN_PA_ON */ + {5, 0}, /* 78 - TZ1090_PIN_RX_HP */ + {5, 6}, /* 79 - TZ1090_PIN_GAIN0 */ + {5, 8}, /* 80 - TZ1090_PIN_GAIN1 */ + {5, 10}, /* 81 - TZ1090_PIN_GAIN2 */ + {5, 12}, /* 82 - TZ1090_PIN_GAIN3 */ + {5, 14}, /* 83 - TZ1090_PIN_GAIN4 */ + {5, 16}, /* 84 - TZ1090_PIN_GAIN5 */ + {5, 18}, /* 85 - TZ1090_PIN_GAIN6 */ + {5, 20}, /* 86 - TZ1090_PIN_GAIN7 */ + {5, 2}, /* 87 - TZ1090_PIN_ANT_SEL0 */ + {5, 4}, /* 88 - TZ1090_PIN_ANT_SEL1 */ + {0, 0}, /* 89 - TZ1090_PIN_SDH_CLK_IN */ + + {5, 24}, /* 90 - TZ1090_PIN_TCK */ + {5, 26}, /* 91 - TZ1090_PIN_TRST */ + {5, 28}, /* 92 - TZ1090_PIN_TDI */ + {5, 30}, /* 93 - TZ1090_PIN_TDO */ + {6, 0}, /* 94 - TZ1090_PIN_TMS */ + {4, 16}, /* 95 - TZ1090_PIN_CLK_OUT0 */ + {4, 22}, /* 96 - TZ1090_PIN_CLK_OUT1 */ +}; + +static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev, + unsigned int pin, + enum pin_config_param param, + bool report_err, + u32 *reg, u32 *width, u32 *mask, u32 *shift, + u32 *val) +{ + struct tz1090_pinconf_pullup *pu; + + /* All supported pins have controllable input bias */ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + *val = REG_PU_PD_TRISTATE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + *val = REG_PU_PD_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + *val = REG_PU_PD_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + *val = REG_PU_PD_REPEATER; + break; + default: + return -ENOTSUPP; + }; + + /* Only input bias parameters supported */ + pu = &tz1090_pinconf_pullup[pin]; + *reg = REG_PINCTRL_PU_PD + 4*pu->index; + *shift = pu->shift; + *width = 2; + + /* Calculate field information */ + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int ret; + u32 reg, width, mask, shift, val, tmp, arg; + + /* Get register information */ + ret = tz1090_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Extract field from register */ + tmp = pmx_read(pmx, reg); + arg = ((tmp & mask) >> shift) == val; + + /* Config not active */ + if (!arg) + return -EINVAL; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(config); + unsigned int arg = pinconf_to_config_argument(config); + int ret; + u32 reg, width, mask, shift, val, tmp; + unsigned long flags; + + dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", + __func__, tz1090_pins[pin].name, config); + + /* Get register information */ + ret = tz1090_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Unpack argument and range check it */ + if (arg > 1) { + dev_dbg(pctldev->dev, "%s: arg %u out of range\n", + __func__, arg); + return -EINVAL; + } + + /* Write register field */ + __global_lock2(flags); + tmp = pmx_read(pmx, reg); + tmp &= ~mask; + if (arg) + tmp |= val << shift; + pmx_write(pmx, tmp, reg); + __global_unlock2(flags); + + return 0; +} + +static const int tz1090_boolean_map[] = { + [0] = -EINVAL, + [1] = 1, +}; + +static const int tz1090_dr_map[] = { + [REG_DR_2mA] = 2, + [REG_DR_4mA] = 4, + [REG_DR_8mA] = 8, + [REG_DR_12mA] = 12, +}; + +static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev, + const struct tz1090_pingroup *g, + enum pin_config_param param, + bool report_err, + u32 *reg, u32 *width, u32 *mask, u32 *shift, + const int **map) +{ + /* Drive configuration applies in groups, but not to all groups. */ + if (!g->drv) { + if (report_err) + dev_dbg(pctldev->dev, + "%s: group %s has no drive control\n", + __func__, g->name); + return -ENOTSUPP; + } + + /* Find information about drive parameter's register */ + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + *reg = REG_PINCTRL_SCHMITT; + *width = 1; + *map = tz1090_boolean_map; + break; + case PIN_CONFIG_SLEW_RATE: + *reg = REG_PINCTRL_SR; + *width = 1; + *map = tz1090_boolean_map; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + *reg = REG_PINCTRL_DR; + *width = 2; + *map = tz1090_dr_map; + break; + default: + return -ENOTSUPP; + }; + + /* Calculate field information */ + *shift = g->slw_bit * *width; + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pingroup *g; + enum pin_config_param param = pinconf_to_config_param(*config); + int ret, arg; + unsigned int pin; + u32 reg, width, mask, shift, val; + const int *map; + + if (group >= ARRAY_SIZE(tz1090_groups)) { + pin = group - ARRAY_SIZE(tz1090_groups); + return tz1090_pinconf_get(pctldev, pin, config); + } + + g = &tz1090_groups[group]; + if (g->npins == 1) { + pin = g->pins[0]; + ret = tz1090_pinconf_get(pctldev, pin, config); + if (ret != -ENOTSUPP) + return ret; + } + + /* Get register information */ + ret = tz1090_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) + return ret; + + /* Extract field from register */ + val = pmx_read(pmx, reg); + arg = map[(val & mask) >> shift]; + if (arg < 0) + return arg; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int group, unsigned long config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pingroup *g; + enum pin_config_param param = pinconf_to_config_param(config); + unsigned int arg, pin, i; + const unsigned int *pit; + int ret; + u32 reg, width, mask, shift, val; + unsigned long flags; + const int *map; + + if (group >= ARRAY_SIZE(tz1090_groups)) { + pin = group - ARRAY_SIZE(tz1090_groups); + return tz1090_pinconf_set(pctldev, pin, config); + } + + g = &tz1090_groups[group]; + if (g->npins == 1) { + pin = g->pins[0]; + ret = tz1090_pinconf_set(pctldev, pin, config); + if (ret != -ENOTSUPP) + return ret; + } + + dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", + __func__, g->name, config); + + /* Get register information */ + ret = tz1090_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) { + /* + * Maybe we're trying to set a per-pin configuration of a group, + * so do the pins one by one. This is mainly as a convenience. + */ + for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { + ret = tz1090_pinconf_set(pctldev, *pit, config); + if (ret) + return ret; + } + return 0; + } + + /* Unpack argument and map it to register value */ + arg = pinconf_to_config_argument(config); + for (i = 0; i < BIT(width); ++i) { + if (map[i] == arg || (map[i] == -EINVAL && !arg)) { + /* Write register field */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~mask; + val |= i << shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); + return 0; + } + } + + dev_dbg(pctldev->dev, "%s: arg %u not supported\n", + __func__, arg); + return -EINVAL; +} + +static struct pinconf_ops tz1090_pinconf_ops = { + .is_generic = true, + .pin_config_get = tz1090_pinconf_get, + .pin_config_set = tz1090_pinconf_set, + .pin_config_group_get = tz1090_pinconf_group_get, + .pin_config_group_set = tz1090_pinconf_group_set, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +/* + * Pin control driver setup + */ + +static struct pinctrl_desc tz1090_pinctrl_desc = { + .pctlops = &tz1090_pinctrl_ops, + .pmxops = &tz1090_pinmux_ops, + .confops = &tz1090_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int tz1090_pinctrl_probe(struct platform_device *pdev) +{ + struct tz1090_pmx *pmx; + struct resource *res; + + pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + if (!pmx) { + dev_err(&pdev->dev, "Can't alloc tz1090_pmx\n"); + return -ENOMEM; + } + pmx->dev = &pdev->dev; + spin_lock_init(&pmx->lock); + + tz1090_pinctrl_desc.name = dev_name(&pdev->dev); + tz1090_pinctrl_desc.pins = tz1090_pins; + tz1090_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pins); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, + "Couldn't request MEM resource\n"); + return -ENODEV; + } + + pmx->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!pmx->regs) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + return -ENODEV; + } + + pmx->pctl = pinctrl_register(&tz1090_pinctrl_desc, &pdev->dev, pmx); + if (!pmx->pctl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, pmx); + + dev_info(&pdev->dev, "TZ1090 pinctrl driver initialised\n"); + + return 0; +} + +static int tz1090_pinctrl_remove(struct platform_device *pdev) +{ + struct tz1090_pmx *pmx = platform_get_drvdata(pdev); + + pinctrl_unregister(pmx->pctl); + + return 0; +} + +static struct of_device_id tz1090_pinctrl_of_match[] = { + { .compatible = "img,tz1090-pinctrl", }, + { }, +}; + +static struct platform_driver tz1090_pinctrl_driver = { + .driver = { + .name = "tz1090-pinctrl", + .owner = THIS_MODULE, + .of_match_table = tz1090_pinctrl_of_match, + }, + .probe = tz1090_pinctrl_probe, + .remove = tz1090_pinctrl_remove, +}; + +static int __init tz1090_pinctrl_init(void) +{ + tz1090_init_mux_pins(); + return platform_driver_register(&tz1090_pinctrl_driver); +} +arch_initcall(tz1090_pinctrl_init); + +static void __exit tz1090_pinctrl_exit(void) +{ + platform_driver_unregister(&tz1090_pinctrl_driver); +} +module_exit(tz1090_pinctrl_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("Toumaz Xenif TZ1090 pinctrl driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, tz1090_pinctrl_of_match); -- cgit v1.2.3 From b58f0273f0858214da2ee4e1675221e56f7712ec Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 20 Jun 2013 10:26:29 +0100 Subject: pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver Add a pin control driver for the TZ1090's low power pins via the powerdown controller SOC_GPIO_CONTROL registers. These pins have individually controlled pull-up, and group controlled schmitt, slew-rate, drive-strength, and power-on-start (pos). The pdc_gpio0 and pdc_gpio1 pins can also be muxed onto the ir_mod_stable_out and ir_mod_power_out functions respectively. If no function is set they remain in GPIO mode. These muxes can be overridden by requesting them as GPIOs. Signed-off-by: James Hogan Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Cc: Linus Walleij Cc: linux-doc@vger.kernel.org Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Linus Walleij --- .../bindings/pinctrl/img,tz1090-pdc-pinctrl.txt | 130 +++ drivers/pinctrl/Kconfig | 6 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-tz1090-pdc.c | 1029 ++++++++++++++++++++ 4 files changed, 1166 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-tz1090-pdc.c (limited to 'drivers/pinctrl/Kconfig') diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt new file mode 100644 index 000000000000..9f7a85bb8fca --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt @@ -0,0 +1,130 @@ +ImgTec TZ1090 PDC pin controller + +Required properties: +- compatible: "img,tz1090-pdc-pinctrl" +- reg: Should contain the register physical address and length of the + SOC_GPIO_CONTROL registers in the PDC register region. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +TZ1090-PDC's pin configuration nodes act as a container for an abitrary number +of subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, drive strength, etc. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. For this reason, even seemingly boolean +values are actually tristates in this binding: unspecified, off, or on. +Unspecified is represented as an absent property, and off/on are represented as +integer values 0 and 1. + +Required subnode-properties: +- tz1090,pins : An array of strings. Each string contains the name of a pin or + group. Valid values for these names are listed below. + +Optional subnode-properties: +- tz1090,function: A string containing the name of the function to mux to the + pin or group. Valid values for function names are listed below, including + which pingroups can be muxed to them. +- supported generic pinconfig properties (for further details see + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt): + - bias-disable + - bias-high-impedance + - bias-bus-hold + - bias-pull-up + - bias-pull-down + - input-schmitt-enable + - input-schmitt-disable + - slew-rate: Integer, control slew rate of pins. + 0: slow (half frequency) + 1: fast + - drive-strength: Integer, control drive strength of pins in mA. + 2: 2mA + 4: 4mA + 8: 8mA + 12: 12mA + - low-power-enable: Flag, power-on-start weak pull-down for invalid power. + - low-power-disable: Flag, power-on-start weak pull-down disabled. + +Note that many of these properties are only valid for certain specific pins +or groups. See the TZ1090 TRM for complete details regarding which groups +support which functionality. The Linux pinctrl driver may also be a useful +reference. + +Valid values for pin and group names are: + + pins: + + These all support bias-high-impediance, bias-pull-up, bias-pull-down, and + bias-bus-hold (which can also be provided to any of the groups below to set + it for all gpio pins in that group). + + gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data, ext_power. + + mux groups: + + These all support function. + + gpio0 + pins: gpio0. + function: ir_mod_stable_out. + gpio1 + pins: gpio1. + function: ir_mod_power_out. + + drive groups: + + These support input-schmitt-enable, input-schmitt-disable, slew-rate, + drive-strength, low-power-enable, and low-power-disable. + + pdc + pins: gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data, + ext_power. + +Example: + + pinctrl_pdc: pinctrl@02006500 { + #gpio-range-cells = <3>; + compatible = "img,tz1090-pdc-pinctrl"; + reg = <0x02006500 0x100>; + }; + +Example board file extracts: + + &pinctrl_pdc { + pinctrl-names = "default"; + pinctrl-0 = <&syswake_default>; + + syswake_default: syswakes { + syswake_cfg { + tz1090,pins = "sys_wake0", + "sys_wake1", + "sys_wake2"; + pull-up; + }; + }; + irmod_default: irmod { + gpio0_cfg { + tz1090,pins = "gpio0"; + tz1090,function = "ir_mod_stable_out"; + }; + gpio1_cfg { + tz1090,pins = "gpio1"; + tz1090,function = "ir_mod_power_out"; + }; + }; + }; + + ir: ir@02006200 { + pinctrl-names = "default"; + pinctrl-0 = <&irmod_default>; + }; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index acdaa08b325c..74ec8348eed6 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -218,6 +218,12 @@ config PINCTRL_TZ1090 select PINMUX select GENERIC_PINCONF +config PINCTRL_TZ1090_PDC + bool "Toumaz Xenif TZ1090 PDC pin control driver" + depends on SOC_TZ1090 + select PINMUX + select PINCONF + config PINCTRL_U300 bool "U300 pin controller driver" depends on ARCH_U300 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 37ff29e1bba3..cf699a519e9a 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o +obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c new file mode 100644 index 000000000000..12e480869468 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c @@ -0,0 +1,1029 @@ +/* + * Pinctrl driver for the Toumaz Xenif TZ1090 PowerDown Controller pins + * + * Copyright (c) 2013, Imagination Technologies Ltd. + * + * Derived from Tegra code: + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * + * Derived from code: + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2009-2011 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The registers may be shared with other threads/cores, so we need to use the + * metag global lock2 for atomicity. + */ +#include + +#include "core.h" +#include "pinconf.h" + +/* Register offsets from bank base address */ +#define REG_GPIO_CONTROL0 0x00 +#define REG_GPIO_CONTROL2 0x08 + +/* Register field information */ +#define REG_GPIO_CONTROL2_PU_PD_S 16 +#define REG_GPIO_CONTROL2_PDC_POS_S 4 +#define REG_GPIO_CONTROL2_PDC_DR_S 2 +#define REG_GPIO_CONTROL2_PDC_SR_S 1 +#define REG_GPIO_CONTROL2_PDC_SCHMITT_S 0 + +/* PU_PD field values */ +#define REG_PU_PD_TRISTATE 0 +#define REG_PU_PD_UP 1 +#define REG_PU_PD_DOWN 2 +#define REG_PU_PD_REPEATER 3 + +/* DR field values */ +#define REG_DR_2mA 0 +#define REG_DR_4mA 1 +#define REG_DR_8mA 2 +#define REG_DR_12mA 3 + +/** + * struct tz1090_pdc_function - TZ1090 PDC pinctrl mux function + * @name: The name of the function, exported to pinctrl core. + * @groups: An array of pin groups that may select this function. + * @ngroups: The number of entries in @groups. + */ +struct tz1090_pdc_function { + const char *name; + const char * const *groups; + unsigned int ngroups; +}; + +/** + * struct tz1090_pdc_pingroup - TZ1090 PDC pin group + * @name: Name of pin group. + * @pins: Array of pin numbers in this pin group. + * @npins: Number of pins in this pin group. + * @func: Function enabled by the mux. + * @reg: Mux register offset. + * @bit: Mux register bit. + * @drv: Drive control supported, otherwise it's a mux. + * This means Schmitt, Slew, and Drive strength. + * + * A representation of a group of pins (possibly just one pin) in the TZ1090 + * PDC pin controller. Each group allows some parameter or parameters to be + * configured. The most common is mux function selection. + */ +struct tz1090_pdc_pingroup { + const char *name; + const unsigned int *pins; + unsigned int npins; + int func; + u16 reg; + u8 bit; + bool drv; +}; + +/* + * All PDC pins can be GPIOs. Define these first to match how the GPIO driver + * names/numbers its pins. + */ + +enum tz1090_pdc_pin { + TZ1090_PDC_PIN_GPIO0, + TZ1090_PDC_PIN_GPIO1, + TZ1090_PDC_PIN_SYS_WAKE0, + TZ1090_PDC_PIN_SYS_WAKE1, + TZ1090_PDC_PIN_SYS_WAKE2, + TZ1090_PDC_PIN_IR_DATA, + TZ1090_PDC_PIN_EXT_POWER, +}; + +/* Pin names */ + +static const struct pinctrl_pin_desc tz1090_pdc_pins[] = { + /* PDC GPIOs */ + PINCTRL_PIN(TZ1090_PDC_PIN_GPIO0, "gpio0"), + PINCTRL_PIN(TZ1090_PDC_PIN_GPIO1, "gpio1"), + PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE0, "sys_wake0"), + PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE1, "sys_wake1"), + PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE2, "sys_wake2"), + PINCTRL_PIN(TZ1090_PDC_PIN_IR_DATA, "ir_data"), + PINCTRL_PIN(TZ1090_PDC_PIN_EXT_POWER, "ext_power"), +}; + +/* Pin group pins */ + +static const unsigned int gpio0_pins[] = { + TZ1090_PDC_PIN_GPIO0, +}; + +static const unsigned int gpio1_pins[] = { + TZ1090_PDC_PIN_GPIO1, +}; + +static const unsigned int pdc_pins[] = { + TZ1090_PDC_PIN_GPIO0, + TZ1090_PDC_PIN_GPIO1, + TZ1090_PDC_PIN_SYS_WAKE0, + TZ1090_PDC_PIN_SYS_WAKE1, + TZ1090_PDC_PIN_SYS_WAKE2, + TZ1090_PDC_PIN_IR_DATA, + TZ1090_PDC_PIN_EXT_POWER, +}; + +/* Mux functions */ + +enum tz1090_pdc_mux { + /* PDC_GPIO0 mux */ + TZ1090_PDC_MUX_IR_MOD_STABLE_OUT, + /* PDC_GPIO1 mux */ + TZ1090_PDC_MUX_IR_MOD_POWER_OUT, +}; + +/* Pin groups a function can be muxed to */ + +static const char * const gpio0_groups[] = { + "gpio0", +}; + +static const char * const gpio1_groups[] = { + "gpio1", +}; + +#define FUNCTION(mux, fname, group) \ + [(TZ1090_PDC_MUX_ ## mux)] = { \ + .name = #fname, \ + .groups = group##_groups, \ + .ngroups = ARRAY_SIZE(group##_groups), \ + } + +/* Must correlate with enum tz1090_pdc_mux */ +static const struct tz1090_pdc_function tz1090_pdc_functions[] = { + /* MUX fn pingroups */ + FUNCTION(IR_MOD_STABLE_OUT, ir_mod_stable_out, gpio0), + FUNCTION(IR_MOD_POWER_OUT, ir_mod_power_out, gpio1), +}; + +/** + * MUX_PG() - Initialise a pin group with mux control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * @f0: Function 0 (TZ1090_PDC_MUX_ is prepended) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register of mux field + */ +#define MUX_PG(pg_name, f0, mux_r, mux_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .func = TZ1090_PDC_MUX_ ## f0, \ + .reg = (REG_ ## mux_r), \ + .bit = (mux_b), \ + } + +/** + * DRV_PG() - Initialise a pin group with drive control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + */ +#define DRV_PG(pg_name) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .drv = true, \ + } + +static const struct tz1090_pdc_pingroup tz1090_pdc_groups[] = { + /* Muxing pin groups */ + /* pg_name, f0, mux register, mux bit */ + MUX_PG(gpio0, IR_MOD_STABLE_OUT, GPIO_CONTROL0, 7), + MUX_PG(gpio1, IR_MOD_POWER_OUT, GPIO_CONTROL0, 6), + + /* Drive pin groups */ + /* pg_name */ + DRV_PG(pdc), +}; + +/** + * struct tz1090_pdc_pmx - Private pinctrl data + * @dev: Platform device + * @pctl: Pin control device + * @regs: Register region + * @lock: Lock protecting coherency of mux_en and gpio_en + * @mux_en: Muxes that have been enabled + * @gpio_en: Muxable GPIOs that have been enabled + */ +struct tz1090_pdc_pmx { + struct device *dev; + struct pinctrl_dev *pctl; + void __iomem *regs; + spinlock_t lock; + u32 mux_en; + u32 gpio_en; +}; + +static inline u32 pmx_read(struct tz1090_pdc_pmx *pmx, u32 reg) +{ + return ioread32(pmx->regs + reg); +} + +static inline void pmx_write(struct tz1090_pdc_pmx *pmx, u32 val, u32 reg) +{ + iowrite32(val, pmx->regs + reg); +} + +/* + * Pin control operations + */ + +static int tz1090_pdc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_pdc_groups); +} + +static const char *tz1090_pdc_pinctrl_get_group_name(struct pinctrl_dev *pctl, + unsigned int group) +{ + return tz1090_pdc_groups[group].name; +} + +static int tz1090_pdc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = tz1090_pdc_groups[group].pins; + *num_pins = tz1090_pdc_groups[group].npins; + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void tz1090_pdc_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int offset) +{ + seq_printf(s, " %s", dev_name(pctldev->dev)); +} +#endif + +static int reserve_map(struct device *dev, struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + unsigned int reserve) +{ + unsigned int old_num = *reserved_maps; + unsigned int new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) { + dev_err(dev, "krealloc(map) failed\n"); + return -ENOMEM; + } + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + const char *function) +{ + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +/** + * get_group_selector() - returns the group selector for a group + * @pin_group: the pin group to look up + * + * This is the same as pinctrl_get_group_selector except it doesn't produce an + * error message if the group isn't found or debug messages. + */ +static int get_group_selector(const char *pin_group) +{ + unsigned int group; + + for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group) + if (!strcmp(tz1090_pdc_groups[group].name, pin_group)) + return group; + + return -EINVAL; +} + +static int add_map_configs(struct device *dev, + struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + const char *group, unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *dup_configs; + enum pinctrl_map_type type; + + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) { + dev_err(dev, "kmemdup(configs) failed\n"); + return -ENOMEM; + } + + /* + * We support both pins and pin groups, but we need to figure out which + * one we have. + */ + if (get_group_selector(group) >= 0) + type = PIN_MAP_TYPE_CONFIGS_GROUP; + else + type = PIN_MAP_TYPE_CONFIGS_PIN; + (*map)[*num_maps].type = type; + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +static void tz1090_pdc_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned int num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + + kfree(map); +} + +static int tz1090_pdc_pinctrl_dt_subnode_to_map(struct device *dev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *reserved_maps, + unsigned int *num_maps) +{ + int ret; + const char *function; + unsigned long *configs = NULL; + unsigned int num_configs = 0; + unsigned int reserve; + struct property *prop; + const char *group; + + ret = of_property_read_string(np, "tz1090,function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, + "could not parse property function\n"); + function = NULL; + } + + ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + if (ret) + return ret; + + reserve = 0; + if (function != NULL) + reserve++; + if (num_configs) + reserve++; + ret = of_property_count_strings(np, "tz1090,pins"); + if (ret < 0) { + dev_err(dev, "could not parse property pins\n"); + goto exit; + } + reserve *= ret; + + ret = reserve_map(dev, map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "tz1090,pins", prop, group) { + if (function) { + ret = add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + + if (num_configs) { + ret = add_map_configs(dev, map, reserved_maps, + num_maps, group, configs, + num_configs); + if (ret < 0) + goto exit; + } + } + + ret = 0; + +exit: + kfree(configs); + return ret; +} + +static int tz1090_pdc_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + unsigned int reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = tz1090_pdc_pinctrl_dt_subnode_to_map(pctldev->dev, np, + map, &reserved_maps, + num_maps); + if (ret < 0) { + tz1090_pdc_pinctrl_dt_free_map(pctldev, *map, + *num_maps); + return ret; + } + } + + return 0; +} + +static struct pinctrl_ops tz1090_pdc_pinctrl_ops = { + .get_groups_count = tz1090_pdc_pinctrl_get_groups_count, + .get_group_name = tz1090_pdc_pinctrl_get_group_name, + .get_group_pins = tz1090_pdc_pinctrl_get_group_pins, +#ifdef CONFIG_DEBUG_FS + .pin_dbg_show = tz1090_pdc_pinctrl_pin_dbg_show, +#endif + .dt_node_to_map = tz1090_pdc_pinctrl_dt_node_to_map, + .dt_free_map = tz1090_pdc_pinctrl_dt_free_map, +}; + +/* + * Pin mux operations + */ + +static int tz1090_pdc_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_pdc_functions); +} + +static const char *tz1090_pdc_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return tz1090_pdc_functions[function].name; +} + +static int tz1090_pdc_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = tz1090_pdc_functions[function].groups; + *num_groups = tz1090_pdc_functions[function].ngroups; + + return 0; +} + +/** + * tz1090_pdc_pinctrl_mux() - update mux bit + * @pmx: Pinmux data + * @grp: Pin mux group + */ +static void tz1090_pdc_pinctrl_mux(struct tz1090_pdc_pmx *pmx, + const struct tz1090_pdc_pingroup *grp) +{ + u32 reg, select; + unsigned int pin_shift = grp->pins[0]; + unsigned long flags; + + /* select = mux && !gpio */ + select = ((pmx->mux_en & ~pmx->gpio_en) >> pin_shift) & 1; + + /* set up the mux */ + __global_lock2(flags); + reg = pmx_read(pmx, grp->reg); + reg &= ~BIT(grp->bit); + reg |= select << grp->bit; + pmx_write(pmx, reg, grp->reg); + __global_unlock2(flags); +} + +static int tz1090_pdc_pinctrl_enable(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group]; + + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n", + __func__, + function, tz1090_pdc_functions[function].name, + group, tz1090_pdc_groups[group].name); + + /* is it even a mux? */ + if (grp->drv) + return -EINVAL; + + /* does this group even control the function? */ + if (function != grp->func) + return -EINVAL; + + /* record the pin being muxed and update mux bit */ + spin_lock(&pmx->lock); + pmx->mux_en |= BIT(grp->pins[0]); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); + return 0; +} + +static void tz1090_pdc_pinctrl_disable(struct pinctrl_dev *pctldev, + unsigned int function, + unsigned int group) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group]; + + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n", + __func__, + function, tz1090_pdc_functions[function].name, + group, tz1090_pdc_groups[group].name); + + /* is it even a mux? */ + if (grp->drv) + return; + + /* does this group even control the function? */ + if (function != grp->func) + return; + + /* record the pin being unmuxed and update mux bit */ + spin_lock(&pmx->lock); + pmx->mux_en &= ~BIT(grp->pins[0]); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); +} + +static const struct tz1090_pdc_pingroup *find_mux_group( + struct tz1090_pdc_pmx *pmx, + unsigned int pin) +{ + const struct tz1090_pdc_pingroup *grp; + unsigned int group; + + grp = tz1090_pdc_groups; + for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group, ++grp) { + /* only match muxes */ + if (grp->drv) + continue; + + /* with a matching pin */ + if (grp->pins[0] == pin) + return grp; + } + + return NULL; +} + +static int tz1090_pdc_pinctrl_gpio_request_enable( + struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin); + + if (grp) { + /* record the pin in GPIO use and update mux bit */ + spin_lock(&pmx->lock); + pmx->gpio_en |= BIT(pin); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); + } + return 0; +} + +static void tz1090_pdc_pinctrl_gpio_disable_free( + struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin); + + if (grp) { + /* record the pin not in GPIO use and update mux bit */ + spin_lock(&pmx->lock); + pmx->gpio_en &= ~BIT(pin); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); + } +} + +static struct pinmux_ops tz1090_pdc_pinmux_ops = { + .get_functions_count = tz1090_pdc_pinctrl_get_funcs_count, + .get_function_name = tz1090_pdc_pinctrl_get_func_name, + .get_function_groups = tz1090_pdc_pinctrl_get_func_groups, + .enable = tz1090_pdc_pinctrl_enable, + .disable = tz1090_pdc_pinctrl_disable, + .gpio_request_enable = tz1090_pdc_pinctrl_gpio_request_enable, + .gpio_disable_free = tz1090_pdc_pinctrl_gpio_disable_free, +}; + +/* + * Pin config operations + */ + +static int tz1090_pdc_pinconf_reg(struct pinctrl_dev *pctldev, + unsigned int pin, + enum pin_config_param param, + bool report_err, + u32 *reg, u32 *width, u32 *mask, u32 *shift, + u32 *val) +{ + /* Find information about parameter's register */ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + *val = REG_PU_PD_TRISTATE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + *val = REG_PU_PD_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + *val = REG_PU_PD_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + *val = REG_PU_PD_REPEATER; + break; + default: + return -ENOTSUPP; + }; + + /* Only input bias parameters supported */ + *reg = REG_GPIO_CONTROL2; + *shift = REG_GPIO_CONTROL2_PU_PD_S + pin*2; + *width = 2; + + /* Calculate field information */ + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pdc_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int ret; + u32 reg, width, mask, shift, val, tmp, arg; + + /* Get register information */ + ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Extract field from register */ + tmp = pmx_read(pmx, reg); + arg = ((tmp & mask) >> shift) == val; + + /* Config not active */ + if (!arg) + return -EINVAL; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pdc_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(config); + unsigned int arg = pinconf_to_config_argument(config); + int ret; + u32 reg, width, mask, shift, val, tmp; + unsigned long flags; + + dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", + __func__, tz1090_pdc_pins[pin].name, config); + + /* Get register information */ + ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Unpack argument and range check it */ + if (arg > 1) { + dev_dbg(pctldev->dev, "%s: arg %u out of range\n", + __func__, arg); + return -EINVAL; + } + + /* Write register field */ + __global_lock2(flags); + tmp = pmx_read(pmx, reg); + tmp &= ~mask; + if (arg) + tmp |= val << shift; + pmx_write(pmx, tmp, reg); + __global_unlock2(flags); + + return 0; +} + +static const int tz1090_pdc_boolean_map[] = { + [0] = -EINVAL, + [1] = 1, +}; + +static const int tz1090_pdc_dr_map[] = { + [REG_DR_2mA] = 2, + [REG_DR_4mA] = 4, + [REG_DR_8mA] = 8, + [REG_DR_12mA] = 12, +}; + +static int tz1090_pdc_pinconf_group_reg(struct pinctrl_dev *pctldev, + const struct tz1090_pdc_pingroup *g, + enum pin_config_param param, + bool report_err, u32 *reg, u32 *width, + u32 *mask, u32 *shift, const int **map) +{ + /* Drive configuration applies in groups, but not to all groups. */ + if (!g->drv) { + if (report_err) + dev_dbg(pctldev->dev, + "%s: group %s has no drive control\n", + __func__, g->name); + return -ENOTSUPP; + } + + /* Find information about drive parameter's register */ + *reg = REG_GPIO_CONTROL2; + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + *shift = REG_GPIO_CONTROL2_PDC_SCHMITT_S; + *width = 1; + *map = tz1090_pdc_boolean_map; + break; + case PIN_CONFIG_SLEW_RATE: + *shift = REG_GPIO_CONTROL2_PDC_SR_S; + *width = 1; + *map = tz1090_pdc_boolean_map; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + *shift = REG_GPIO_CONTROL2_PDC_DR_S; + *width = 2; + *map = tz1090_pdc_dr_map; + break; + case PIN_CONFIG_LOW_POWER_MODE: + *shift = REG_GPIO_CONTROL2_PDC_POS_S; + *width = 1; + *map = tz1090_pdc_boolean_map; + break; + default: + return -ENOTSUPP; + }; + + /* Calculate field information */ + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pdc_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group]; + enum pin_config_param param = pinconf_to_config_param(*config); + int ret, arg; + u32 reg, width, mask, shift, val; + const int *map; + + /* Get register information */ + ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) + return ret; + + /* Extract field from register */ + val = pmx_read(pmx, reg); + arg = map[(val & mask) >> shift]; + if (arg < 0) + return arg; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pdc_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group]; + enum pin_config_param param = pinconf_to_config_param(config); + const unsigned int *pit; + unsigned int i; + int ret, arg; + u32 reg, width, mask, shift, val; + unsigned long flags; + const int *map; + + dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", + __func__, g->name, config); + + /* Get register information */ + ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) { + /* + * Maybe we're trying to set a per-pin configuration of a group, + * so do the pins one by one. This is mainly as a convenience. + */ + for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { + ret = tz1090_pdc_pinconf_set(pctldev, *pit, config); + if (ret) + return ret; + } + return 0; + } + + /* Unpack argument and map it to register value */ + arg = pinconf_to_config_argument(config); + for (i = 0; i < BIT(width); ++i) { + if (map[i] == arg || (map[i] == -EINVAL && !arg)) { + /* Write register field */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~mask; + val |= i << shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); + return 0; + } + } + + dev_dbg(pctldev->dev, "%s: arg %u not supported\n", + __func__, arg); + return 0; +} + +static struct pinconf_ops tz1090_pdc_pinconf_ops = { + .is_generic = true, + .pin_config_get = tz1090_pdc_pinconf_get, + .pin_config_set = tz1090_pdc_pinconf_set, + .pin_config_group_get = tz1090_pdc_pinconf_group_get, + .pin_config_group_set = tz1090_pdc_pinconf_group_set, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +/* + * Pin control driver setup + */ + +static struct pinctrl_desc tz1090_pdc_pinctrl_desc = { + .pctlops = &tz1090_pdc_pinctrl_ops, + .pmxops = &tz1090_pdc_pinmux_ops, + .confops = &tz1090_pdc_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int tz1090_pdc_pinctrl_probe(struct platform_device *pdev) +{ + struct tz1090_pdc_pmx *pmx; + struct resource *res; + + pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + if (!pmx) { + dev_err(&pdev->dev, "Can't alloc tz1090_pdc_pmx\n"); + return -ENOMEM; + } + pmx->dev = &pdev->dev; + spin_lock_init(&pmx->lock); + + tz1090_pdc_pinctrl_desc.name = dev_name(&pdev->dev); + tz1090_pdc_pinctrl_desc.pins = tz1090_pdc_pins; + tz1090_pdc_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pdc_pins); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, + "Couldn't request MEM resource\n"); + return -ENODEV; + } + + pmx->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!pmx->regs) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + return -ENODEV; + } + + pmx->pctl = pinctrl_register(&tz1090_pdc_pinctrl_desc, &pdev->dev, pmx); + if (!pmx->pctl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, pmx); + + dev_info(&pdev->dev, "TZ1090 PDC pinctrl driver initialised\n"); + + return 0; +} + +static int tz1090_pdc_pinctrl_remove(struct platform_device *pdev) +{ + struct tz1090_pdc_pmx *pmx = platform_get_drvdata(pdev); + + pinctrl_unregister(pmx->pctl); + + return 0; +} + +static struct of_device_id tz1090_pdc_pinctrl_of_match[] = { + { .compatible = "img,tz1090-pdc-pinctrl", }, + { }, +}; + +static struct platform_driver tz1090_pdc_pinctrl_driver = { + .driver = { + .name = "tz1090-pdc-pinctrl", + .owner = THIS_MODULE, + .of_match_table = tz1090_pdc_pinctrl_of_match, + }, + .probe = tz1090_pdc_pinctrl_probe, + .remove = tz1090_pdc_pinctrl_remove, +}; + +static int __init tz1090_pdc_pinctrl_init(void) +{ + return platform_driver_register(&tz1090_pdc_pinctrl_driver); +} +arch_initcall(tz1090_pdc_pinctrl_init); + +static void __exit tz1090_pdc_pinctrl_exit(void) +{ + platform_driver_unregister(&tz1090_pdc_pinctrl_driver); +} +module_exit(tz1090_pdc_pinctrl_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("Toumaz Xenif TZ1090 PDC pinctrl driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, tz1090_pdc_pinctrl_of_match); -- cgit v1.2.3