gpio: uniphier: add driver for UniPhier GPIO controller
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Tue, 16 Feb 2016 08:03:48 +0000 (17:03 +0900)
committerMasahiro Yamada <yamada.masahiro@socionext.com>
Sun, 28 Feb 2016 18:50:15 +0000 (03:50 +0900)
This GPIO controller device is used on UniPhier SoCs.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Simon Glass <sjg@chromium.org>
doc/README.uniphier
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-uniphier.c [new file with mode: 0644]

index bcf0ac3d4b5147b34542fccec518209f1431c388..f03c20700cb171ffe76e2421622932e4fb99a72c 100644 (file)
@@ -95,6 +95,7 @@ Supported devices
  - NAND
  - USB 2.0 (EHCI)
  - USB 3.0 (xHCI)
+ - GPIO
  - LAN (on-board SMSC9118)
  - I2C
  - EEPROM (connected to the on-board I2C bus)
index 845dc725c54385efed371a837240bfc017254a81..94fabb9e18f86f7a1aecc20e9876a10a65297e8f 100644 (file)
@@ -76,6 +76,12 @@ config SANDBOX_GPIO_COUNT
          of 'anonymous' GPIOs that do not belong to any device or bank.
          Select a suitable value depending on your needs.
 
+config GPIO_UNIPHIER
+       bool "UniPhier GPIO"
+       depends on ARCH_UNIPHIER
+       help
+         Say yes here to support UniPhier GPIOs.
+
 config VYBRID_GPIO
        bool "Vybrid GPIO driver"
        depends on DM
index 845a6d4493b955355eaf27c53c9b6f30f643d429..ca8c4879e4abf5e80f8f230a5534d03fa5bb1c74 100644 (file)
@@ -43,6 +43,7 @@ oby-$(CONFIG_SX151X)          += sx151x.o
 obj-$(CONFIG_SUNXI_GPIO)       += sunxi_gpio.o
 obj-$(CONFIG_LPC32XX_GPIO)     += lpc32xx_gpio.o
 obj-$(CONFIG_STM32_GPIO)       += stm32_gpio.o
+obj-$(CONFIG_GPIO_UNIPHIER)    += gpio-uniphier.o
 obj-$(CONFIG_ZYNQ_GPIO)                += zynq_gpio.o
 obj-$(CONFIG_VYBRID_GPIO)      += vybrid_gpio.o
 obj-$(CONFIG_HIKEY_GPIO)       += hi6220_gpio.o
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
new file mode 100644 (file)
index 0000000..80bb16e
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm/device.h>
+#include <mapmem.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <asm/errno.h>
+#include <asm/gpio.h>
+
+#define UNIPHIER_GPIO_PORTS_PER_BANK   8
+
+#define UNIPHIER_GPIO_REG_DATA         0       /* data */
+#define UNIPHIER_GPIO_REG_DIR          4       /* direction (1:in, 0:out) */
+
+struct uniphier_gpio_priv {
+       void __iomem *base;
+       char bank_name[16];
+};
+
+static void uniphier_gpio_offset_write(struct udevice *dev, unsigned offset,
+                                      unsigned reg, int value)
+{
+       struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+       u32 tmp;
+
+       tmp = readl(priv->base + reg);
+       if (value)
+               tmp |= BIT(offset);
+       else
+               tmp &= ~BIT(offset);
+       writel(tmp, priv->base + reg);
+}
+
+static int uniphier_gpio_offset_read(struct udevice *dev, unsigned offset,
+                                    unsigned reg)
+{
+       struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+
+       return !!(readl(priv->base + reg) & BIT(offset));
+}
+
+static int uniphier_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+       uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 1);
+
+       return 0;
+}
+
+static int uniphier_gpio_direction_output(struct udevice *dev, unsigned offset,
+                                         int value)
+{
+       uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value);
+       uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 0);
+
+       return 0;
+}
+
+static int uniphier_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+       return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DATA);
+}
+
+static int uniphier_gpio_set_value(struct udevice *dev, unsigned offset,
+                                  int value)
+{
+       uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value);
+
+       return 0;
+}
+
+static int uniphier_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+       return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DIR) ?
+                                               GPIOF_INPUT : GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops uniphier_gpio_ops = {
+       .direction_input        = uniphier_gpio_direction_input,
+       .direction_output       = uniphier_gpio_direction_output,
+       .get_value              = uniphier_gpio_get_value,
+       .set_value              = uniphier_gpio_set_value,
+       .get_function           = uniphier_gpio_get_function,
+};
+
+static int uniphier_gpio_probe(struct udevice *dev)
+{
+       struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+       DECLARE_GLOBAL_DATA_PTR;
+       fdt_addr_t addr;
+       fdt_size_t size;
+       unsigned int tmp;
+
+       addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg",
+                                   &size);
+       if (addr == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       priv->base = map_sysmem(addr, size);
+       if (!priv->base)
+               return -ENOMEM;
+
+       uc_priv->gpio_count = UNIPHIER_GPIO_PORTS_PER_BANK;
+
+       tmp = (addr & 0xfff);
+
+       /* Unfortunately, there is a register hole at offset 0x90-0x9f. */
+       if (tmp > 0x90)
+               tmp -= 0x10;
+
+       snprintf(priv->bank_name, sizeof(priv->bank_name) - 1,
+                "port%d-", (tmp - 8) / 8);
+
+       uc_priv->bank_name = priv->bank_name;
+
+       return 0;
+}
+
+static int uniphier_gpio_remove(struct udevice *dev)
+{
+       struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+
+       unmap_sysmem(priv->base);
+
+       return 0;
+}
+
+/* .data = the number of GPIO banks */
+static const struct udevice_id uniphier_gpio_match[] = {
+       { .compatible = "socionext,uniphier-gpio" },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(uniphier_gpio) = {
+       .name   = "uniphier_gpio",
+       .id     = UCLASS_GPIO,
+       .of_match = uniphier_gpio_match,
+       .probe  = uniphier_gpio_probe,
+       .remove = uniphier_gpio_remove,
+       .priv_auto_alloc_size = sizeof(struct uniphier_gpio_priv),
+       .ops    = &uniphier_gpio_ops,
+};