diff options
-rw-r--r-- | arch/riscv/include/asm/arch-jh7110/gpio.h | 53 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/starfive-gpio.c | 164 |
4 files changed, 224 insertions, 0 deletions
diff --git a/arch/riscv/include/asm/arch-jh7110/gpio.h b/arch/riscv/include/asm/arch-jh7110/gpio.h new file mode 100644 index 0000000000..80675d64de --- /dev/null +++ b/arch/riscv/include/asm/arch-jh7110/gpio.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Starfive, Inc. + * Author: yanhong <yanhong.wang@starfivetech.com> + * + */ + +#ifndef _GPIO_STARFIVE_H_ +#define _GPIO_STARFIVE_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GPIO_NUM_SHIFT 2 /*one dword include 4 gpios*/ +#define GPIO_BYTE_SHIFT 3 + +#define GPIO_INDEX_MASK 0x3 + +#define GPIO_DOEN_MASK 0x3f +#define GPIO_DOUT_MASK 0x7f +#define GPIO_DIN_MASK 0x7f + +#define NR_GPIOS 64 + +#define GPIO_OFFSET(gpio) \ + (((gpio) >> GPIO_NUM_SHIFT) << GPIO_NUM_SHIFT) + +#define GPIO_SHIFT(gpio) \ + (((gpio) & GPIO_INDEX_MASK) << GPIO_BYTE_SHIFT) + +enum gpio_state { + LOW, + HIGH +}; + +#define GPIO_DOEN 0x0 +#define GPIO_DOUT 0x40 +#define GPIO_DIN 0x80 +#define GPIO_EN 0xdc +#define GPIO_LOW_IE 0x100 +#define GPIO_HIGH_IE 0x104 + +/* Details about a GPIO bank */ +struct starfive_gpio_platdata { + void *base; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _GPIO_STARFIVE_H_ */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e37ac9f494..29e88f910c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -382,6 +382,12 @@ config SIFIVE_GPIO Device model driver for GPIO controller present in SiFive FU540 SoC. This driver enables GPIO interface on HiFive Unleashed A00 board. +config STARFIVE_GPIO + bool "StarFive GPIO driver" + depends on DM_GPIO + help + Device model driver for GPIO controller present in StarFive JH7110 SoC. + config MVEBU_GPIO bool "Marvell MVEBU GPIO driver" depends on DM_GPIO && (ARCH_MVEBU || ARCH_KIRKWOOD) diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 58f4704f6b..2fb64c90c7 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,3 +69,4 @@ obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_NX_GPIO) += nx_gpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o +obj-$(CONFIG_STARFIVE_GPIO) += starfive-gpio.o
\ No newline at end of file diff --git a/drivers/gpio/starfive-gpio.c b/drivers/gpio/starfive-gpio.c new file mode 100644 index 0000000000..9c715c1f9c --- /dev/null +++ b/drivers/gpio/starfive-gpio.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Starfive, Inc. + * Author: yanhong <yanhong.wang@starfivetech.com> + * + */ + +#include <common.h> +#include <dm.h> +#include <asm/arch/gpio.h> +#include <asm/io.h> +#include <errno.h> +#include <asm/gpio.h> +#include <linux/bitops.h> + +static int starfive_gpio_probe(struct udevice *dev) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(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 int starfive_gpio_direction_input(struct udevice *dev, u32 offset) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(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 */ + clrsetbits_le32(plat->base + GPIO_OFFSET(offset), + GPIO_DOEN_MASK << GPIO_SHIFT(offset), + HIGH << GPIO_SHIFT(offset)); + + return 0; +} + +static int starfive_gpio_direction_output(struct udevice *dev, u32 offset, + int value) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(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 */ + clrsetbits_le32(plat->base + GPIO_OFFSET(offset), + GPIO_DOEN_MASK << GPIO_SHIFT(offset), + LOW << GPIO_SHIFT(offset)); + + /* Set the output value of the pin */ + clrsetbits_le32(plat->base + GPIO_DOUT + GPIO_OFFSET(offset), + GPIO_DOUT_MASK << GPIO_SHIFT(offset), + (value & GPIO_DOUT_MASK) << GPIO_SHIFT(offset)); + + return 0; +} + +static int starfive_gpio_get_value(struct udevice *dev, u32 offset) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + int val; + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + /*Get dout value*/ + val = readl(plat->base + GPIO_DIN + GPIO_OFFSET(offset)); + val &= GPIO_DIN_MASK << GPIO_SHIFT(offset); + + return val ? HIGH : LOW; +} + +static int starfive_gpio_set_value(struct udevice *dev, u32 offset, int value) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (offset > uc_priv->gpio_count) + return -EINVAL; + + clrsetbits_le32(plat->base + GPIO_DOUT + GPIO_OFFSET(offset), + GPIO_DOUT_MASK << GPIO_SHIFT(offset), + (value & GPIO_DOUT_MASK) << GPIO_SHIFT(offset)); + + return 0; +} + +static int starfive_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(dev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + u32 dir, val; + + if (offset > uc_priv->gpio_count) + return -1; + + /*Get doen value*/ + val = readl(plat->base + GPIO_OFFSET(offset)); + val &= (GPIO_DOEN_MASK << GPIO_SHIFT(offset)); + + dir = (val > 1) ? GPIOF_UNUSED : (val ? GPIOF_INPUT : GPIOF_OUTPUT); + + return dir; +} + +static int starfive_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct starfive_gpio_platdata *plat = dev_get_plat(dev); + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->base = (void *)addr; + + writel(0, plat->base + GPIO_EN); + writel(0, plat->base + GPIO_LOW_IE); + writel(0, plat->base + GPIO_HIGH_IE); + writel(1, plat->base + GPIO_EN); + + return 0; +} + +static const struct udevice_id starfive_gpio_match[] = { + { .compatible = "starfive,jh7110-gpio" }, + { } +}; + +static const struct dm_gpio_ops starfive_gpio_ops = { + .direction_input = starfive_gpio_direction_input, + .direction_output = starfive_gpio_direction_output, + .get_value = starfive_gpio_get_value, + .set_value = starfive_gpio_set_value, + .get_function = starfive_gpio_get_function, +}; + +U_BOOT_DRIVER(gpio_starfive) = { + .name = "gpio_starfive", + .id = UCLASS_GPIO, + .of_match = starfive_gpio_match, + .of_to_plat = starfive_gpio_ofdata_to_platdata, + .plat_auto = sizeof(struct starfive_gpio_platdata), + .ops = &starfive_gpio_ops, + .probe = starfive_gpio_probe, +}; |