[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250709091542.968968-1-marcos@orca.pet>
Date: Wed, 9 Jul 2025 11:15:40 +0200
From: Marcos Del Sol Vives <marcos@...a.pet>
To: linux-kernel@...r.kernel.org
Cc: marcos@...a.pet,
Linus Walleij <linus.walleij@...aro.org>,
Bartosz Golaszewski <brgl@...ev.pl>,
linux-gpio@...r.kernel.org
Subject: [PATCH v2] gpio: vortex: add new GPIO device driver
Add a new simple GPIO device driver for Vortex86 lines of SoCs,
implemented according to their programming reference manual [1].
This is required for detecting the status of the poweroff button and
performing the poweroff sequence on ICOP eBox computers.
IRQs are not implemented as they are available for less than half the
GPIO pins, and they are not the ones required for the poweroff stuff, so
polling will be required anyway.
[1]: http://www.dmp.com.tw/tech/DMP_Vortex86_Series_Software_Programming_Reference_091216.pdf
Signed-off-by: Marcos Del Sol Vives <marcos@...a.pet>
---
MAINTAINERS | 6 ++
drivers/gpio/Kconfig | 10 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-vortex.c | 169 +++++++++++++++++++++++++++++++++++++
4 files changed, 186 insertions(+)
create mode 100644 drivers/gpio/gpio-vortex.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ff244fe3e1a..0ad462e8cceb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26568,6 +26568,12 @@ VOLTAGE AND CURRENT REGULATOR IRQ HELPERS
R: Matti Vaittinen <mazziesaccount@...il.com>
F: drivers/regulator/irq_helpers.c
+VORTEX GPIO DRIVER
+R: Marcos Del Sol Vives <marcos@...a.pet>
+L: linux-gpio@...r.kernel.org
+S: Maintained
+F: drivers/gpio/gpio-vortex.c
+
VRF
M: David Ahern <dsahern@...nel.org>
L: netdev@...r.kernel.org
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 44f922e10db2..796fd6d43910 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1066,6 +1066,16 @@ config GPIO_TS5500
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
+config GPIO_VORTEX
+ tristate "Vortex86 SoC GPIO support"
+ depends on CPU_SUP_VORTEX_32 || COMPILE_TEST
+ help
+ Driver to access the five 8-bit bidirectional GPIO ports present on
+ all DM&P Vortex86 SoCs.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-vortex.
+
config GPIO_WINBOND
tristate "Winbond Super I/O GPIO support"
select ISA_BUS_API
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 88dedd298256..d226fc751686 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -196,6 +196,7 @@ obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o
obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
+obj-$(CONFIG_GPIO_VORTEX) += gpio-vortex.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o
diff --git a/drivers/gpio/gpio-vortex.c b/drivers/gpio/gpio-vortex.c
new file mode 100644
index 000000000000..cf14d56d22d9
--- /dev/null
+++ b/drivers/gpio/gpio-vortex.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * GPIO driver for Vortex86 SoCs
+ *
+ * Author: Marcos Del Sol Vives <marcos@...a.pet>
+ *
+ * Based on the it87xx GPIO driver by Diego Elio Pettenò
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+
+#define GPIO_PORTS 5
+#define GPIO_PER_PORT 8
+#define GPIO_COUNT (GPIO_PORTS * GPIO_PER_PORT)
+
+#define GPIO_DATA_BASE 0x78
+#define GPIO_DIRECTION_BASE 0x98
+
+static struct platform_device *pdev;
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static int vortex_gpio_get(struct gpio_chip *chip, unsigned int gpio_num)
+{
+ uint8_t port = gpio_num / GPIO_PER_PORT;
+ uint8_t bit = gpio_num % GPIO_PER_PORT;
+ uint8_t val;
+
+ val = inb(GPIO_DATA_BASE + port);
+ return !!(val & (1 << bit));
+}
+
+static int vortex_gpio_direction_in(struct gpio_chip *chip, unsigned int gpio_num)
+{
+ uint8_t port = gpio_num / GPIO_PER_PORT;
+ uint8_t bit = gpio_num % GPIO_PER_PORT;
+ unsigned long flags;
+ uint8_t dir;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ dir = inb(GPIO_DIRECTION_BASE + port);
+ dir &= ~(1 << bit); /* 0 = input */
+ outb(dir, GPIO_DIRECTION_BASE + port);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int vortex_gpio_set(struct gpio_chip *chip, unsigned int gpio_num, int value)
+{
+ uint8_t port = gpio_num / GPIO_PER_PORT;
+ uint8_t bit = gpio_num % GPIO_PER_PORT;
+ unsigned long flags;
+ uint8_t dat;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ dat = inb(GPIO_DATA_BASE + port);
+ if (value)
+ dat |= (1 << bit);
+ else
+ dat &= ~(1 << bit);
+ outb(dat, GPIO_DATA_BASE + port);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static int vortex_gpio_direction_out(struct gpio_chip *chip, unsigned int gpio_num, int value)
+{
+ uint8_t port = gpio_num / GPIO_PER_PORT;
+ uint8_t bit = gpio_num % GPIO_PER_PORT;
+ unsigned long flags;
+ uint8_t dir, dat;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /* Have to set direction first. Else writes to data are ignored. */
+ dir = inb(GPIO_DIRECTION_BASE + port);
+ dir |= (1 << bit); /* 1 = output */
+ outb(dir, GPIO_DIRECTION_BASE + port);
+
+ dat = inb(GPIO_DATA_BASE + port);
+ if (value)
+ dat |= (1 << bit);
+ else
+ dat &= ~(1 << bit);
+ outb(dat, GPIO_DATA_BASE + port);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+
+static char labels[GPIO_COUNT][sizeof("vortex_gpXY")];
+static char *labels_table[GPIO_COUNT];
+
+static struct gpio_chip gpio_chip = {
+ .label = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .get = vortex_gpio_get,
+ .direction_input = vortex_gpio_direction_in,
+ .set_rv = vortex_gpio_set,
+ .direction_output = vortex_gpio_direction_out,
+ .base = -1,
+ .ngpio = GPIO_COUNT,
+ .names = (const char * const *)labels_table,
+};
+
+static int vortex_gpio_probe(struct platform_device *pdev)
+{
+ /* Set up GPIO labels */
+ for (int i = 0; i < GPIO_COUNT; i++) {
+ sprintf(labels[i], "vortex_gp%u%u", i / 8, i % 8);
+ labels_table[i] = &labels[i][0];
+ }
+
+ return devm_gpiochip_add_data(&pdev->dev, &gpio_chip, NULL);
+}
+
+static struct platform_driver vortex_gpio_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = vortex_gpio_probe,
+};
+
+static struct resource vortex_gpio_resources[] = {
+ DEFINE_RES_IO_NAMED(GPIO_DATA_BASE, GPIO_PORTS, KBUILD_MODNAME " data"),
+ DEFINE_RES_IO_NAMED(GPIO_DIRECTION_BASE, GPIO_PORTS, KBUILD_MODNAME " dir"),
+};
+
+static int __init vortex_gpio_init(void)
+{
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_VORTEX) {
+ pr_err("Not a Vortex86 CPU, refusing to load\n");
+ return -ENODEV;
+ }
+
+ pdev = platform_create_bundle(&vortex_gpio_driver, vortex_gpio_probe,
+ vortex_gpio_resources, ARRAY_SIZE(vortex_gpio_resources),
+ NULL, 0);
+ return PTR_ERR_OR_ZERO(pdev);
+}
+
+static void __exit vortex_gpio_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&vortex_gpio_driver);
+}
+
+module_init(vortex_gpio_init);
+module_exit(vortex_gpio_exit);
+
+MODULE_AUTHOR("Marcos Del Sol Vives <marcos@...a.pet>");
+MODULE_DESCRIPTION("GPIO driver for Vortex86 SoCs");
+MODULE_LICENSE("GPL");
--
2.34.1
Powered by blists - more mailing lists