gpio: Add DW APB GPIO driver
Add driver for the DesignWare APB GPIO IP block.
This driver is DM capable and probes from DT.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Simon Glass <sjg@chromium.org>
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 5934597..9a5d29d 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -14,6 +14,13 @@
particular GPIOs that they provide. The uclass interface
is defined in include/asm-generic/gpio.h.
+config DWAPB_GPIO
+ bool "DWAPB GPIO driver"
+ depends on DM && DM_GPIO
+ default n
+ help
+ Support for the Designware APB GPIO driver.
+
config LPC32XX_GPIO
bool "LPC32XX GPIO driver"
depends on DM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 26f2574..ba18594 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -6,6 +6,7 @@
#
ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_DWAPB_GPIO) += dwapb_gpio.o
obj-$(CONFIG_AXP_GPIO) += axp_gpio.o
endif
obj-$(CONFIG_DM_GPIO) += gpio-uclass.o
diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c
new file mode 100644
index 0000000..72cec48
--- /dev/null
+++ b/drivers/gpio/dwapb_gpio.c
@@ -0,0 +1,167 @@
+/*
+ * (C) Copyright 2015 Marek Vasut <marex@denx.de>
+ *
+ * DesignWare APB GPIO driver
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/arch/gpio.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define GPIO_SWPORTA_DR 0x00
+#define GPIO_SWPORTA_DDR 0x04
+#define GPIO_INTEN 0x30
+#define GPIO_INTMASK 0x34
+#define GPIO_INTTYPE_LEVEL 0x38
+#define GPIO_INT_POLARITY 0x3c
+#define GPIO_INTSTATUS 0x40
+#define GPIO_PORTA_DEBOUNCE 0x48
+#define GPIO_PORTA_EOI 0x4c
+#define GPIO_EXT_PORTA 0x50
+
+struct gpio_dwapb_platdata {
+ const char *name;
+ int bank;
+ int pins;
+ fdt_addr_t base;
+};
+
+static int dwapb_gpio_direction_input(struct udevice *dev, unsigned pin)
+{
+ struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+
+ clrbits_le32(plat->base + GPIO_SWPORTA_DDR, 1 << pin);
+ return 0;
+}
+
+static int dwapb_gpio_direction_output(struct udevice *dev, unsigned pin,
+ int val)
+{
+ struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+
+ setbits_le32(plat->base + GPIO_SWPORTA_DDR, 1 << pin);
+
+ if (val)
+ setbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+ else
+ clrbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+
+ return 0;
+}
+
+static int dwapb_gpio_get_value(struct udevice *dev, unsigned pin)
+{
+ struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+ return !!(readl(plat->base + GPIO_EXT_PORTA) & (1 << pin));
+}
+
+
+static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val)
+{
+ struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+
+ if (val)
+ setbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+ else
+ clrbits_le32(plat->base + GPIO_SWPORTA_DR, 1 << pin);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_dwapb_ops = {
+ .direction_input = dwapb_gpio_direction_input,
+ .direction_output = dwapb_gpio_direction_output,
+ .get_value = dwapb_gpio_get_value,
+ .set_value = dwapb_gpio_set_value,
+};
+
+static int gpio_dwapb_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
+ struct gpio_dwapb_platdata *plat = dev->platdata;
+
+ if (!plat)
+ return 0;
+
+ priv->gpio_count = plat->pins;
+ priv->bank_name = plat->name;
+
+ return 0;
+}
+
+static int gpio_dwapb_bind(struct udevice *dev)
+{
+ struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
+ const void *blob = gd->fdt_blob;
+ struct udevice *subdev;
+ fdt_addr_t base;
+ int ret, node, bank = 0;
+
+ /* If this is a child device, there is nothing to do here */
+ if (plat)
+ return 0;
+
+ base = fdtdec_get_addr(blob, dev->of_offset, "reg");
+ if (base == FDT_ADDR_T_NONE) {
+ debug("Can't get the GPIO register base address\n");
+ return -ENXIO;
+ }
+
+ for (node = fdt_first_subnode(blob, dev->of_offset);
+ node > 0;
+ node = fdt_next_subnode(blob, node)) {
+ if (!fdtdec_get_bool(blob, node, "gpio-controller"))
+ continue;
+
+ plat = NULL;
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+
+ plat->base = base;
+ plat->bank = bank;
+ plat->pins = fdtdec_get_int(blob, node, "snps,nr-gpios", 0);
+ ret = fdt_get_string(blob, node, "bank-name", &plat->name);
+ if (ret)
+ goto err;
+
+ ret = device_bind(dev, dev->driver, plat->name,
+ plat, -1, &subdev);
+ if (ret)
+ goto err;
+
+ subdev->of_offset = node;
+ bank++;
+ }
+
+ return 0;
+
+err:
+ free(plat);
+ return ret;
+}
+
+static const struct udevice_id gpio_dwapb_ids[] = {
+ { .compatible = "snps,dw-apb-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_dwapb) = {
+ .name = "gpio-dwapb",
+ .id = UCLASS_GPIO,
+ .of_match = gpio_dwapb_ids,
+ .ops = &gpio_dwapb_ops,
+ .bind = gpio_dwapb_bind,
+ .probe = gpio_dwapb_probe,
+};