[PATCH 15/27] gpio: pxa: add a driver and switch the architecture to GPIOLIB

Sascha Hauer s.hauer at pengutronix.de
Sun Aug 16 10:56:35 PDT 2026


PXA had no GPIO driver at all. ARCH_PXA selected GENERIC_GPIO, which on
PXA means arch/arm/mach-pxa/gpio.c: a handful of gpio_get_value() and
friends operating directly on the registers, but no gpio chip. So none
of the GPIOs described in the device tree could be used and every
consumer of them silently did nothing, be it the leds, the gpio keys,
the poweroff or the reset of the ethernet chip.

Add a proper driver for the device tree described controller and switch
the architecture over to GPIOLIB. The register layout is simple: banks
of 32 GPIOs, the first three interleaved one 32bit register per bank and
every further group of three starting at the next 0x100 boundary.

The old implementation has to go in the same step, its gpio_set_value()
and friends collide with the ones from gpiolib. Its only users were the
pxa_init_gpio() calls in the PXA2xx MFP code, which set up the gpio
range for that implementation and have no equivalent here: the range
comes from the device tree now.

Assisted-by: Claude Opus 5
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 arch/arm/Kconfig           |   2 +-
 arch/arm/mach-pxa/Makefile |   1 -
 arch/arm/mach-pxa/gpio.c   | 101 -------------------------------
 drivers/gpio/Kconfig       |   6 ++
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/gpio-pxa.c    | 146 +++++++++++++++++++++++++++++++++++++++++++++
 include/mach/pxa/gpio.h    |   1 -
 7 files changed, 154 insertions(+), 104 deletions(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index fe4fd3f471..2f2ab5d76d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -121,7 +121,7 @@ config ARCH_MXS
 config ARCH_PXA
 	bool "Intel/Marvell PXA based"
 	depends on 32BIT
-	select GENERIC_GPIO
+	select GPIOLIB
 
 config ARCH_SOCFPGA
 	bool "Altera SOCFPGA"
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index b4f3a51046..fa0793d8a9 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -3,7 +3,6 @@
 obj-y += clocksource.o
 obj-y += sleep.o
 obj-y += common.o
-obj-y += gpio.o
 obj-y += devices.o
 
 obj-$(CONFIG_ARCH_PXA3XX) += mfp-pxa3xx.o pxa3xx.o
diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c
deleted file mode 100644
index 130faa404b..0000000000
--- a/arch/arm/mach-pxa/gpio.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  Generic PXA GPIO handling
- *
- *  Author:	Nicolas Pitre
- *  Created:	Jun 15, 2001
- *  Copyright:	MontaVista Software Inc.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-
-#include <common.h>
-#include <errno.h>
-#include <gpio.h>
-
-#include <mach/pxa/gpio.h>
-#include <asm/io.h>
-
-int pxa_last_gpio;
-
-struct pxa_gpio_chip {
-	void __iomem	*regbase;
-};
-
-static struct pxa_gpio_chip *pxa_gpio_chips;
-
-#define for_each_gpio_chip(i, c) \
-	for (i = 0, c = &pxa_gpio_chips[0]; i <= pxa_last_gpio; i += 32, c++)
-
-static int __init pxa_init_gpio_chip(int gpio_end)
-{
-	int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1;
-	struct pxa_gpio_chip *chips;
-
-	chips = kzalloc(nbanks * sizeof(struct pxa_gpio_chip), GFP_KERNEL);
-	if (chips == NULL) {
-		pr_err("%s: failed to allocate GPIO chips\n", __func__);
-		return -ENOMEM;
-	}
-
-	for (i = 0, gpio = 0; i < nbanks; i++, gpio += 32)
-		chips[i].regbase = (void __iomem *)GPIO_BANK(i);
-
-	pxa_gpio_chips = chips;
-	return 0;
-}
-
-int __init pxa_init_gpio(int start, int end)
-{
-	struct pxa_gpio_chip *c;
-	int err,  gpio;
-
-	pxa_last_gpio = end;
-
-	/* Initialize GPIO chips */
-	err = pxa_init_gpio_chip(end);
-	if (err)
-		return err;
-
-	for_each_gpio_chip(gpio, c) {
-		/* clear all GPIO edge detects */
-		__raw_writel(0, c->regbase + GFER_OFFSET);
-		__raw_writel(0, c->regbase + GRER_OFFSET);
-		__raw_writel(~0, c->regbase + GEDR_OFFSET);
-	}
-
-	return 0;
-}
-
-int gpio_get_value(unsigned gpio)
-{
-	return GPLR(gpio) & GPIO_bit(gpio);
-}
-
-void gpio_set_value(unsigned gpio, int value)
-{
-	if (value)
-		GPSR(gpio) = GPIO_bit(gpio);
-	else
-		GPCR(gpio) = GPIO_bit(gpio);
-}
-
-int gpio_direction_input(unsigned gpio)
-{
-	if (__gpio_is_inverted(gpio))
-		GPDR(gpio) |= GPIO_bit(gpio);
-	else
-		GPDR(gpio) &= ~GPIO_bit(gpio);
-	return 0;
-}
-
-int gpio_direction_output(unsigned gpio, int value)
-{
-	gpio_set_value(gpio, value);
-	if (__gpio_is_inverted(gpio))
-		GPDR(gpio) &= ~GPIO_bit(gpio);
-	else
-		GPDR(gpio) |= GPIO_bit(gpio);
-	return 0;
-}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 988d08cf14..923d424e62 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -163,6 +163,12 @@ config GPIO_PL061
 	help
 	  Say yes here to support the PrimeCell PL061 GPIO device
 
+config GPIO_PXA
+	bool "GPIO support for Intel/Marvell PXA"
+	depends on ARCH_PXA || COMPILE_TEST
+	help
+	  Say yes here to enable the GPIO driver for the PXA SoCs
+
 config GPIO_RASPBERRYPI_EXP
 	bool "Raspberry Pi 3 GPIO Expander"
 	depends on ARCH_BCM283X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 8560e1f059..38059a47f7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_GPIO_JZ4740)	+= gpio-jz4740.o
 obj-$(CONFIG_GPIO_MALTA_FPGA_I2C) += gpio-malta-fpga-i2c.o
 obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
 obj-$(CONFIG_GPIO_ORION)	+= gpio-orion.o
+obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
 obj-$(CONFIG_GPIO_OMAP)		+= gpio-omap.o
 obj-$(CONFIG_GPIO_PCA953X)	+= gpio-pca953x.o
 obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
new file mode 100644
index 0000000000..04b547a141
--- /dev/null
+++ b/drivers/gpio/gpio-pxa.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * GPIO driver for the Intel/Marvell PXA SoCs
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <gpio.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <of_device.h>
+
+/*
+ * The registers are grouped in banks of 32 GPIOs. The first three banks
+ * are interleaved, one 32bit register per bank, and every further group
+ * of three banks starts at the next 0x100 boundary.
+ */
+#define GPLR	0x00	/* pin level, read only */
+#define GPDR	0x0c	/* direction, 1 = output */
+#define GPSR	0x18	/* set output, write 1 to set */
+#define GPCR	0x24	/* clear output, write 1 to clear */
+
+#define BANK_OFF(bank)	((((bank) / 3) << 8) + (((bank) % 3) << 2))
+
+struct pxa_gpio_chip {
+	struct gpio_chip chip;
+	void __iomem *base;
+};
+
+static inline struct pxa_gpio_chip *to_pxa_gpio(struct gpio_chip *chip)
+{
+	return container_of(chip, struct pxa_gpio_chip, chip);
+}
+
+static inline void __iomem *pxa_gpio_reg(struct gpio_chip *chip,
+					 unsigned off, unsigned reg)
+{
+	return to_pxa_gpio(chip)->base + BANK_OFF(off / 32) + reg;
+}
+
+static int pxa_gpio_get(struct gpio_chip *chip, unsigned off)
+{
+	return !!(readl(pxa_gpio_reg(chip, off, GPLR)) & BIT(off % 32));
+}
+
+static int pxa_gpio_set(struct gpio_chip *chip, unsigned off, int value)
+{
+	writel(BIT(off % 32), pxa_gpio_reg(chip, off, value ? GPSR : GPCR));
+
+	return 0;
+}
+
+static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+	void __iomem *gpdr = pxa_gpio_reg(chip, off, GPDR);
+
+	writel(readl(gpdr) & ~BIT(off % 32), gpdr);
+
+	return 0;
+}
+
+static int pxa_gpio_direction_output(struct gpio_chip *chip, unsigned off,
+				     int value)
+{
+	void __iomem *gpdr = pxa_gpio_reg(chip, off, GPDR);
+
+	/* drive the requested level before switching the pin to output */
+	pxa_gpio_set(chip, off, value);
+	writel(readl(gpdr) | BIT(off % 32), gpdr);
+
+	return 0;
+}
+
+static int pxa_gpio_get_direction(struct gpio_chip *chip, unsigned off)
+{
+	if (readl(pxa_gpio_reg(chip, off, GPDR)) & BIT(off % 32))
+		return GPIOF_DIR_OUT;
+
+	return GPIOF_DIR_IN;
+}
+
+static struct gpio_ops pxa_gpio_ops = {
+	.direction_input = pxa_gpio_direction_input,
+	.direction_output = pxa_gpio_direction_output,
+	.get_direction = pxa_gpio_get_direction,
+	.get = pxa_gpio_get,
+	.set = pxa_gpio_set,
+};
+
+static int pxa_gpio_probe(struct device *dev)
+{
+	struct pxa_gpio_chip *pxa;
+	struct resource *iores;
+	int ret;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	pxa = xzalloc(sizeof(*pxa));
+	pxa->base = IOMEM(iores->start);
+
+	pxa->chip.dev = dev;
+	pxa->chip.ops = &pxa_gpio_ops;
+	pxa->chip.base = 0;
+	pxa->chip.ngpio = (uintptr_t)device_get_match_data(dev);
+
+	ret = gpiochip_add(&pxa->chip);
+	if (ret) {
+		dev_err(dev, "couldn't add gpiochip: %pe\n", ERR_PTR(ret));
+		free(pxa);
+		return ret;
+	}
+
+	dev_dbg(dev, "probed %u gpios\n", pxa->chip.ngpio);
+
+	return 0;
+}
+
+static struct of_device_id pxa_gpio_dt_ids[] = {
+	{
+		.compatible = "intel,pxa25x-gpio",
+		.data = (void *)85,
+	}, {
+		.compatible = "intel,pxa26x-gpio",
+		.data = (void *)90,
+	}, {
+		.compatible = "intel,pxa27x-gpio",
+		.data = (void *)121,
+	}, {
+		.compatible = "intel,pxa3xx-gpio",
+		.data = (void *)128,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, pxa_gpio_dt_ids);
+
+static struct driver pxa_gpio_driver = {
+	.name = "pxa-gpio",
+	.probe = pxa_gpio_probe,
+	.of_compatible = DRV_OF_COMPAT(pxa_gpio_dt_ids),
+};
+core_platform_driver(pxa_gpio_driver);
diff --git a/include/mach/pxa/gpio.h b/include/mach/pxa/gpio.h
index 2d169608d8..84331f6b5c 100644
--- a/include/mach/pxa/gpio.h
+++ b/include/mach/pxa/gpio.h
@@ -133,6 +133,5 @@ static inline int __gpio_is_inverted(unsigned gpio) { return 0; }
  */
 extern int pxa_last_gpio;
 
-extern int pxa_init_gpio(int start, int end);
 
 #endif

-- 
2.47.3




More information about the barebox mailing list