From a4e1f15b6be34b3d8b79dee9d47581c975df5fa3 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 9 Nov 2016 08:13:54 -0800 Subject: pinctrl: Add provisions to control GPIO pin direction Some SoC (of which Vybrid is a one example) relegate GPIO direction control to their pinmux IP block, instead of having that functionality within GPIO IP. Add provisions to control that aspect of pinmux to support such SoCs. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/pinctrl/pinctrl.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c index e7b08a2bfe..bef4fcdba1 100644 --- a/drivers/pinctrl/pinctrl.c +++ b/drivers/pinctrl/pinctrl.c @@ -24,6 +24,51 @@ static LIST_HEAD(pinctrl_list); +static struct pinctrl_device *pin_to_pinctrl(unsigned int pin) +{ + struct pinctrl_device *pinctrl; + + list_for_each_entry(pinctrl, &pinctrl_list, list) + if (pin > pinctrl->base && + pin < pinctrl->base + pinctrl->npins) + return pinctrl; + return NULL; +} + +static int pinctrl_gpio_direction(unsigned pin, bool input) +{ + struct pinctrl_device *pinctrl = pin_to_pinctrl(pin); + + if (!pinctrl) + return -EINVAL; + + BUG_ON(!pinctrl->ops->set_direction); + + return pinctrl->ops->set_direction(pinctrl, pin, input); +} + +int pinctrl_gpio_direction_input(unsigned int pin) +{ + return pinctrl_gpio_direction(pin, true); +} + +int pinctrl_gpio_direction_output(unsigned int pin) +{ + return pinctrl_gpio_direction(pin, false); +} + +int pinctrl_gpio_get_direction(unsigned pin) +{ + struct pinctrl_device *pinctrl = pin_to_pinctrl(pin); + + if (!pinctrl) + return -EINVAL; + + BUG_ON(!pinctrl->ops->get_direction); + + return pinctrl->ops->get_direction(pinctrl, pin); +} + static struct pinctrl_device *find_pinctrl(struct device_node *node) { struct pinctrl_device *pdev; -- cgit v1.2.3 From 49877e312509de9d0bb391caba37c8a2858cb822 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 9 Nov 2016 08:13:55 -0800 Subject: i.MX: Add pinctrl driver for VF610 Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/pinctrl/Kconfig | 5 ++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-vf610.c | 167 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-vf610.c (limited to 'drivers/pinctrl') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 5c69928e75..12fff4f010 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -77,4 +77,9 @@ config PINCTRL_TEGRA_XUSB source drivers/pinctrl/mvebu/Kconfig +config PINCTRL_VF610 + bool + default y if ARCH_VF610 + help + Pinmux controller found on Vybrid VF610 family of SoCs endif diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index af9b30df0f..9450dbbdf5 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o +obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o obj-$(CONFIG_ARCH_MVEBU) += mvebu/ diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c new file mode 100644 index 0000000000..b479bf20e6 --- /dev/null +++ b/drivers/pinctrl/pinctrl-vf610.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016 Zodiac Inflight Innovation + * Author: Andrey Smirnov + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + +enum { + PINCTRL_VF610_MUX_LINE_SIZE = 20, + PINCTRL_VF610_MUX_SHIFT = 20, + + PINCTRL_VF610_IBE = 1 << 0, + PINCTRL_VF610_OBE = 1 << 1, + PINCTRL_VF610_xBE = 0b11, +}; + +struct pinctrl_vf610 { + void __iomem *base; + struct pinctrl_device pinctrl; +}; + +static int pinctrl_vf610_set_state(struct pinctrl_device *pdev, + struct device_node *np) +{ + const __be32 *list; + int npins, size, i; + + struct pinctrl_vf610 *iomux = + container_of(pdev, struct pinctrl_vf610, pinctrl); + + list = of_get_property(np, "fsl,pins", &size); + if (!list) + return -EINVAL; + + if (!size || size % PINCTRL_VF610_MUX_LINE_SIZE) { + dev_err(pdev->dev, "Invalid fsl,pins property in %s\n", + np->full_name); + return -EINVAL; + } + + npins = size / PINCTRL_VF610_MUX_LINE_SIZE; + + for (i = 0; i < npins; i++) { + u32 mux_reg = be32_to_cpu(*list++); + u32 input_reg = be32_to_cpu(*list++); + u32 mux_val = be32_to_cpu(*list++); + u32 input_val = be32_to_cpu(*list++); + u32 conf_val = be32_to_cpu(*list++); + + writel(mux_val << PINCTRL_VF610_MUX_SHIFT | conf_val, + iomux->base + mux_reg); + + if (input_reg) + writel(input_val, iomux->base + input_reg); + } + + return 0; +} + +static int pinctrl_vf610_set_direction(struct pinctrl_device *pdev, + unsigned int pin, bool input) +{ + u32 pad_cr; + const u32 off = pin * sizeof(u32); + struct pinctrl_vf610 *iomux = + container_of(pdev, struct pinctrl_vf610, pinctrl); + + pad_cr = readl(iomux->base + off); + + if (input) { + pad_cr |= PINCTRL_VF610_IBE; + pad_cr &= ~PINCTRL_VF610_OBE; + } else { + pad_cr &= ~PINCTRL_VF610_IBE; + pad_cr |= PINCTRL_VF610_OBE; + } + + writel(pad_cr, iomux->base + off); + + return 0; +} + +static int pinctrl_vf610_get_direction(struct pinctrl_device *pdev, + unsigned int pin) +{ + const u32 off = pin * sizeof(u32); + struct pinctrl_vf610 *iomux = + container_of(pdev, struct pinctrl_vf610, pinctrl); + + const u32 pad_cr = readl(iomux->base + off); + + switch (pad_cr & PINCTRL_VF610_xBE) { + case PINCTRL_VF610_IBE: + return GPIOF_DIR_IN; + case PINCTRL_VF610_OBE: + return GPIOF_DIR_OUT; + default: + return -EINVAL; + } +} + +static struct pinctrl_ops pinctrl_vf610_ops = { + .set_state = pinctrl_vf610_set_state, + .set_direction = pinctrl_vf610_set_direction, + .get_direction = pinctrl_vf610_get_direction, +}; + +static int pinctrl_vf610_probe(struct device_d *dev) +{ + int ret; + struct resource *io; + struct pinctrl_vf610 *iomux; + + iomux = xzalloc(sizeof(*iomux)); + + io = dev_request_mem_resource(dev, 0); + if (IS_ERR(io)) + return PTR_ERR(io); + + iomux->base = IOMEM(io->start); + iomux->pinctrl.dev = dev; + iomux->pinctrl.ops = &pinctrl_vf610_ops; + iomux->pinctrl.base = 0; + iomux->pinctrl.npins = ARCH_NR_GPIOS; + + ret = pinctrl_register(&iomux->pinctrl); + if (ret) + free(iomux); + + return ret; +} + +static __maybe_unused struct of_device_id pinctrl_vf610_dt_ids[] = { + { .compatible = "fsl,vf610-iomuxc", }, + { /* sentinel */ } +}; + +static struct driver_d pinctrl_vf610_driver = { + .name = "vf610-pinctrl", + .probe = pinctrl_vf610_probe, + .of_compatible = DRV_OF_COMPAT(pinctrl_vf610_dt_ids), +}; + +static int pinctrl_vf610_init(void) +{ + return platform_driver_register(&pinctrl_vf610_driver); +} +postcore_initcall(pinctrl_vf610_init); -- cgit v1.2.3