[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