diff mbox

[U-Boot,v2,1/4] gpio: UniPhier: add driver for UniPhier GPIO controller

Message ID 1437135346-4612-2-git-send-email-yamada.masahiro@socionext.com
State Deferred
Delegated to: Simon Glass
Headers show

Commit Message

Masahiro Yamada July 17, 2015, 12:15 p.m. UTC
This GPIO controller device is used on UniPhier SoCs.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Do not use "ngpio" property to specify the number of GPIO pins.
    Instead, use .data field of OF match table.

 drivers/gpio/Kconfig         |   6 ++
 drivers/gpio/Makefile        |   1 +
 drivers/gpio/gpio-uniphier.c | 192 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 199 insertions(+)
 create mode 100644 drivers/gpio/gpio-uniphier.c

Comments

Simon Glass Aug. 26, 2015, 3:53 a.m. UTC | #1
Hi Masahiro,

On 17 July 2015 at 05:15, Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> This GPIO controller device is used on UniPhier SoCs.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
>
> Changes in v2:
>   - Do not use "ngpio" property to specify the number of GPIO pins.
>     Instead, use .data field of OF match table.
>
>  drivers/gpio/Kconfig         |   6 ++
>  drivers/gpio/Makefile        |   1 +
>  drivers/gpio/gpio-uniphier.c | 192 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 199 insertions(+)
>  create mode 100644 drivers/gpio/gpio-uniphier.c

Since this seems to be in my queue. Please see below for a question.

Acked-by: Simon Glass <sjg@chromium.org>

>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 0c43777..1176e3f 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -36,6 +36,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
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 5864850..5ec4ad7 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -44,5 +44,6 @@ 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
> diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
> new file mode 100644
> index 0000000..e65e9f3
> --- /dev/null
> +++ b/drivers/gpio/gpio-uniphier.c
> @@ -0,0 +1,192 @@
> +/*
> + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <mapmem.h>
> +#include <linux/io.h>
> +#include <asm/errno.h>
> +#include <asm/gpio.h>
> +#include <dm/device.h>
> +
> +/*
> + * Unfortunately, the hardware specification adopts weird GPIO pin labeling.
> + * The ports are named as
> + *   PORT00,  PORT01,  PORT02,  ..., PORT07,
> + *   PORT10,  PORT11,  PORT12,  ..., PORT17,
> + *   PORT20,  PORT21,  PORT22,  ..., PORT27,
> + *    ...
> + *   PORT90,  PORT91,  PORT92,  ..., PORT97,
> + *   PORT100, PORT101, PORT102, ..., PORT107,
> + *    ...
> + *
> + * The PORTs with 8 or 9 in the one's place are missing, i.e. the one's place
> + * is octal, while the other places are decimal.  If we handle the port numbers
> + * as seen in the hardware documents, the GPIO offsets must be non-contiguous.
> + * It is possible to have sparse GPIO pins, but not handy for GPIO range
> + * mappings, register accessing, etc.
> + *
> + * To make things simpler (for driver and device tree implementation), this
> + * driver takes contiguously-numbered GPIO offsets.  GPIO consumers should make
> + * sure to convert the PORT number into the one that fits in this driver.
> + * The conversion logic is very easy math, for example,
> + *   PORT15  -->  GPIO offset 13   (8 * 1 + 5)
> + *   PORT123 -->  GPIO offset 99   (8 * 12 + 3)
> + */
> +#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) */
> +
> +/* delete the following when BIT(nr) is added to include/linux/bitops.h */
> +#define BIT(nr)                        (1UL << (nr))
> +
> +struct uniphier_gpio_priv {
> +       void __iomem *base;
> +};
> +
> +static unsigned uniphier_gpio_bank_to_reg(unsigned bank, unsigned reg_type)
> +{
> +       unsigned reg;
> +
> +       reg = (bank + 1) * 8 + reg_type;
> +
> +       /*
> +        * Unfortunately, there is a register hole at offset 0x90-0x9f.
> +        * Add 0x10 when crossing the hole.
> +        */
> +       if (reg >= 0x90)
> +               reg += 0x10;
> +
> +       return reg;
> +}
> +
> +static void uniphier_gpio_offset_write(struct udevice *dev, unsigned offset,
> +                                      unsigned reg_type, int value)
> +{
> +       struct uniphier_gpio_priv *priv = dev_get_priv(dev);
> +       unsigned bank = offset / UNIPHIER_GPIO_PORTS_PER_BANK;
> +       unsigned bit = offset % UNIPHIER_GPIO_PORTS_PER_BANK;
> +       unsigned reg;
> +       u32 tmp;
> +
> +       reg = uniphier_gpio_bank_to_reg(bank, reg_type);
> +
> +       tmp = readl(priv->base + reg);
> +       if (value)
> +               tmp |= BIT(bit);
> +       else
> +               tmp &= ~BIT(bit);
> +       writel(tmp, priv->base + reg);
> +}
> +
> +static int uniphier_gpio_offset_read(struct udevice *dev, unsigned offset,
> +                                    unsigned reg_type)
> +{
> +       struct uniphier_gpio_priv *priv = dev_get_priv(dev);
> +       unsigned bank = offset / UNIPHIER_GPIO_PORTS_PER_BANK;
> +       unsigned bit = offset % UNIPHIER_GPIO_PORTS_PER_BANK;
> +       unsigned reg;
> +
> +       reg = uniphier_gpio_bank_to_reg(bank, reg_type);
> +
> +       return readl(priv->base + reg) & BIT(bit) ? 1 : 0;
> +}
> +
> +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;
> +
> +       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 * dev->driver_data;

But I just want to check - here you are not setting a bank name. In
fact you are only defining a single bank of GPIOs and the only way to
access them will be by number (both in device tree and on command
line).

I see your Linux patches do a similar thing so I guess this is right.

> +
> +       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,ph1-sld3-gpio", .data = 17 },
> +       { .compatible = "socionext,ph1-ld4-gpio", .data = 17 },
> +       { .compatible = "socionext,ph1-pro4-gpio", .data = 31 },
> +       { .compatible = "socionext,ph1-sld8-gpio", .data = 17 },
> +       { .compatible = "socionext,ph1-pro5-gpio", .data = 31 },
> +       { .compatible = "socionext,proxstream2-gpio", .data = 29 },
> +       { .compatible = "socionext,ph1-ld6b-gpio", .data = 29 },
> +       { /* 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,
> +};
> --
> 1.9.1
>

Regards,
Simon
Masahiro Yamada Aug. 26, 2015, 4:14 a.m. UTC | #2
2015-08-26 12:53 GMT+09:00 Simon Glass <sjg@chromium.org>:
> Hi Masahiro,
>
> On 17 July 2015 at 05:15, Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
>> This GPIO controller device is used on UniPhier SoCs.
>>
>> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
>> ---
>>
>> Changes in v2:
>>   - Do not use "ngpio" property to specify the number of GPIO pins.
>>     Instead, use .data field of OF match table.
>>
>>  drivers/gpio/Kconfig         |   6 ++
>>  drivers/gpio/Makefile        |   1 +
>>  drivers/gpio/gpio-uniphier.c | 192 +++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 199 insertions(+)
>>  create mode 100644 drivers/gpio/gpio-uniphier.c
>
> Since this seems to be in my queue. Please see below for a question.
>
> Acked-by: Simon Glass <sjg@chromium.org>

[snip]


> But I just want to check - here you are not setting a bank name. In
> fact you are only defining a single bank of GPIOs and the only way to
> access them will be by number (both in device tree and on command
> line).


Yes, numbering access is available.



This GPIO driver has not been applied in Linux either.
Nor I have not decided the design policy yet.

I marked this series as Deferred
to not have you under obligation.


I will come back here after finishing Linux GPIO stuff.
diff mbox

Patch

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0c43777..1176e3f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -36,6 +36,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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 5864850..5ec4ad7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -44,5 +44,6 @@  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
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
new file mode 100644
index 0000000..e65e9f3
--- /dev/null
+++ b/drivers/gpio/gpio-uniphier.c
@@ -0,0 +1,192 @@ 
+/*
+ * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <mapmem.h>
+#include <linux/io.h>
+#include <asm/errno.h>
+#include <asm/gpio.h>
+#include <dm/device.h>
+
+/*
+ * Unfortunately, the hardware specification adopts weird GPIO pin labeling.
+ * The ports are named as
+ *   PORT00,  PORT01,  PORT02,  ..., PORT07,
+ *   PORT10,  PORT11,  PORT12,  ..., PORT17,
+ *   PORT20,  PORT21,  PORT22,  ..., PORT27,
+ *    ...
+ *   PORT90,  PORT91,  PORT92,  ..., PORT97,
+ *   PORT100, PORT101, PORT102, ..., PORT107,
+ *    ...
+ *
+ * The PORTs with 8 or 9 in the one's place are missing, i.e. the one's place
+ * is octal, while the other places are decimal.  If we handle the port numbers
+ * as seen in the hardware documents, the GPIO offsets must be non-contiguous.
+ * It is possible to have sparse GPIO pins, but not handy for GPIO range
+ * mappings, register accessing, etc.
+ *
+ * To make things simpler (for driver and device tree implementation), this
+ * driver takes contiguously-numbered GPIO offsets.  GPIO consumers should make
+ * sure to convert the PORT number into the one that fits in this driver.
+ * The conversion logic is very easy math, for example,
+ *   PORT15  -->  GPIO offset 13   (8 * 1 + 5)
+ *   PORT123 -->  GPIO offset 99   (8 * 12 + 3)
+ */
+#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) */
+
+/* delete the following when BIT(nr) is added to include/linux/bitops.h */
+#define BIT(nr)			(1UL << (nr))
+
+struct uniphier_gpio_priv {
+	void __iomem *base;
+};
+
+static unsigned uniphier_gpio_bank_to_reg(unsigned bank, unsigned reg_type)
+{
+	unsigned reg;
+
+	reg = (bank + 1) * 8 + reg_type;
+
+	/*
+	 * Unfortunately, there is a register hole at offset 0x90-0x9f.
+	 * Add 0x10 when crossing the hole.
+	 */
+	if (reg >= 0x90)
+		reg += 0x10;
+
+	return reg;
+}
+
+static void uniphier_gpio_offset_write(struct udevice *dev, unsigned offset,
+				       unsigned reg_type, int value)
+{
+	struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+	unsigned bank = offset / UNIPHIER_GPIO_PORTS_PER_BANK;
+	unsigned bit = offset % UNIPHIER_GPIO_PORTS_PER_BANK;
+	unsigned reg;
+	u32 tmp;
+
+	reg = uniphier_gpio_bank_to_reg(bank, reg_type);
+
+	tmp = readl(priv->base + reg);
+	if (value)
+		tmp |= BIT(bit);
+	else
+		tmp &= ~BIT(bit);
+	writel(tmp, priv->base + reg);
+}
+
+static int uniphier_gpio_offset_read(struct udevice *dev, unsigned offset,
+				     unsigned reg_type)
+{
+	struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+	unsigned bank = offset / UNIPHIER_GPIO_PORTS_PER_BANK;
+	unsigned bit = offset % UNIPHIER_GPIO_PORTS_PER_BANK;
+	unsigned reg;
+
+	reg = uniphier_gpio_bank_to_reg(bank, reg_type);
+
+	return readl(priv->base + reg) & BIT(bit) ? 1 : 0;
+}
+
+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;
+
+	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 * dev->driver_data;
+
+	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,ph1-sld3-gpio", .data = 17 },
+	{ .compatible = "socionext,ph1-ld4-gpio", .data = 17 },
+	{ .compatible = "socionext,ph1-pro4-gpio", .data = 31 },
+	{ .compatible = "socionext,ph1-sld8-gpio", .data = 17 },
+	{ .compatible = "socionext,ph1-pro5-gpio", .data = 31 },
+	{ .compatible = "socionext,proxstream2-gpio", .data = 29 },
+	{ .compatible = "socionext,ph1-ld6b-gpio", .data = 29 },
+	{ /* 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,
+};