diff options
Diffstat (limited to 'drivers/gpio/gpio-vf610.c')
-rw-r--r-- | drivers/gpio/gpio-vf610.c | 137 |
1 files changed, 102 insertions, 35 deletions
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index ab35310fbe..7c535c2e5e 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2014 Toradex AG (Stefan Agner <stefan@agner.ch>) + /* * vf610 GPIO support through PORT and GPIO module - * - * Copyright (c) 2014 Toradex AG. - * - * Author: Stefan Agner <stefan@agner.ch>. - * - * 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. */ #include <common.h> @@ -31,6 +21,8 @@ struct vf610_gpio_port { struct gpio_chip chip; void __iomem *gpio_base; unsigned int pinctrl_base; + bool have_paddr; + bool need_pinctrl; }; #define GPIO_PDOR 0x00 @@ -38,19 +30,46 @@ struct vf610_gpio_port { #define GPIO_PCOR 0x08 #define GPIO_PTOR 0x0c #define GPIO_PDIR 0x10 +#define GPIO_PDDR 0x14 + +struct fsl_gpio_soc_data { + /* SoCs has a Port Data Direction Register (PDDR) */ + bool have_paddr; + bool need_pinctrl; +}; + +static const struct fsl_gpio_soc_data vf610_data = { + .need_pinctrl = true, +}; + +static const struct fsl_gpio_soc_data imx_data = { + .have_paddr = true, +}; static const struct of_device_id vf610_gpio_dt_ids[] = { - { .compatible = "fsl,vf610-gpio" }, + { .compatible = "fsl,vf610-gpio", .data = &vf610_data, }, + { .compatible = "fsl,imx7ulp-gpio", .data = &imx_data, }, + { .compatible = "fsl,imx8ulp-gpio", .data = &imx_data, }, + { .compatible = "fsl,imx93-gpio", .data = &imx_data, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, vf610_gpio_dt_ids); static int vf610_gpio_get_value(struct gpio_chip *chip, unsigned int gpio) { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + unsigned long offset = GPIO_PDIR; - return !!(readl(port->gpio_base + GPIO_PDIR) & BIT(gpio)); + if (port->have_paddr) { + mask &= readl(port->gpio_base + GPIO_PDDR); + if (mask) + offset = GPIO_PDOR; + } + + return !!(readl(port->gpio_base + offset) & BIT(gpio)); } static void vf610_gpio_set_value(struct gpio_chip *chip, @@ -67,8 +86,19 @@ static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + u32 val; - return pinctrl_gpio_direction_input(port->pinctrl_base + gpio); + if (port->have_paddr) { + val = readl(port->gpio_base + GPIO_PDDR); + val &= ~mask; + writel(val, port->gpio_base + GPIO_PDDR); + } + + if (port->need_pinctrl) + return pinctrl_gpio_direction_input(port->pinctrl_base + gpio); + + return 0; } static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, @@ -76,18 +106,41 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + u32 val; vf610_gpio_set_value(chip, gpio, value); - return pinctrl_gpio_direction_output(port->pinctrl_base + gpio); + if (port->have_paddr) { + val = readl(port->gpio_base + GPIO_PDDR); + val |= mask; + writel(val, port->gpio_base + GPIO_PDDR); + } + + if (port->need_pinctrl) + return pinctrl_gpio_direction_output(port->pinctrl_base + gpio); + + return 0; } static int vf610_gpio_get_direction(struct gpio_chip *chip, unsigned gpio) { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + u32 val; + + if (port->have_paddr) { + val = readl(port->gpio_base + GPIO_PDDR); + if (val & BIT(gpio)) + return GPIOF_DIR_OUT; + else + return GPIOF_DIR_IN; + } - return pinctrl_gpio_get_direction(port->pinctrl_base + gpio); + if (port->need_pinctrl) + return pinctrl_gpio_get_direction(port->pinctrl_base + gpio); + + return 0; } static struct gpio_ops vf610_gpio_ops = { @@ -98,38 +151,56 @@ static struct gpio_ops vf610_gpio_ops = { .get_direction = vf610_gpio_get_direction, }; -static int vf610_gpio_probe(struct device_d *dev) +static int vf610_gpio_probe(struct device *dev) { int ret, size; struct resource *iores; struct vf610_gpio_port *port; const __be32 *gpio_ranges; + struct fsl_gpio_soc_data *devtype; + + ret = dev_get_drvdata(dev, (const void **)&devtype); + if (ret) + return ret; port = xzalloc(sizeof(*port)); - gpio_ranges = of_get_property(dev->device_node, "gpio-ranges", &size); + gpio_ranges = of_get_property(dev->of_node, "gpio-ranges", &size); if (!gpio_ranges) { - dev_err(dev, "Couldn't read 'gpio-ranges' propery of %s\n", - dev->device_node->full_name); + dev_err(dev, "Couldn't read 'gpio-ranges' propery of %pOF\n", + dev->of_node); ret = -EINVAL; goto free_port; } + port->have_paddr = devtype->have_paddr; + port->need_pinctrl = devtype->need_pinctrl; + port->pinctrl_base = be32_to_cpu(gpio_ranges[PINCTRL_BASE]); - port->chip.ngpio = be32_to_cpu(gpio_ranges[COUNT]); + port->chip.ngpio = 32; + /* + * Some old bindings have two register ranges. When we have two ranges + * the GPIO base is in the second range. With only one range the GPIO + * base is at offset 0x40. + */ iores = dev_request_mem_resource(dev, 1); if (IS_ERR(iores)) { - ret = PTR_ERR(iores); - dev_dbg(dev, "Failed to request memory resource\n"); - goto free_port; + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + dev_dbg(dev, "Failed to request memory resource\n"); + goto free_port; + } else { + port->gpio_base = IOMEM(iores->start) + 0x40; + } + } else { + port->gpio_base = IOMEM(iores->start); } - port->gpio_base = IOMEM(iores->start); - port->chip.ops = &vf610_gpio_ops; if (dev->id < 0) { - port->chip.base = of_alias_get_id(dev->device_node, "gpio"); + port->chip.base = of_alias_get_id(dev->of_node, "gpio"); if (port->chip.base < 0) { ret = port->chip.base; dev_dbg(dev, "Failed to get GPIO alias\n"); @@ -150,14 +221,10 @@ free_port: return ret; } -static struct driver_d vf610_gpio_driver = { +static struct driver vf610_gpio_driver = { .name = "gpio-vf610", .probe = vf610_gpio_probe, .of_compatible = DRV_OF_COMPAT(vf610_gpio_dt_ids), }; -static int __init gpio_vf610_init(void) -{ - return platform_driver_register(&vf610_gpio_driver); -} -postcore_initcall(gpio_vf610_init); +postcore_platform_driver(vf610_gpio_driver); |