Raspberry PI5 has an external chip RP1 installed which provides it's own GPIO controller. This driver implements GPIO support for RP1 GPIO controller.
Signed-off-by: Oleksii Moisieiev <[email protected]> Reviewed-by: Volodymyr Babchuk <[email protected]> --- drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/rp1_gpio.c | 374 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+) create mode 100644 drivers/gpio/rp1_gpio.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b050585389..74d5a4dbbc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -340,6 +340,13 @@ config RCAR_GPIO help This driver supports the GPIO banks on Renesas RCar SoCs. +config RP1_GPIO + bool "Raspberry PR1 GPIO driver" + help + This driver supports the GPIO banks on Raspberry RP1 chip. + Raspberry PI5 has an external chip RP1 installed, which + provides it's own GPIO controller. + config RZA1_GPIO bool "Renesas RZ/A1 GPIO driver" depends on DM_GPIO && RZA1 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4a29315435..29d0bdf343 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_NPCM_GPIO) += npcm_gpio.o obj-$(CONFIG_PCA953X) += pca953x.o obj-$(CONFIG_ROCKCHIP_GPIO) += rk_gpio.o obj-$(CONFIG_RCAR_GPIO) += gpio-rcar.o +obj-$(CONFIG_RP1_GPIO) += rp1_gpio.o obj-$(CONFIG_RZA1_GPIO) += gpio-rza1.o obj-$(CONFIG_S5P) += s5p_gpio.o obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o sandbox_test.o diff --git a/drivers/gpio/rp1_gpio.c b/drivers/gpio/rp1_gpio.c new file mode 100644 index 0000000000..a64af18fcf --- /dev/null +++ b/drivers/gpio/rp1_gpio.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 EPAM Systems + * + * Derived from linux/drivers/pinctrl/pinctl-rp1.c + * Copyright (C) 2023 Raspberry Pi Ltd. + */ + +#include <dm.h> +#include <errno.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <linux/bitops.h> + +#define RP1_NUM_GPIOS 54 +#define RP1_NUM_BANKS 3 + +#define RP1_RW_OFFSET 0x0000 +#define RP1_XOR_OFFSET 0x1000 +#define RP1_SET_OFFSET 0x2000 +#define RP1_CLR_OFFSET 0x3000 + +#define RP1_GPIO_STATUS 0x0000 +#define RP1_GPIO_CTRL 0x0004 + +#define RP1_GPIO_PCIE_INTE 0x011c +#define RP1_GPIO_PCIE_INTS 0x0124 + +#define RP1_GPIO_EVENTS_SHIFT_RAW 20 +#define RP1_GPIO_STATUS_FALLING BIT(20) +#define RP1_GPIO_STATUS_RISING BIT(21) +#define RP1_GPIO_STATUS_LOW BIT(22) +#define RP1_GPIO_STATUS_HIGH BIT(23) + +#define RP1_GPIO_EVENTS_SHIFT_FILTERED 24 +#define RP1_GPIO_STATUS_F_FALLING BIT(24) +#define RP1_GPIO_STATUS_F_RISING BIT(25) +#define RP1_GPIO_STATUS_F_LOW BIT(26) +#define RP1_GPIO_STATUS_F_HIGH BIT(27) + +#define RP1_GPIO_CTRL_FUNCSEL_LSB 0 +#define RP1_GPIO_CTRL_FUNCSEL_MASK 0x0000001f +#define RP1_GPIO_CTRL_OUTOVER_LSB 12 +#define RP1_GPIO_CTRL_OUTOVER_MASK 0x00003000 +#define RP1_GPIO_CTRL_OEOVER_LSB 14 +#define RP1_GPIO_CTRL_OEOVER_MASK 0x0000c000 +#define RP1_GPIO_CTRL_INOVER_LSB 16 +#define RP1_GPIO_CTRL_INOVER_MASK 0x00030000 +#define RP1_GPIO_CTRL_IRQEN_FALLING BIT(20) +#define RP1_GPIO_CTRL_IRQEN_RISING BIT(21) +#define RP1_GPIO_CTRL_IRQEN_LOW BIT(22) +#define RP1_GPIO_CTRL_IRQEN_HIGH BIT(23) +#define RP1_GPIO_CTRL_IRQEN_F_FALLING BIT(24) +#define RP1_GPIO_CTRL_IRQEN_F_RISING BIT(25) +#define RP1_GPIO_CTRL_IRQEN_F_LOW BIT(26) +#define RP1_GPIO_CTRL_IRQEN_F_HIGH BIT(27) +#define RP1_GPIO_CTRL_IRQRESET BIT(28) +#define RP1_GPIO_CTRL_IRQOVER_LSB 30 +#define RP1_GPIO_CTRL_IRQOVER_MASK 0xc0000000 + +#define RP1_PUD_OFF 0 +#define RP1_PUD_DOWN 1 +#define RP1_PUD_UP 2 + +#define RP1_FSEL_COUNT 9 + +#define RP1_FSEL_ALT0 0x00 +#define RP1_FSEL_GPIO 0x05 +#define RP1_FSEL_NONE 0x09 +#define RP1_FSEL_NONE_HW 0x1f + +#define RP1_DIR_OUTPUT 0 +#define RP1_DIR_INPUT 1 + +#define RP1_OUTOVER_PERI 0 +#define RP1_OUTOVER_INVPERI 1 +#define RP1_OUTOVER_LOW 2 +#define RP1_OUTOVER_HIGH 3 + +#define RP1_OEOVER_PERI 0 +#define RP1_OEOVER_INVPERI 1 +#define RP1_OEOVER_DISABLE 2 +#define RP1_OEOVER_ENABLE 3 + +#define RP1_INOVER_PERI 0 +#define RP1_INOVER_INVPERI 1 +#define RP1_INOVER_LOW 2 +#define RP1_INOVER_HIGH 3 + +#define RP1_RIO_OUT 0x00 +#define RP1_RIO_OE 0x04 +#define RP1_RIO_IN 0x08 + +#define RP1_PAD_SLEWFAST_MASK 0x00000001 +#define RP1_PAD_SLEWFAST_LSB 0 +#define RP1_PAD_SCHMITT_MASK 0x00000002 +#define RP1_PAD_SCHMITT_LSB 1 +#define RP1_PAD_PULL_MASK 0x0000000c +#define RP1_PAD_PULL_LSB 2 +#define RP1_PAD_DRIVE_MASK 0x00000030 +#define RP1_PAD_DRIVE_LSB 4 +#define RP1_PAD_IN_ENABLE_MASK 0x00000040 +#define RP1_PAD_IN_ENABLE_LSB 6 +#define RP1_PAD_OUT_DISABLE_MASK 0x00000080 +#define RP1_PAD_OUT_DISABLE_LSB 7 + +#define RP1_PAD_DRIVE_2MA 0x00000000 +#define RP1_PAD_DRIVE_4MA 0x00000010 +#define RP1_PAD_DRIVE_8MA 0x00000020 +#define RP1_PAD_DRIVE_12MA 0x00000030 + +#define FLD_GET(r, f) (((r) & (f ## _MASK)) >> (f ## _LSB)) +#define FLD_SET(r, f, v) r = (((r) & ~(f ## _MASK)) | ((v) << (f ## _LSB))) + +struct rp1_iobank_desc { + int min_gpio; + int num_gpios; + int gpio_offset; + int inte_offset; + int ints_offset; + int rio_offset; + int pads_offset; +}; + +const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = { + /* gpio inte ints rio pads */ + { 0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 }, + { 28, 6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 }, + { 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 }, +}; + +struct rp1_pin_info { + u8 num; + u8 bank; + u8 offset; + u8 fsel; + + void __iomem *gpio; + void __iomem *rio; + void __iomem *inte; + void __iomem *ints; + void __iomem *pad; +}; + +struct rp1_gpio_priv { + void __iomem *gpio_base; + void __iomem *rio_base; + void __iomem *pads_base; + + struct rp1_pin_info pins[RP1_NUM_GPIOS]; +}; + +static struct rp1_pin_info *rp1_get_pin(struct udevice *dev, + unsigned int offset) +{ + struct rp1_gpio_priv *priv = dev_get_priv(dev); + + if (priv && offset < RP1_NUM_GPIOS) + return &priv->pins[offset]; + + return NULL; +} + +static void rp1_pad_update(struct rp1_pin_info *pin, u32 clr, u32 set) +{ + u32 padctrl = readl(pin->pad); + + padctrl &= ~clr; + padctrl |= set; + + writel(padctrl, pin->pad); +} + +static void rp1_input_enable(struct rp1_pin_info *pin, int value) +{ + rp1_pad_update(pin, RP1_PAD_IN_ENABLE_MASK, + value ? RP1_PAD_IN_ENABLE_MASK : 0); +} + +static void rp1_output_enable(struct rp1_pin_info *pin, int value) +{ + rp1_pad_update(pin, RP1_PAD_OUT_DISABLE_MASK, + value ? 0 : RP1_PAD_OUT_DISABLE_MASK); +} + +static u32 rp1_get_fsel(struct rp1_pin_info *pin) +{ + u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL); + u32 oeover = FLD_GET(ctrl, RP1_GPIO_CTRL_OEOVER); + u32 fsel = FLD_GET(ctrl, RP1_GPIO_CTRL_FUNCSEL); + + if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT) + fsel = RP1_FSEL_NONE; + + return fsel; +} + +static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel) +{ + u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL); + + if (fsel >= RP1_FSEL_COUNT) + fsel = RP1_FSEL_NONE_HW; + + rp1_input_enable(pin, 1); + rp1_output_enable(pin, 1); + + if (fsel == RP1_FSEL_NONE) { + FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_DISABLE); + } else { + FLD_SET(ctrl, RP1_GPIO_CTRL_OUTOVER, RP1_OUTOVER_PERI); + FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_PERI); + } + FLD_SET(ctrl, RP1_GPIO_CTRL_FUNCSEL, fsel); + + writel(ctrl, pin->gpio + RP1_GPIO_CTRL); +} + +static int rp1_get_dir(struct rp1_pin_info *pin) +{ + return !(readl(pin->rio + RP1_RIO_OE) & (1 << pin->offset)) ? + RP1_DIR_INPUT : RP1_DIR_OUTPUT; +} + +static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input) +{ + int offset = is_input ? RP1_CLR_OFFSET : RP1_SET_OFFSET; + + writel(1 << pin->offset, pin->rio + RP1_RIO_OE + offset); +} + +static int rp1_get_value(struct rp1_pin_info *pin) +{ + return !!(readl(pin->rio + RP1_RIO_IN) & (1 << pin->offset)); +} + +static void rp1_set_value(struct rp1_pin_info *pin, int value) +{ + /* Assume the pin is already an output */ + writel(1 << pin->offset, + pin->rio + RP1_RIO_OUT + (value ? RP1_SET_OFFSET : RP1_CLR_OFFSET)); +} + +static int rp1_gpio_get(struct udevice *dev, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(dev, offset); + int ret; + + if (!pin) + return -EINVAL; + + ret = rp1_get_value(pin); + return ret; +} + +static int rp1_gpio_set(struct udevice *dev, unsigned int offset, int value) +{ + struct rp1_pin_info *pin = rp1_get_pin(dev, offset); + + if (pin) + rp1_set_value(pin, value); + + return 0; +} + +static int rp1_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(dev, offset); + + if (!pin) + return -EINVAL; + + rp1_set_dir(pin, RP1_DIR_INPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + return 0; +} + +static int rp1_gpio_direction_output(struct udevice *dev, unsigned int offset, int value) +{ + struct rp1_pin_info *pin = rp1_get_pin(dev, offset); + + if (!pin) + return -EINVAL; + + rp1_set_value(pin, value); + rp1_set_dir(pin, RP1_DIR_OUTPUT); + rp1_set_fsel(pin, RP1_FSEL_GPIO); + return 0; +} + +static int rp1_gpio_get_direction(struct udevice *dev, unsigned int offset) +{ + struct rp1_pin_info *pin = rp1_get_pin(dev, offset); + u32 fsel; + + if (!pin) + return -EINVAL; + + fsel = rp1_get_fsel(pin); + if (fsel != RP1_FSEL_GPIO) + return -EINVAL; + + return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ? + GPIOF_OUTPUT : + GPIOF_INPUT; +} + +static const struct dm_gpio_ops rp1_gpio_ops = { + .direction_input = rp1_gpio_direction_input, + .direction_output = rp1_gpio_direction_output, + .get_value = rp1_gpio_get, + .set_value = rp1_gpio_set, + .get_function = rp1_gpio_get_direction, +}; + +static int rp1_gpio_probe(struct udevice *dev) +{ + int i; + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct rp1_gpio_priv *priv = dev_get_priv(dev); + + priv->gpio_base = dev_remap_addr_index(dev, 0); + if (!priv->gpio_base) + return -EINVAL; + + priv->rio_base = dev_remap_addr_index(dev, 1); + if (!priv->rio_base) + return -EINVAL; + + priv->pads_base = dev_remap_addr_index(dev, 2); + if (!priv->pads_base) + return -EINVAL; + + uc_priv->gpio_count = RP1_NUM_GPIOS; + uc_priv->bank_name = dev->name; + + for (i = 0; i < RP1_NUM_BANKS; i++) { + const struct rp1_iobank_desc *bank = &rp1_iobanks[i]; + int j; + + for (j = 0; j < bank->num_gpios; j++) { + struct rp1_pin_info *pin = + &priv->pins[bank->min_gpio + j]; + + pin->num = bank->min_gpio + j; + pin->bank = i; + pin->offset = j; + + pin->gpio = priv->gpio_base + bank->gpio_offset + + j * sizeof(u32) * 2; + pin->inte = priv->gpio_base + bank->inte_offset; + pin->ints = priv->gpio_base + bank->ints_offset; + pin->rio = priv->rio_base + bank->rio_offset; + pin->pad = priv->pads_base + bank->pads_offset + + j * sizeof(u32); + } + } + + return 0; +} + +static const struct udevice_id rp1_gpio_ids[] = { + { .compatible = "raspberrypi,rp1-gpio" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(rp1_gpio) = { + .name = "rp1-gpio", + .id = UCLASS_GPIO, + .of_match = rp1_gpio_ids, + .ops = &rp1_gpio_ops, + .priv_auto = sizeof(struct rp1_gpio_priv), + .probe = rp1_gpio_probe, +}; -- 2.34.1

