mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-03-14 19:39:48 +01:00
generic: backport gpio-line-mux driver
Backport the upstream 'gpio-line-mux' driver which allows to provide a 1-to-many mapping between one physical GPIO and multiple virtual GPIOs, based on a multiplexer. For this purpose, there's been a dedicated downstream driver 'gpio-cascade' which is mostly the same, but wasn't upstreamed in the end. Independently developed, the 'gpio-line-mux' driver was upstreamed to solve the exact same problem occuring on Realtek-based Zyxel XS1930 switches. Support for those is being worked on, but the hardware uses a similar quirk for SFP signals. The signals 'RX_LOS', 'MOD_ABS' and 'TX_FAULT' do not have dedicated GPIOs each but all use a single GPIO which is multiplexed. Depending on the multiplexer state the GPIO line is connected to one of the signals. Since the SFP driver needs single GPIOs for the single signals, this adapter drivers fills the gap to make both work together. Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> Link: https://github.com/openwrt/openwrt/pull/22206 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
parent
59862e9ffe
commit
95532ba906
4 changed files with 390 additions and 0 deletions
|
|
@ -0,0 +1,128 @@
|
|||
From 2a7618ba8698874e9871a8ec5453e0068e94d9e5 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
Date: Sat, 27 Dec 2025 18:01:33 +0000
|
||||
Subject: [PATCH] dt-bindings: gpio: add gpio-line-mux controller
|
||||
|
||||
Add dt-schema for a gpio-line-mux controller which exposes virtual
|
||||
GPIOs for a shared GPIO controlled by a multiplexer, e.g. a gpio-mux.
|
||||
|
||||
The gpio-line-mux controller is a gpio-controller, thus has mostly the
|
||||
same semantics. However, it requires a mux-control to be specified upon
|
||||
which it will operate.
|
||||
|
||||
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
|
||||
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
||||
Link: https://lore.kernel.org/r/20251227180134.1262138-2-jelonek.jonas@gmail.com
|
||||
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml
|
||||
@@ -0,0 +1,107 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: GPIO line mux
|
||||
+
|
||||
+maintainers:
|
||||
+ - Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
+
|
||||
+description: |
|
||||
+ A GPIO controller to provide virtual GPIOs for a 1-to-many input-only mapping
|
||||
+ backed by a single shared GPIO and a multiplexer. A simple illustrated
|
||||
+ example is:
|
||||
+
|
||||
+ +----- A
|
||||
+ IN /
|
||||
+ <-----o------- B
|
||||
+ / |\
|
||||
+ | | +----- C
|
||||
+ | | \
|
||||
+ | | +--- D
|
||||
+ | |
|
||||
+ M1 M0
|
||||
+
|
||||
+ MUX CONTROL
|
||||
+
|
||||
+ M1 M0 IN
|
||||
+ 0 0 A
|
||||
+ 0 1 B
|
||||
+ 1 0 C
|
||||
+ 1 1 D
|
||||
+
|
||||
+ This can be used in case a real GPIO is connected to multiple inputs and
|
||||
+ controlled by a multiplexer, and another subsystem/driver does not work
|
||||
+ directly with the multiplexer subsystem.
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ const: gpio-line-mux
|
||||
+
|
||||
+ gpio-controller: true
|
||||
+
|
||||
+ "#gpio-cells":
|
||||
+ const: 2
|
||||
+
|
||||
+ gpio-line-mux-states:
|
||||
+ description: Mux states corresponding to the virtual GPIOs.
|
||||
+ $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
+
|
||||
+ gpio-line-names: true
|
||||
+
|
||||
+ mux-controls:
|
||||
+ maxItems: 1
|
||||
+ description:
|
||||
+ Phandle to the multiplexer to control access to the GPIOs.
|
||||
+
|
||||
+ ngpios: false
|
||||
+
|
||||
+ muxed-gpios:
|
||||
+ maxItems: 1
|
||||
+ description:
|
||||
+ GPIO which is the '1' in 1-to-many and is shared by the virtual GPIOs
|
||||
+ and controlled via the mux.
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - gpio-controller
|
||||
+ - gpio-line-mux-states
|
||||
+ - mux-controls
|
||||
+ - muxed-gpios
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ #include <dt-bindings/gpio/gpio.h>
|
||||
+ #include <dt-bindings/mux/mux.h>
|
||||
+
|
||||
+ sfp_gpio_mux: mux-controller-1 {
|
||||
+ compatible = "gpio-mux";
|
||||
+ mux-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>,
|
||||
+ <&gpio0 1 GPIO_ACTIVE_HIGH>;
|
||||
+ #mux-control-cells = <0>;
|
||||
+ idle-state = <MUX_IDLE_AS_IS>;
|
||||
+ };
|
||||
+
|
||||
+ sfp1_gpio: sfp-gpio-1 {
|
||||
+ compatible = "gpio-line-mux";
|
||||
+ gpio-controller;
|
||||
+ #gpio-cells = <2>;
|
||||
+
|
||||
+ mux-controls = <&sfp_gpio_mux>;
|
||||
+ muxed-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
|
||||
+
|
||||
+ gpio-line-mux-states = <0>, <1>, <3>;
|
||||
+ };
|
||||
+
|
||||
+ sfp1: sfp-p1 {
|
||||
+ compatible = "sff,sfp";
|
||||
+
|
||||
+ i2c-bus = <&sfp1_i2c>;
|
||||
+ los-gpios = <&sfp1_gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
+ mod-def0-gpios = <&sfp1_gpio 1 GPIO_ACTIVE_LOW>;
|
||||
+ tx-fault-gpios = <&sfp1_gpio 2 GPIO_ACTIVE_HIGH>;
|
||||
+ };
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
From 2b03d9a40cd1fea42fd65d2b66df80edc0f374c8 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
Date: Sat, 27 Dec 2025 18:01:34 +0000
|
||||
Subject: [PATCH] gpio: add gpio-line-mux driver
|
||||
|
||||
Add a new driver which provides a 1-to-many mapping for a single real
|
||||
GPIO using a multiplexer. Each virtual GPIO corresponds to a multiplexer
|
||||
state which, if set for the multiplexer, connects the real GPIO to the
|
||||
corresponding virtual GPIO.
|
||||
|
||||
This can help in various usecases. One practical case is the special
|
||||
hardware design of the Realtek-based XS1930-10 switch from Zyxel. It
|
||||
features two SFP+ ports/cages whose signals are wired directly to the
|
||||
switch SoC. Although Realtek SoCs are short on GPIOs, there are usually
|
||||
enough the fit the SFP signals without any hacks.
|
||||
|
||||
However, Zyxel did some weird design and connected RX_LOS, MOD_ABS and
|
||||
TX_FAULT of one SFP cage onto a single GPIO line controlled by a
|
||||
multiplexer (the same for the other SFP cage). The single multiplexer
|
||||
controls the lines for both SFP and depending on the state, the
|
||||
designated 'signal GPIO lines' are connected to one of the three SFP
|
||||
signals.
|
||||
|
||||
Because the SFP core/driver doesn't support multiplexer but needs single
|
||||
GPIOs for each of the signals, this driver fills the gap between both.
|
||||
It registers a gpio_chip, provides multiple virtual GPIOs and sets the
|
||||
backing multiplexer accordingly.
|
||||
|
||||
Due to several practical issues, this is input-only and doesn't support
|
||||
IRQs.
|
||||
|
||||
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
Reviewed-by: Thomas Richard <thomas.richard@bootlin.com>
|
||||
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
||||
Link: https://lore.kernel.org/r/20251227180134.1262138-3-jelonek.jonas@gmail.com
|
||||
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -9704,6 +9704,12 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.yaml
|
||||
F: drivers/media/rc/gpio-ir-tx.c
|
||||
|
||||
+GPIO LINE MUX
|
||||
+M: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
+S: Maintained
|
||||
+F: Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml
|
||||
+F: drivers/gpio/gpio-line-mux.c
|
||||
+
|
||||
GPIO MOCKUP DRIVER
|
||||
M: Bamvor Jian Zhang <bamv2005@gmail.com>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
--- a/drivers/gpio/Kconfig
|
||||
+++ b/drivers/gpio/Kconfig
|
||||
@@ -1866,6 +1866,15 @@ config GPIO_LATCH
|
||||
Say yes here to enable a driver for GPIO multiplexers based on latches
|
||||
connected to other GPIOs.
|
||||
|
||||
+config GPIO_LINE_MUX
|
||||
+ tristate "GPIO line mux driver"
|
||||
+ depends on OF_GPIO
|
||||
+ select MULTIPLEXER
|
||||
+ help
|
||||
+ Say Y here to support the GPIO line mux, which can provide virtual
|
||||
+ GPIOs backed by a shared real GPIO and a multiplexer in a 1-to-many
|
||||
+ fashion.
|
||||
+
|
||||
config GPIO_MOCKUP
|
||||
tristate "GPIO Testing Driver (DEPRECATED)"
|
||||
select IRQ_SIM
|
||||
--- a/drivers/gpio/Makefile
|
||||
+++ b/drivers/gpio/Makefile
|
||||
@@ -84,6 +84,7 @@ obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4x
|
||||
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
|
||||
obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
|
||||
obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o
|
||||
+obj-$(CONFIG_GPIO_LINE_MUX) += gpio-line-mux.o
|
||||
obj-$(CONFIG_GPIO_LJCA) += gpio-ljca.o
|
||||
obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o
|
||||
obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/gpio/gpio-line-mux.c
|
||||
@@ -0,0 +1,126 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * GPIO line mux which acts as virtual gpiochip and provides a 1-to-many
|
||||
+ * mapping between virtual GPIOs and a real GPIO + multiplexer.
|
||||
+ *
|
||||
+ * Copyright (c) 2025 Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/gpio/consumer.h>
|
||||
+#include <linux/gpio/driver.h>
|
||||
+#include <linux/mod_devicetable.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/mux/consumer.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#define MUX_SELECT_DELAY_US 100
|
||||
+
|
||||
+struct gpio_lmux {
|
||||
+ struct gpio_chip gc;
|
||||
+ struct mux_control *mux;
|
||||
+ struct gpio_desc *muxed_gpio;
|
||||
+
|
||||
+ u32 num_gpio_mux_states;
|
||||
+ unsigned int gpio_mux_states[] __counted_by(num_gpio_mux_states);
|
||||
+};
|
||||
+
|
||||
+static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
+{
|
||||
+ struct gpio_lmux *glm = gpiochip_get_data(gc);
|
||||
+ int ret;
|
||||
+
|
||||
+ if (offset > gc->ngpio)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset],
|
||||
+ MUX_SELECT_DELAY_US);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = gpiod_get_raw_value_cansleep(glm->muxed_gpio);
|
||||
+ mux_control_deselect(glm->mux);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int gpio_lmux_gpio_set(struct gpio_chip *gc, unsigned int offset,
|
||||
+ int value)
|
||||
+{
|
||||
+ return -EOPNOTSUPP;
|
||||
+}
|
||||
+
|
||||
+static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc,
|
||||
+ unsigned int offset)
|
||||
+{
|
||||
+ return GPIO_LINE_DIRECTION_IN;
|
||||
+}
|
||||
+
|
||||
+static int gpio_lmux_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct gpio_lmux *glm;
|
||||
+ unsigned int ngpio;
|
||||
+ size_t size;
|
||||
+ int ret;
|
||||
+
|
||||
+ ngpio = device_property_count_u32(dev, "gpio-line-mux-states");
|
||||
+ if (!ngpio)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ size = struct_size(glm, gpio_mux_states, ngpio);
|
||||
+ glm = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
+ if (!glm)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ glm->gc.base = -1;
|
||||
+ glm->gc.can_sleep = true;
|
||||
+ glm->gc.fwnode = dev_fwnode(dev);
|
||||
+ glm->gc.label = dev_name(dev);
|
||||
+ glm->gc.ngpio = ngpio;
|
||||
+ glm->gc.owner = THIS_MODULE;
|
||||
+ glm->gc.parent = dev;
|
||||
+
|
||||
+ glm->gc.get = gpio_lmux_gpio_get;
|
||||
+ glm->gc.set = gpio_lmux_gpio_set;
|
||||
+ glm->gc.get_direction = gpio_lmux_gpio_get_direction;
|
||||
+
|
||||
+ glm->mux = devm_mux_control_get(dev, NULL);
|
||||
+ if (IS_ERR(glm->mux))
|
||||
+ return dev_err_probe(dev, PTR_ERR(glm->mux),
|
||||
+ "could not get mux controller\n");
|
||||
+
|
||||
+ glm->muxed_gpio = devm_gpiod_get(dev, "muxed", GPIOD_IN);
|
||||
+ if (IS_ERR(glm->muxed_gpio))
|
||||
+ return dev_err_probe(dev, PTR_ERR(glm->muxed_gpio),
|
||||
+ "could not get muxed-gpio\n");
|
||||
+
|
||||
+ glm->num_gpio_mux_states = ngpio;
|
||||
+ ret = device_property_read_u32_array(dev, "gpio-line-mux-states",
|
||||
+ &glm->gpio_mux_states[0], ngpio);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "could not get mux states\n");
|
||||
+
|
||||
+ ret = devm_gpiochip_add_data(dev, &glm->gc, glm);
|
||||
+ if (ret)
|
||||
+ return dev_err_probe(dev, ret, "failed to add gpiochip\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id gpio_lmux_of_match[] = {
|
||||
+ { .compatible = "gpio-line-mux" },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, gpio_lmux_of_match);
|
||||
+
|
||||
+static struct platform_driver gpio_lmux_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "gpio-line-mux",
|
||||
+ .of_match_table = gpio_lmux_of_match,
|
||||
+ },
|
||||
+ .probe = gpio_lmux_probe,
|
||||
+};
|
||||
+module_platform_driver(gpio_lmux_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
|
||||
+MODULE_DESCRIPTION("GPIO line mux driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
From e034e058897a12bc856f8b22d1796964c742f732 Mon Sep 17 00:00:00 2001
|
||||
From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
|
||||
Date: Wed, 7 Jan 2026 09:58:33 +0100
|
||||
Subject: [PATCH] gpio: line-mux: remove bits already handled by GPIO core
|
||||
|
||||
GPIO core already handles checking the offset against the number of
|
||||
GPIOs as well as missing any of the GPIO chip callbacks. Remove the
|
||||
unnecessary bits.
|
||||
|
||||
Also, the offset check was off-by-one as reported by Dan.
|
||||
|
||||
Fixes: 2b03d9a40cd1 ("gpio: add gpio-line-mux driver")
|
||||
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
|
||||
Closes: https://lore.kernel.org/all/aV4b6GAGz1zyf8Xy@stanley.mountain/
|
||||
Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
Reviewed-by: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
Link: https://lore.kernel.org/r/20260107085833.17338-1-bartosz.golaszewski@oss.qualcomm.com
|
||||
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
|
||||
|
||||
--- a/drivers/gpio/gpio-line-mux.c
|
||||
+++ b/drivers/gpio/gpio-line-mux.c
|
||||
@@ -29,9 +29,6 @@ static int gpio_lmux_gpio_get(struct gpi
|
||||
struct gpio_lmux *glm = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
- if (offset > gc->ngpio)
|
||||
- return -EINVAL;
|
||||
-
|
||||
ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset],
|
||||
MUX_SELECT_DELAY_US);
|
||||
if (ret < 0)
|
||||
@@ -42,12 +39,6 @@ static int gpio_lmux_gpio_get(struct gpi
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int gpio_lmux_gpio_set(struct gpio_chip *gc, unsigned int offset,
|
||||
- int value)
|
||||
-{
|
||||
- return -EOPNOTSUPP;
|
||||
-}
|
||||
-
|
||||
static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc,
|
||||
unsigned int offset)
|
||||
{
|
||||
@@ -80,7 +71,6 @@ static int gpio_lmux_probe(struct platfo
|
||||
glm->gc.parent = dev;
|
||||
|
||||
glm->gc.get = gpio_lmux_gpio_get;
|
||||
- glm->gc.set = gpio_lmux_gpio_set;
|
||||
glm->gc.get_direction = gpio_lmux_gpio_get_direction;
|
||||
|
||||
glm->mux = devm_mux_control_get(dev, NULL);
|
||||
|
|
@ -2250,6 +2250,7 @@ CONFIG_GPIOLIB_FASTPATH_LIMIT=512
|
|||
# CONFIG_GPIO_IT87 is not set
|
||||
# CONFIG_GPIO_LATCH is not set
|
||||
# CONFIG_GPIO_LOGICVC is not set
|
||||
# CONFIG_GPIO_LINE_MUX is not set
|
||||
# CONFIG_GPIO_MAX3191X is not set
|
||||
# CONFIG_GPIO_MAX7300 is not set
|
||||
# CONFIG_GPIO_MAX7301 is not set
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue