diff options
author | Andrey Smirnov <andrew.smirnov@gmail.com> | 2016-11-09 08:13:55 -0800 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-11-14 08:58:01 +0100 |
commit | 49877e312509de9d0bb391caba37c8a2858cb822 (patch) | |
tree | 7fc62f00f2d601201e5805a0cc3e93ca6657df73 /drivers/pinctrl/pinctrl-vf610.c | |
parent | a4e1f15b6be34b3d8b79dee9d47581c975df5fa3 (diff) | |
download | barebox-49877e312509de9d0bb391caba37c8a2858cb822.tar.gz barebox-49877e312509de9d0bb391caba37c8a2858cb822.tar.xz |
i.MX: Add pinctrl driver for VF610
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/pinctrl/pinctrl-vf610.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-vf610.c | 167 |
1 files changed, 167 insertions, 0 deletions
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 <andrew.smirnov@gmail.com> + * + * 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 <common.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <pinctrl.h> +#include <malloc.h> +#include <gpio.h> + +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); |