Message ID | 1437135346-4612-2-git-send-email-yamada.masahiro@socionext.com |
---|---|
State | Deferred |
Delegated to: | Simon Glass |
Headers | show |
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
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 --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, +};
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