[RFC PATCH v1 11/20] drivers: gpio: add support of RP1 GPIO for Raspberry PI 5
Oleksii Moisieiev
Oleksii_Moisieiev at epam.com
Wed Feb 5 11:15:45 CET 2025
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 <oleksii_moisieiev at epam.com>
Reviewed-by: Volodymyr Babchuk <volodymyr_babchuk at epam.com>
---
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
More information about the U-Boot
mailing list