[U-Boot] [U-BOOT PATCH v2 1/2] gpio: sifive: add support for DM based gpio driver for FU540-SoC

Bin Meng bmeng.cn at gmail.com
Wed Oct 2 13:03:38 UTC 2019


On Wed, Oct 2, 2019 at 1:01 AM Sagar Shrikant Kadam
<sagar.kadam at sifive.com> wrote:
>
> This patch adds a DM based driver model for gpio controller present in
> FU540-C000 SoC on HiFive Unleashed A00 board. This SoC has one GPIO
> bank and 16 GPIO lines in total, out of which GPIO0 to GPIO9 and
> GPIO15 are routed to the J1 header on the board.
>
> This implementation is ported from linux based gpio driver submitted
> for review by Wesley W. Terpstra <wesley at sifive.com> and/or Atish Patra
> <atish.patra at wdc.com> (many thanks !!). The linux driver can be referred
> here [1]
>
> [1]: https://lkml.org/lkml/2018/10/9/1103
>
> Signed-off-by: Sagar Shrikant Kadam <sagar.kadam at sifive.com>
> ---
>  arch/riscv/include/asm/arch-generic/gpio.h |  35 ++++++
>  arch/riscv/include/asm/gpio.h              |   6 +
>  drivers/gpio/Kconfig                       |   7 ++
>  drivers/gpio/Makefile                      |   1 +
>  drivers/gpio/sifive-gpio.c                 | 177 +++++++++++++++++++++++++++++
>  5 files changed, 226 insertions(+)
>  create mode 100644 arch/riscv/include/asm/arch-generic/gpio.h
>  create mode 100644 arch/riscv/include/asm/gpio.h
>  create mode 100644 drivers/gpio/sifive-gpio.c
>
> diff --git a/arch/riscv/include/asm/arch-generic/gpio.h b/arch/riscv/include/asm/arch-generic/gpio.h
> new file mode 100644
> index 0000000..dfcb753
> --- /dev/null
> +++ b/arch/riscv/include/asm/arch-generic/gpio.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2019 SiFive, Inc.
> + */
> +
> +#ifndef _GPIO_SIFIVE_H
> +#define _GPIO_SIFIVE_H
> +
> +#define GPIO_INPUT_VAL 0x00
> +#define GPIO_INPUT_EN  0x04
> +#define GPIO_OUTPUT_EN 0x08
> +#define GPIO_OUTPUT_VAL        0x0C
> +#define GPIO_RISE_IE   0x18
> +#define GPIO_RISE_IP   0x1C
> +#define GPIO_FALL_IE   0x20
> +#define GPIO_FALL_IP   0x24
> +#define GPIO_HIGH_IE   0x28
> +#define GPIO_HIGH_IP   0x2C
> +#define GPIO_LOW_IE    0x30
> +#define GPIO_LOW_IP    0x34
> +#define GPIO_OUTPUT_XOR        0x40
> +
> +#define NR_GPIOS       16
> +
> +enum gpio_state {
> +       LOW,
> +       HIGH
> +};
> +
> +/* Details about a GPIO bank */
> +struct sifive_gpio_platdata {
> +       void *base;     /* address of registers in physical memory */
> +};
> +
> +#endif /* _GPIO_SIFIVE_H */
> diff --git a/arch/riscv/include/asm/gpio.h b/arch/riscv/include/asm/gpio.h
> new file mode 100644
> index 0000000..008d756
> --- /dev/null
> +++ b/arch/riscv/include/asm/gpio.h
> @@ -0,0 +1,6 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2018 SiFive, Inc.
> + */
> +
> +#include <asm-generic/gpio.h>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index f2dabb5..39f2c7e 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -285,6 +285,13 @@ config STM32_GPIO
>           usable on many stm32 families like stm32f4/f7/h7 and stm32mp1.
>           Tested on STM32F7.
>
> +config SIFIVE_GPIO
> +       bool "SiFive GPIO driver"
> +       depends on DM_GPIO
> +       help
> +         Device model driver for GPIO controller present in SiFive FU540 SoC. This
> +         driver enables GPIO interface on HiFive Unleashed A00 board.
> +
>  config MVEBU_GPIO
>         bool "Marvell MVEBU GPIO driver"
>         depends on DM_GPIO && ARCH_MVEBU
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 4a8aa0f..ccc49e2 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -61,3 +61,4 @@ obj-$(CONFIG_$(SPL_)PCF8575_GPIO)     += pcf8575_gpio.o
>  obj-$(CONFIG_PM8916_GPIO)      += pm8916_gpio.o
>  obj-$(CONFIG_MT7621_GPIO)      += mt7621_gpio.o
>  obj-$(CONFIG_MSCC_SGPIO)       += mscc_sgpio.o
> +obj-$(CONFIG_SIFIVE_GPIO)      += sifive-gpio.o
> diff --git a/drivers/gpio/sifive-gpio.c b/drivers/gpio/sifive-gpio.c
> new file mode 100644
> index 0000000..76d5a1d3
> --- /dev/null
> +++ b/drivers/gpio/sifive-gpio.c
> @@ -0,0 +1,177 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * SiFive GPIO driver
> + *
> + * Copyright (C) 2019 SiFive, Inc.
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <asm/arch/gpio.h>
> +#include <asm/io.h>
> +#include <errno.h>
> +#include <asm/gpio.h>
> +
> +static int sifive_gpio_probe(struct udevice *dev)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +       char name[18], *str;
> +
> +       sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base);
> +       str = strdup(name);
> +       if (!str)
> +               return -ENOMEM;
> +       uc_priv->bank_name = str;
> +
> +       /*
> +        * Use the gpio count mentioned in device tree,
> +        * if not specified in dt, set NR_GPIOS as default
> +        */
> +       uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", NR_GPIOS);
> +
> +       return 0;
> +}
> +
> +static void sifive_update_gpio_reg(void *bptr, u32 offset, bool value)
> +{
> +       void __iomem *ptr = (void __iomem *)bptr;
> +
> +       u32 bit = BIT(offset);
> +       u32 old = readl(ptr);
> +
> +       if (value)
> +               writel(old | bit, ptr);
> +       else
> +               writel(old & ~bit, ptr);
> +}
> +
> +static int sifive_gpio_direction_input(struct udevice *dev, u32 offset)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +
> +       if (offset > uc_priv->gpio_count)
> +               return -EINVAL;
> +
> +       /* Configure gpio direction as input */
> +       sifive_update_gpio_reg(plat->base + GPIO_INPUT_EN,  offset, true);
> +       sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_EN, offset, false);
> +
> +       return 0;
> +}
> +
> +static int sifive_gpio_direction_output(struct udevice *dev, u32 offset,
> +                                       int value)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +
> +       if (offset > uc_priv->gpio_count)
> +               return -EINVAL;
> +
> +       /* Configure gpio direction as output */
> +       sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_EN, offset, true);
> +       sifive_update_gpio_reg(plat->base + GPIO_INPUT_EN,  offset, false);
> +
> +       /* Set the output state of the pin */
> +       sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_VAL, offset, value);
> +
> +       return 0;
> +}
> +
> +static int sifive_gpio_get_value(struct udevice *dev, u32 offset)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +       int val;
> +       int dir;
> +
> +       if (offset > uc_priv->gpio_count)
> +               return -EINVAL;
> +
> +       /* Get direction of the pin */
> +       dir = !(readl(plat->base + GPIO_OUTPUT_EN) & BIT(offset));
> +
> +       if (dir)
> +               val = readl(plat->base + GPIO_INPUT_VAL) & BIT(offset);
> +       else
> +               val = readl(plat->base + GPIO_OUTPUT_VAL) & BIT(offset);
> +
> +       return val ? HIGH : LOW;
> +}
> +
> +static int sifive_gpio_set_value(struct udevice *dev, u32 offset, int value)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +
> +       if (offset > uc_priv->gpio_count)
> +               return -EINVAL;
> +
> +       sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_VAL, offset, value);
> +
> +       return 0;
> +}
> +
> +static int sifive_gpio_get_function(struct udevice *dev, unsigned int offset)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       u32     outdir, indir, val;
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +
> +       if (offset > uc_priv->gpio_count)
> +               return -1;
> +
> +       /* Get direction of the pin */
> +       outdir = readl(plat->base + GPIO_OUTPUT_EN) & BIT(offset);
> +       indir  = readl(plat->base + GPIO_INPUT_EN) & BIT(offset);
> +
> +       if (outdir)
> +               /* Pin at specified offset is configured as output */
> +               val = GPIOF_OUTPUT;
> +       else if (indir)
> +               /* Pin at specified offset is configured as input */
> +               val = GPIOF_INPUT;
> +       else
> +               /*The requested GPIO is not set as input or output */

nits: need a space after /*

> +               val = GPIOF_UNUSED;
> +
> +       return val;
> +}
> +
> +static const struct udevice_id sifive_gpio_match[] = {
> +       { .compatible = "sifive,gpio0" },
> +       { }
> +};
> +
> +static const struct dm_gpio_ops sifive_gpio_ops = {
> +       .direction_input        = sifive_gpio_direction_input,
> +       .direction_output       = sifive_gpio_direction_output,
> +       .get_value              = sifive_gpio_get_value,
> +       .set_value              = sifive_gpio_set_value,
> +       .get_function           = sifive_gpio_get_function,
> +};
> +
> +static int sifive_gpio_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
> +       fdt_addr_t addr;
> +
> +       addr = devfdt_get_addr(dev);
> +       if (addr == FDT_ADDR_T_NONE)
> +               return -EINVAL;
> +
> +       plat->base = (void *)addr;
> +       return 0;
> +}
> +
> +U_BOOT_DRIVER(gpio_sifive) = {
> +       .name   = "gpio_sifive",
> +       .id     = UCLASS_GPIO,
> +       .of_match = sifive_gpio_match,
> +       .ofdata_to_platdata = of_match_ptr(sifive_gpio_ofdata_to_platdata),
> +       .platdata_auto_alloc_size = sizeof(struct sifive_gpio_platdata),
> +       .ops    = &sifive_gpio_ops,
> +       .probe  = sifive_gpio_probe,
> +};
> --

Other than above,

Reviewed-by: Bin Meng <bmeng.cn at gmail.com>


More information about the U-Boot mailing list