diff --git a/target/linux/generic/backport-6.12/821-01-v7.0-dt-bindings-gpio-add-gpio-line-mux-controller.patch b/target/linux/generic/backport-6.12/821-01-v7.0-dt-bindings-gpio-add-gpio-line-mux-controller.patch new file mode 100644 index 0000000000..12e6fedd01 --- /dev/null +++ b/target/linux/generic/backport-6.12/821-01-v7.0-dt-bindings-gpio-add-gpio-line-mux-controller.patch @@ -0,0 +1,128 @@ +From 2a7618ba8698874e9871a8ec5453e0068e94d9e5 Mon Sep 17 00:00:00 2001 +From: Jonas Jelonek +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 +Reviewed-by: Conor Dooley +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20251227180134.1262138-2-jelonek.jonas@gmail.com +Signed-off-by: Bartosz Golaszewski + +--- /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 ++ ++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 ++ #include ++ ++ 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 = ; ++ }; ++ ++ 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>; ++ }; diff --git a/target/linux/generic/backport-6.12/821-02-v7.0-gpio-add-gpio-line-mux-driver.patch b/target/linux/generic/backport-6.12/821-02-v7.0-gpio-add-gpio-line-mux-driver.patch new file mode 100644 index 0000000000..3569615565 --- /dev/null +++ b/target/linux/generic/backport-6.12/821-02-v7.0-gpio-add-gpio-line-mux-driver.patch @@ -0,0 +1,209 @@ +From 2b03d9a40cd1fea42fd65d2b66df80edc0f374c8 Mon Sep 17 00:00:00 2001 +From: Jonas Jelonek +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 +Reviewed-by: Thomas Richard +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20251227180134.1262138-3-jelonek.jonas@gmail.com +Signed-off-by: Bartosz Golaszewski + +--- 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 ++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 + 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_DESCRIPTION("GPIO line mux driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.12/821-03-v7.0-gpio-line-mux-remove-bits-already-handled-by-GPIO-co.patch b/target/linux/generic/backport-6.12/821-03-v7.0-gpio-line-mux-remove-bits-already-handled-by-GPIO-co.patch new file mode 100644 index 0000000000..717421ea62 --- /dev/null +++ b/target/linux/generic/backport-6.12/821-03-v7.0-gpio-line-mux-remove-bits-already-handled-by-GPIO-co.patch @@ -0,0 +1,52 @@ +From e034e058897a12bc856f8b22d1796964c742f732 Mon Sep 17 00:00:00 2001 +From: Bartosz Golaszewski +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 +Closes: https://lore.kernel.org/all/aV4b6GAGz1zyf8Xy@stanley.mountain/ +Tested-by: Jonas Jelonek +Reviewed-by: Jonas Jelonek +Link: https://lore.kernel.org/r/20260107085833.17338-1-bartosz.golaszewski@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski + +--- 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); diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 index 78da675285..e7c9ba71d7 100644 --- a/target/linux/generic/config-6.12 +++ b/target/linux/generic/config-6.12 @@ -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