summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-vf610.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-vf610.c')
-rw-r--r--drivers/gpio/gpio-vf610.c137
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);