[PATCH 5/9] drivers: gpio: add QIXIS FPGA GPIO controller

Ioana Ciornei posted 9 patches 3 months ago
There is a newer version of this series
[PATCH 5/9] drivers: gpio: add QIXIS FPGA GPIO controller
Posted by Ioana Ciornei 3 months ago
Add support for the GPIO controller found on some QIXIS FPGAs in
Layerscape boards such as LX2160ARDB and LS1046AQDS. This driver is
using gpio-regmap.

A GPIO controller has a maximum of 8 lines (all found in the same
register). Even within the same controller, the GPIO lines' direction is
fixed, either output or input, without the possibility to change it.
This is why the driver also implements the newly added .get_diretion()
callback exposed by gpio-regmap.

Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
---
 drivers/gpio/Kconfig           |   9 +++
 drivers/gpio/Makefile          |   1 +
 drivers/gpio/gpio-qixis-fpga.c | 141 +++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 drivers/gpio/gpio-qixis-fpga.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 6802e549621b..fe69b9fa12eb 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1926,6 +1926,15 @@ config GPIO_LATCH
 	  Say yes here to enable a driver for GPIO multiplexers based on latches
 	  connected to other GPIOs.
 
+config GPIO_QIXIS_FPGA
+	tristate "NXP QIXIS FPGA GPIO support"
+	depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST
+	select GPIO_REGMAP
+	help
+	  This enables support for the GPIOs found in the QIXIS FPGA which is
+	  integrated on some NXP Layerscape boards such as LX2160ARDB and
+	  LS1046AQDS.
+
 config GPIO_MOCKUP
 	tristate "GPIO Testing Driver (DEPRECATED)"
 	select IRQ_SIM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 88dedd298256..b8dbd1aa2c85 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -143,6 +143,7 @@ obj-$(CONFIG_GPIO_PL061)		+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PMIC_EIC_SPRD)	+= gpio-pmic-eic-sprd.o
 obj-$(CONFIG_GPIO_POLARFIRE_SOC)	+= gpio-mpfs.o
 obj-$(CONFIG_GPIO_PXA)			+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QIXIS_FPGA)		+= gpio-qixis-fpga.o
 obj-$(CONFIG_GPIO_RASPBERRYPI_EXP)	+= gpio-raspberrypi-exp.o
 obj-$(CONFIG_GPIO_RC5T583)		+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RCAR)			+= gpio-rcar.o
diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c
new file mode 100644
index 000000000000..d5b7619970e9
--- /dev/null
+++ b/drivers/gpio/gpio-qixis-fpga.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Layerscape GPIO QIXIS FPGA driver
+ *
+ * Copyright 2025 NXP
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+enum qixis_cpld_gpio_type {
+	LX2160ARDB_GPIO_SFP2 = 0,
+	LX2160ARDB_GPIO_SFP3,
+	LS1046AQDS_GPIO_STAT_PRES2,
+};
+
+struct qixis_cpld_gpio_config {
+	enum qixis_cpld_gpio_type type;
+	unsigned int input_lines;
+};
+
+static struct qixis_cpld_gpio_config lx2160ardb_sfp2_cfg = {
+	.type = LX2160ARDB_GPIO_SFP2,
+	.input_lines = GENMASK(7, 1),
+};
+
+static struct qixis_cpld_gpio_config lx2160ardb_sfp3_cfg = {
+	.type = LX2160ARDB_GPIO_SFP3,
+	.input_lines = GENMASK(7, 1),
+};
+
+static struct qixis_cpld_gpio_config ls1046aqds_stat_pres2_cfg = {
+	.type = LS1046AQDS_GPIO_STAT_PRES2,
+	.input_lines = GENMASK(7, 0),
+};
+
+static int qixis_cpld_gpio_get_direction(struct gpio_regmap *gpio, unsigned int offset)
+{
+	struct qixis_cpld_gpio_config *cfg = gpio_regmap_get_drvdata(gpio);
+
+	if (cfg->input_lines & BIT(offset))
+		return GPIO_LINE_DIRECTION_IN;
+	else
+		return GPIO_LINE_DIRECTION_OUT;
+}
+
+static const struct regmap_config regmap_config_8r_8v = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int qixis_cpld_gpio_probe(struct platform_device *pdev)
+{
+	const struct qixis_cpld_gpio_config *cfg;
+	struct gpio_regmap_config config = {0};
+	struct regmap *regmap;
+	void __iomem *reg;
+	u32 base;
+	int ret;
+
+	if (!pdev->dev.parent)
+		return -ENODEV;
+
+	cfg = device_get_match_data(&pdev->dev);
+	if (!cfg)
+		return -ENODEV;
+
+	ret = device_property_read_u32(&pdev->dev, "reg", &base);
+	if (ret)
+		return ret;
+
+	regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!regmap) {
+		/* In case there is no regmap configured by the parent device,
+		 * create our own.
+		 */
+		reg = devm_platform_ioremap_resource(pdev, 0);
+		if (!reg)
+			return -ENODEV;
+
+		regmap = devm_regmap_init_mmio(&pdev->dev, reg, &regmap_config_8r_8v);
+		if (!regmap)
+			return -ENODEV;
+
+		/* In this case, the offset of our register is 0 inside the
+		 * regmap area that we just created.
+		 */
+		base = 0;
+	}
+
+	config.get_direction = qixis_cpld_gpio_get_direction;
+	config.drvdata = (void *)cfg;
+	config.regmap = regmap;
+	config.parent = &pdev->dev;
+	config.ngpio_per_reg = 8;
+	config.ngpio = 8;
+
+	switch (cfg->type) {
+	case LX2160ARDB_GPIO_SFP2:
+	case LX2160ARDB_GPIO_SFP3:
+	case LS1046AQDS_GPIO_STAT_PRES2:
+		config.reg_dat_base = GPIO_REGMAP_ADDR(base);
+		config.reg_set_base = GPIO_REGMAP_ADDR(base);
+		break;
+	}
+
+	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
+}
+
+static const struct of_device_id qixis_cpld_gpio_of_match[] = {
+	{
+		.compatible = "fsl,lx2160ardb-fpga-gpio-sfp2",
+		.data = &lx2160ardb_sfp2_cfg,
+	},
+	{
+		.compatible = "fsl,lx2160ardb-fpga-gpio-sfp3",
+		.data = &lx2160ardb_sfp3_cfg,
+	},
+	{
+		.compatible = "fsl,ls1046aqds-fpga-gpio-stat-pres2",
+		.data = &ls1046aqds_stat_pres2_cfg,
+	},
+
+	{}
+};
+MODULE_DEVICE_TABLE(of, qixis_cpld_gpio_of_match);
+
+static struct platform_driver qixis_cpld_gpio_driver = {
+	.probe = qixis_cpld_gpio_probe,
+	.driver = {
+		.name = "gpio-qixis-cpld",
+		.of_match_table = qixis_cpld_gpio_of_match,
+	},
+};
+module_platform_driver(qixis_cpld_gpio_driver);
-- 
2.25.1
Re: [PATCH 5/9] drivers: gpio: add QIXIS FPGA GPIO controller
Posted by Andrew Lunn 2 months, 4 weeks ago
> A GPIO controller has a maximum of 8 lines (all found in the same
> register). Even within the same controller, the GPIO lines' direction is
> fixed, either output or input, without the possibility to change it.

Since this is an FPGA, not silicon, is the selection of output or
input a syntheses option?

> +static const struct of_device_id qixis_cpld_gpio_of_match[] = {
> +	{
> +		.compatible = "fsl,lx2160ardb-fpga-gpio-sfp2",
> +		.data = &lx2160ardb_sfp2_cfg,
> +	},
> +	{
> +		.compatible = "fsl,lx2160ardb-fpga-gpio-sfp3",
> +		.data = &lx2160ardb_sfp3_cfg,
> +	},
> +	{
> +		.compatible = "fsl,ls1046aqds-fpga-gpio-stat-pres2",
> +		.data = &ls1046aqds_stat_pres2_cfg,
> +	},

Does the FPGA have an ID register you can read to confirm it is what
you think it is?

Or is the bitstream downloaded at boot by another driver? Can you ask
that driver what bitstream it downloaded?

Given how similar these devices are, it seems like a typ0 could give a
mostly working device which passes testing, so doing some validation
of the compatible against the actual FPGA would be nice.

	Andrew
Re: [PATCH 5/9] drivers: gpio: add QIXIS FPGA GPIO controller
Posted by Ioana Ciornei 2 months, 4 weeks ago
On Wed, Jul 09, 2025 at 05:17:18PM +0200, Andrew Lunn wrote:
> > A GPIO controller has a maximum of 8 lines (all found in the same
> > register). Even within the same controller, the GPIO lines' direction is
> > fixed, either output or input, without the possibility to change it.
> 
> Since this is an FPGA, not silicon, is the selection of output or
> input a syntheses option?

I suppose so, yes. The idea is that in this particular case, the fixed
direction for each GPIO line (bit in the register) matches the use case
and will not be changed. For example, the presence detect or rx los
GPIOs for the SFP cages are only input, while the tx enable one is
output always.

>
> > +static const struct of_device_id qixis_cpld_gpio_of_match[] = {
> > +	{
> > +		.compatible = "fsl,lx2160ardb-fpga-gpio-sfp2",
> > +		.data = &lx2160ardb_sfp2_cfg,
> > +	},
> > +	{
> > +		.compatible = "fsl,lx2160ardb-fpga-gpio-sfp3",
> > +		.data = &lx2160ardb_sfp3_cfg,
> > +	},
> > +	{
> > +		.compatible = "fsl,ls1046aqds-fpga-gpio-stat-pres2",
> > +		.data = &ls1046aqds_stat_pres2_cfg,
> > +	},
> 
> Does the FPGA have an ID register you can read to confirm it is what
> you think it is?
> 
> Or is the bitstream downloaded at boot by another driver? Can you ask
> that driver what bitstream it downloaded?
> 
> Given how similar these devices are, it seems like a typ0 could give a
> mostly working device which passes testing, so doing some validation
> of the compatible against the actual FPGA would be nice.
> 

The FPGA does have an ID register that we could verify and match against
the board type that we expect.

On the other hand, I am not 100% on board with the idea to check this
from the GPIO driver which teoretically should only touch its one
register. Maybe from the parent's driver we could do that and prevent
the probing of children if things don't match up. But this does prove to
be complicated since those drivers are simple-mfd (for LS1046AQDS) and
simple-mfd-i2c (for LX2160ARDB). And I don't think it would be wise to
add some specific board logic into of/platform.c.

Ioana
Re: [PATCH 5/9] drivers: gpio: add QIXIS FPGA GPIO controller
Posted by Andrew Lunn 2 months, 4 weeks ago
> The FPGA does have an ID register that we could verify and match against
> the board type that we expect.
> 
> On the other hand, I am not 100% on board with the idea to check this
> from the GPIO driver which teoretically should only touch its one
> register. Maybe from the parent's driver we could do that and prevent
> the probing of children if things don't match up. But this does prove to
> be complicated since those drivers are simple-mfd (for LS1046AQDS) and
> simple-mfd-i2c (for LX2160ARDB). And I don't think it would be wise to
> add some specific board logic into of/platform.c.

My experience is, DT authors will mess up and put in the wrong
compatible. And the wrong compatible might be enough for it to mostly
work, so you end up with deployed systems with wrong compatibles. It
then becomes difficult to actually extend the use of the compatible,
without causing regressions.

Also, checking will catch putting the wrong bitstream into the FPGA.

So if you can check it, do check it, and return -ENODEV.

   Andrew