[PATCH v2 1/2] gpio: add PolarFire SoC GPIO and Core GPIO driver

Eoin Dickson Eoin.Dickson at microchip.com
Thu May 29 12:21:11 CEST 2025


From: Eoin Dickson <eoin.dickson at microchip.com>

This driver adds GPIO support for PolarFire SoC family, this is required
to add sd card support on the Beagle-V-Fire as it uses GPIO chip selects

Signed-off-by: Eoin Dickson <eoin.dickson at microchip.com>
---
 drivers/gpio/Kconfig     |   5 +
 drivers/gpio/Makefile    |   1 +
 drivers/gpio/mpfs_gpio.c | 198 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+)
 create mode 100644 drivers/gpio/mpfs_gpio.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e11109fb56d..a47b4be09f7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -729,5 +729,10 @@ config SPL_ADP5585_GPIO
 	depends on SPL_DM_GPIO && SPL_I2C
 	help
 	  Support ADP5585 GPIO expander in SPL.
+config MPFS_GPIO
+	bool "Enable Polarfire SoC GPIO driver"
+	depends on DM_GPIO
+	help
+		Enable to support the GPIO driver on Polarfire SoC
 
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d64c14db5cf..222d51b6d89 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -81,3 +81,4 @@ obj-$(CONFIG_SLG7XL45106_I2C_GPO)	+= gpio_slg7xl45106.o
 obj-$(CONFIG_FTGPIO010)		+= ftgpio010.o
 obj-$(CONFIG_$(PHASE_)ADP5585_GPIO)	+= adp5585_gpio.o
 obj-$(CONFIG_RZG2L_GPIO)	+= rzg2l-gpio.o
+obj-$(CONFIG_MPFS_GPIO)	+= mpfs_gpio.o
diff --git a/drivers/gpio/mpfs_gpio.c b/drivers/gpio/mpfs_gpio.c
new file mode 100644
index 00000000000..9bbeada4ef5
--- /dev/null
+++ b/drivers/gpio/mpfs_gpio.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Microchip Technology Inc.
+ * Eoin Dickson <eoin.dickson at microchip.com>
+ */
+
+#include <dm.h>
+#include <asm-generic/gpio.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+
+#define MPFS_INP_REG			0x84
+#define COREGPIO_INP_REG		0x90
+#define MPFS_OUTP_REG			0x88
+#define COREGPIO_OUTP_REG		0xA0
+#define MPFS_GPIO_CTRL(i)		(0x4 * (i))
+#define MPFS_MAX_NUM_GPIO		32
+#define MPFS_GPIO_EN_OUT_BUF		BIT(2)
+#define MPFS_GPIO_EN_IN			BIT(1)
+#define MPFS_GPIO_EN_OUT		BIT(0)
+
+struct mpfs_gpio_reg_offsets {
+	u8 inp;
+	u8 outp;
+};
+
+struct mchp_gpio_plat {
+	void *base;
+	const struct mpfs_gpio_reg_offsets *regs;
+};
+
+static void mchp_update_gpio_reg(void *bptr, u32 offset, bool value)
+{
+	void __iomem *ptr = (void __iomem *)bptr;
+
+	u32 old = readl(ptr);
+
+	if (value)
+		writel(old | offset, ptr);
+	else
+		writel(old & ~offset, ptr);
+}
+
+static int mchp_gpio_direction_input(struct udevice *dev, u32 offset)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+	if (offset > uc_priv->gpio_count)
+		return -EINVAL;
+
+	mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset),  MPFS_GPIO_EN_IN, true);
+	mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset),  MPFS_GPIO_EN_OUT, false);
+	mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset),  MPFS_GPIO_EN_OUT_BUF, false);
+
+	return 0;
+}
+
+static int mchp_gpio_direction_output(struct udevice *dev, u32 offset, int value)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+	if (offset > uc_priv->gpio_count)
+		return -EINVAL;
+
+	mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset),  MPFS_GPIO_EN_IN, false);
+	mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset),  MPFS_GPIO_EN_OUT, true);
+	mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset),  MPFS_GPIO_EN_OUT_BUF, true);
+
+	mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value);
+
+	return 0;
+}
+
+static bool mchp_gpio_get_value(struct udevice *dev, u32 offset)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	int val, input;
+
+	if (offset > uc_priv->gpio_count)
+		return -EINVAL;
+
+	input = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN;
+
+	if (input)
+		val = (readl(plat->base + plat->regs->inp) & BIT(offset));
+	else
+		val = (readl(plat->base + plat->regs->outp) & BIT(offset));
+
+	return val >> offset;
+}
+
+static int mchp_gpio_set_value(struct udevice *dev, u32 offset, int value)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+	if (offset > uc_priv->gpio_count)
+		return -EINVAL;
+
+	mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value);
+
+	return 0;
+}
+
+static int mchp_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	u32	outdir, indir, val;
+
+	if (offset > uc_priv->gpio_count)
+		return -EINVAL;
+
+	/* Get direction of the pin */
+	outdir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_OUT;
+	indir  = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN;
+
+	if (outdir)
+		val = GPIOF_OUTPUT;
+	else if (indir)
+		val = GPIOF_INPUT;
+	else
+		val = GPIOF_UNUSED;
+
+	return val;
+}
+
+static int mchp_gpio_probe(struct udevice *dev)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	char name[18], *str;
+
+	plat->regs = dev_get_driver_data(dev);
+	sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base);
+	str = strdup(name);
+	if (!str)
+		return -ENOMEM;
+	uc_priv->bank_name = str;
+	uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", MPFS_MAX_NUM_GPIO);
+
+	return 0;
+}
+
+static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = {
+	.inp = MPFS_INP_REG,
+	.outp = MPFS_OUTP_REG,
+};
+
+static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = {
+	.inp = COREGPIO_INP_REG,
+	.outp = COREGPIO_OUTP_REG,
+};
+
+static const struct udevice_id mchp_gpio_match[] = {
+	{
+		.compatible = "microchip,mpfs-gpio",
+		.data = &mpfs_reg_offsets,
+	}, {
+		.compatible = "microchip,coregpio-rtl-v3",
+		.data = &coregpio_reg_offsets,
+	},
+	{ /* end of list */ }
+};
+
+static const struct dm_gpio_ops mchp_gpio_ops = {
+	.direction_input        = mchp_gpio_direction_input,
+	.direction_output       = mchp_gpio_direction_output,
+	.get_value              = mchp_gpio_get_value,
+	.set_value              = mchp_gpio_set_value,
+	.get_function		= mchp_gpio_get_function,
+};
+
+static int mchp_gpio_of_to_plat(struct udevice *dev)
+{
+	struct mchp_gpio_plat *plat = dev_get_plat(dev);
+
+	plat->base = dev_read_addr_ptr(dev);
+	if (!plat->base)
+		return -EINVAL;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(gpio_mpfs) = {
+	.name	= "gpio_mpfs",
+	.id	= UCLASS_GPIO,
+	.of_match = mchp_gpio_match,
+	.of_to_plat = of_match_ptr(mchp_gpio_of_to_plat),
+	.plat_auto	= sizeof(struct mchp_gpio_plat),
+	.ops	= &mchp_gpio_ops,
+	.probe	= mchp_gpio_probe,
+};
-- 
2.34.1



More information about the U-Boot mailing list