* [PATCH v2 0/2] add gpio-line-mux
@ 2025-10-26 23:17 Jonas Jelonek
2025-10-26 23:17 ` [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller Jonas Jelonek
2025-10-26 23:17 ` [PATCH v2 2/2] gpio: add gpio-line-mux driver Jonas Jelonek
0 siblings, 2 replies; 12+ messages in thread
From: Jonas Jelonek @ 2025-10-26 23:17 UTC (permalink / raw)
To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Rosin,
Geert Uytterhoeven
Cc: linux-gpio, devicetree, linux-kernel, Jonas Jelonek
This proposes a new type of virtual GPIO controller and corresponding
driver to provide a 1-to-many mapping between virtual GPIOs and a single
real GPIO in combination with a multiplexer. Existing drivers apparently
do not serve the purpose for what I need.
I came across an issue with a switch device from Zyxel which has two
SFP+ cages. Most similar switches either wire up the SFP signals
(RX_LOS, MOD_ABS, TX_FAULT, TX_DISABLE) directly to the SoC (if it has
enough GPIOs) or two a GPIO expander (for which a driver usually
exists). However, Zyxel decided to do it differently in the following
way:
The signals RX_LOS, MOD_ABS and TX_FAULT share a single GPIO line to
the SoC. Which one is actually connected to that GPIO line at a time
is controlled by a separate multiplexer, a GPIO multiplexer in this
case (which uses two other GPIOs). Only the TX_DISABLE is separate.
The SFP core/driver doesn't seem to support such a usecase for now, for
each signal one needs to specify a separate GPIO like:
los-gpio = <&gpio0 0 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio0 1 GPIO_ACTIVE_LOW>;
...
But for my device, I actually need to directly specify multiplexing
behavior in the SFP node or provide a mux-controller with 'mux-control'.
To fill this gap, I created a dt-schema and a working driver which
exactly does what is needed. It takes a phandle to a mux-controller and
the 'shared' gpio, and provides several virtual GPIOs based on the
gpio-line-mux-states property.
This virtual gpio-controller can then be referenced in the '-gpio'
properties of the SFP node (or other nodes depending on the usecase) as
usual and do not require any modification to the SFP core/driver.
---
Changelog:
v2: - as requested by Linus:
- renamed from 'gpio-split' to 'gpio-line-mux'
- added better description and examples to DT bindings
- simplified driver
- added missing parts to DT bindings
- dropped RFC tag
- renamed patchset
Link to v1 (in case it isn't linked properly due to changed title):
https://lore.kernel.org/linux-gpio/20251009223501.570949-1-jelonek.jonas@gmail.com/
---
Jonas Jelonek (2):
dt-bindings: gpio: add gpio-line-mux controller
gpio: add gpio-line-mux driver
.../bindings/gpio/gpio-line-mux.yaml | 108 ++++++++++
.../devicetree/bindings/mux/gpio-mux.yaml | 30 +++
MAINTAINERS | 6 +
drivers/gpio/Kconfig | 10 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-line-mux.c | 194 ++++++++++++++++++
6 files changed, 349 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml
create mode 100644 drivers/gpio/gpio-line-mux.c
base-commit: 897396b418d1720aac39585b208aada708b5b433
--
2.48.1
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller 2025-10-26 23:17 [PATCH v2 0/2] add gpio-line-mux Jonas Jelonek @ 2025-10-26 23:17 ` Jonas Jelonek 2025-10-27 11:39 ` Rob Herring (Arm) 2025-10-27 13:32 ` Rob Herring 2025-10-26 23:17 ` [PATCH v2 2/2] gpio: add gpio-line-mux driver Jonas Jelonek 1 sibling, 2 replies; 12+ messages in thread From: Jonas Jelonek @ 2025-10-26 23:17 UTC (permalink / raw) To: Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Rosin, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel, Jonas Jelonek 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> --- .../bindings/gpio/gpio-line-mux.yaml | 108 ++++++++++++++++++ .../devicetree/bindings/mux/gpio-mux.yaml | 30 +++++ 2 files changed, 138 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml b/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml new file mode 100644 index 000000000000..4c907a35eb4d --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml @@ -0,0 +1,108 @@ +# 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 mapping backed by + a single shared GPIO and a multiplexer. A simple illustrated example is + + +----- A + IN/OUT / + <-----o------- B + / |\ + | | +----- C + | | \ + | | +--- D + | | + M1 M0 + + MUX CONTROL + + M1 M0 IN/OUT + 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/outputs + 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. + minItems: 1 + items: + type: string + + gpio-line-names: true + + mux-controls: + maxItems: 1 + $ref: /schemas/mux/mux-controller.yaml# + description: + Phandle to the multiplexer to control access to the GPIOs. + + ngpios: false + + shared-gpio: + 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 + - shared-gpio + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/mux/mux.h> + + sfp_gpio_mux: gpio-mux { + 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>; + shared-gpio = <&gpio0 2 GPIO_ACTIVE_HIGH>; + + gpio-line-names = "SFP1_LOS", "SFP1_MOD_ABS", "SFP1_TX_FAULT"; + gpio-line-mux-states = <0>, <1>, <3>; + }; + + sfp1: sfp-p1 { + compatible = "sff,sfp"; + + 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/Documentation/devicetree/bindings/mux/gpio-mux.yaml b/Documentation/devicetree/bindings/mux/gpio-mux.yaml index ef7e33ec85d4..57eb1e9ef4cf 100644 --- a/Documentation/devicetree/bindings/mux/gpio-mux.yaml +++ b/Documentation/devicetree/bindings/mux/gpio-mux.yaml @@ -100,4 +100,34 @@ examples: }; }; }; + - | + #include <dt-bindings/gpio/gpio.h> + + sfp_mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, + <&gpio0 1 GPIO_ACTIVE_HIGH>; + }; + + sfp_gpio: sfp-gpio-1 { + compatible = "gpio-line-mux"; + gpio-controller; + #gpio-cells = <2>; + + mux-controls = <&sfp_mux>; + shared-gpio = <&gpio0 2 GPIO_ACTIVE_HIGH>; + + gpio-line-names = "SFP_LOS", "SFP_MOD_ABS", "SFP_TX_F"; + gpio-line-mux-states = <0>, <1>, <2>; + }; + + sfp0: sfp-p0 { + compatible = "sff,sfp"; + + los-gpios = <&sfp_gpio 0 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&sfp_gpio 1 GPIO_ACTIVE_LOW>; + tx-fault-gpios = <&sfp_gpio 2 GPIO_ACTIVE_HIGH>; + }; ... -- 2.48.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller 2025-10-26 23:17 ` [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller Jonas Jelonek @ 2025-10-27 11:39 ` Rob Herring (Arm) 2025-10-27 11:47 ` Jonas Jelonek 2025-10-27 13:32 ` Rob Herring 1 sibling, 1 reply; 12+ messages in thread From: Rob Herring (Arm) @ 2025-10-27 11:39 UTC (permalink / raw) To: Jonas Jelonek Cc: Linus Walleij, Geert Uytterhoeven, Bartosz Golaszewski, Peter Rosin, Conor Dooley, devicetree, linux-kernel, Krzysztof Kozlowski, linux-gpio On Sun, 26 Oct 2025 23:17:53 +0000, Jonas Jelonek wrote: > 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> > --- > .../bindings/gpio/gpio-line-mux.yaml | 108 ++++++++++++++++++ > .../devicetree/bindings/mux/gpio-mux.yaml | 30 +++++ > 2 files changed, 138 insertions(+) > create mode 100644 Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml: gpio-line-mux-states: missing type definition /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: gpio-mux (gpio-mux): $nodename:0: 'gpio-mux' does not match '^mux-controller(@.*|-([0-9]|[1-9][0-9]+))?$' from schema $id: http://devicetree.org/schemas/mux/mux-controller.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:0: 0 is not of type 'string' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:1: 1 is not of type 'string' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:2: 3 is not of type 'string' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): mux-controls: [[1]] is not of type 'object' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-p1 (sff,sfp): 'i2c-bus' is a required property from schema $id: http://devicetree.org/schemas/net/sff,sfp.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:0: 0 is not of type 'string' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:1: 1 is not of type 'string' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:2: 2 is not of type 'string' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): mux-controls: [[2]] is not of type 'object' from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-p0 (sff,sfp): 'i2c-bus' is a required property from schema $id: http://devicetree.org/schemas/net/sff,sfp.yaml doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20251026231754.2368904-2-jelonek.jonas@gmail.com The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller 2025-10-27 11:39 ` Rob Herring (Arm) @ 2025-10-27 11:47 ` Jonas Jelonek 0 siblings, 0 replies; 12+ messages in thread From: Jonas Jelonek @ 2025-10-27 11:47 UTC (permalink / raw) To: Rob Herring (Arm) Cc: Linus Walleij, Geert Uytterhoeven, Bartosz Golaszewski, Peter Rosin, Conor Dooley, devicetree, linux-kernel, Krzysztof Kozlowski, linux-gpio On 27.10.25 12:39, Rob Herring (Arm) wrote: > My bot found errors running 'make dt_binding_check' on your patch: > > yamllint warnings/errors: > > dtschema/dtc warnings/errors: > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml: gpio-line-mux-states: missing type definition > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: gpio-mux (gpio-mux): $nodename:0: 'gpio-mux' does not match '^mux-controller(@.*|-([0-9]|[1-9][0-9]+))?$' > from schema $id: http://devicetree.org/schemas/mux/mux-controller.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:0: 0 is not of type 'string' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:1: 1 is not of type 'string' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:2: 3 is not of type 'string' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): mux-controls: [[1]] is not of type 'object' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/gpio/gpio-line-mux.example.dtb: sfp-p1 (sff,sfp): 'i2c-bus' is a required property > from schema $id: http://devicetree.org/schemas/net/sff,sfp.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:0: 0 is not of type 'string' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:1: 1 is not of type 'string' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): gpio-line-mux-states:2: 2 is not of type 'string' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-gpio-1 (gpio-line-mux): mux-controls: [[2]] is not of type 'object' > from schema $id: http://devicetree.org/schemas/gpio/gpio-line-mux.yaml > /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/mux/gpio-mux.example.dtb: sfp-p0 (sff,sfp): 'i2c-bus' is a required property > from schema $id: http://devicetree.org/schemas/net/sff,sfp.yaml > sorry for these silly errors, I'll fix them in the next iteration. Best, Jonas ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller 2025-10-26 23:17 ` [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller Jonas Jelonek 2025-10-27 11:39 ` Rob Herring (Arm) @ 2025-10-27 13:32 ` Rob Herring 1 sibling, 0 replies; 12+ messages in thread From: Rob Herring @ 2025-10-27 13:32 UTC (permalink / raw) To: Jonas Jelonek Cc: Linus Walleij, Bartosz Golaszewski, Krzysztof Kozlowski, Conor Dooley, Peter Rosin, Geert Uytterhoeven, linux-gpio, devicetree, linux-kernel On Sun, Oct 26, 2025 at 11:17:53PM +0000, Jonas Jelonek wrote: > 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> > --- > .../bindings/gpio/gpio-line-mux.yaml | 108 ++++++++++++++++++ > .../devicetree/bindings/mux/gpio-mux.yaml | 30 +++++ > 2 files changed, 138 insertions(+) > create mode 100644 Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml > > diff --git a/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml b/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml > new file mode 100644 > index 000000000000..4c907a35eb4d > --- /dev/null > +++ b/Documentation/devicetree/bindings/gpio/gpio-line-mux.yaml > @@ -0,0 +1,108 @@ > +# 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: You need '>' or '|' to preserve formatting here. > + A GPIO controller to provide virtual GPIOs for a 1-to-many mapping backed by > + a single shared GPIO and a multiplexer. A simple illustrated example is > + > + +----- A > + IN/OUT / > + <-----o------- B > + / |\ > + | | +----- C > + | | \ > + | | +--- D > + | | > + M1 M0 > + > + MUX CONTROL > + > + M1 M0 IN/OUT > + 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/outputs > + 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. > + minItems: 1 > + items: > + type: string > + > + gpio-line-names: true > + > + mux-controls: > + maxItems: 1 > + $ref: /schemas/mux/mux-controller.yaml# > + description: > + Phandle to the multiplexer to control access to the GPIOs. > + > + ngpios: false > + > + shared-gpio: '-gpio' is deprecated. Use '-gpios'. > + 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 > + - shared-gpio > + > +additionalProperties: false > + > +examples: > + - | > + #include <dt-bindings/gpio/gpio.h> > + #include <dt-bindings/mux/mux.h> > + > + sfp_gpio_mux: gpio-mux { > + 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>; > + shared-gpio = <&gpio0 2 GPIO_ACTIVE_HIGH>; > + > + gpio-line-names = "SFP1_LOS", "SFP1_MOD_ABS", "SFP1_TX_FAULT"; > + gpio-line-mux-states = <0>, <1>, <3>; > + }; > + > + sfp1: sfp-p1 { > + compatible = "sff,sfp"; > + > + 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/Documentation/devicetree/bindings/mux/gpio-mux.yaml b/Documentation/devicetree/bindings/mux/gpio-mux.yaml > index ef7e33ec85d4..57eb1e9ef4cf 100644 > --- a/Documentation/devicetree/bindings/mux/gpio-mux.yaml > +++ b/Documentation/devicetree/bindings/mux/gpio-mux.yaml > @@ -100,4 +100,34 @@ examples: > }; > }; > }; > + - | > + #include <dt-bindings/gpio/gpio.h> > + > + sfp_mux: mux-controller { > + compatible = "gpio-mux"; > + #mux-control-cells = <0>; > + > + mux-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, > + <&gpio0 1 GPIO_ACTIVE_HIGH>; > + }; > + > + sfp_gpio: sfp-gpio-1 { > + compatible = "gpio-line-mux"; > + gpio-controller; > + #gpio-cells = <2>; > + > + mux-controls = <&sfp_mux>; > + shared-gpio = <&gpio0 2 GPIO_ACTIVE_HIGH>; > + > + gpio-line-names = "SFP_LOS", "SFP_MOD_ABS", "SFP_TX_F"; > + gpio-line-mux-states = <0>, <1>, <2>; > + }; > + > + sfp0: sfp-p0 { > + compatible = "sff,sfp"; > + > + los-gpios = <&sfp_gpio 0 GPIO_ACTIVE_HIGH>; > + mod-def0-gpios = <&sfp_gpio 1 GPIO_ACTIVE_LOW>; > + tx-fault-gpios = <&sfp_gpio 2 GPIO_ACTIVE_HIGH>; Drop. We don't need the same example twice. Rob ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-26 23:17 [PATCH v2 0/2] add gpio-line-mux Jonas Jelonek 2025-10-26 23:17 ` [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller Jonas Jelonek @ 2025-10-26 23:17 ` Jonas Jelonek 2025-10-27 7:51 ` Peter Rosin 2025-10-28 9:45 ` Thomas Richard 1 sibling, 2 replies; 12+ messages in thread From: Jonas Jelonek @ 2025-10-26 23:17 UTC (permalink / raw) To: Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Rosin, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel, Jonas Jelonek 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. For now, this doesn't support advanced features like IRQs, just normal IN and OUT functionality of GPIOs. 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 to 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. Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> --- MAINTAINERS | 6 ++ drivers/gpio/Kconfig | 10 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-line-mux.c | 194 +++++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 drivers/gpio/gpio-line-mux.c diff --git a/MAINTAINERS b/MAINTAINERS index 46126ce2f968..4d75253fe451 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10647,6 +10647,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 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ce237398fa00..93695b86a955 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1986,6 +1986,16 @@ 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 GPIO_AGGREGATOR + 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 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ee260a0809d3..6caee52b0356 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o 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 diff --git a/drivers/gpio/gpio-line-mux.c b/drivers/gpio/gpio-line-mux.c new file mode 100644 index 000000000000..a367e8f585c6 --- /dev/null +++ b/drivers/gpio/gpio-line-mux.c @@ -0,0 +1,194 @@ +// 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/mux/driver.h> +#include <linux/platform_device.h> + +struct gpio_lmux { + struct gpio_chip gc; + struct mux_control *mux; + struct device *dev; + + struct mutex lock; + + struct gpio_desc *shared_gpio; + /* dynamically sized, must be last */ + unsigned int gpio_mux_states[]; +}; + +DEFINE_GUARD(gpio_lmux, struct gpio_lmux *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) + +static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); + int ret; + + if (offset > gc->ngpio) + return -EINVAL; + + guard(gpio_lmux)(glm); + + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); + if (ret < 0) + return ret; + + ret = gpiod_get_raw_value_cansleep(glm->shared_gpio); + mux_control_deselect(glm->mux); + return ret; +} + +static int gpio_lmux_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); + int ret; + + if (offset > gc->ngpio) + return -EINVAL; + + guard(gpio_lmux)(glm); + + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); + if (ret < 0) + return ret; + + gpiod_set_raw_value_cansleep(glm->shared_gpio, value); + mux_control_deselect(glm->mux); + return 0; +} + +static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); + + if (offset > gc->ngpio) + return -EINVAL; + + guard(gpio_lmux)(glm); + + return gpiod_get_direction(glm->shared_gpio); +} + +static int gpio_lmux_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); + + if (offset > gc->ngpio) + return -EINVAL; + + guard(gpio_lmux)(glm); + + return gpiod_direction_input(glm->shared_gpio); +} + +static int gpio_lmux_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); + + if (offset > gc->ngpio) + return -EINVAL; + + guard(gpio_lmux)(glm); + + return gpiod_direction_output_raw(glm->shared_gpio, value); +} + +static int gpio_lmux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_lmux *glm; + unsigned int ngpio, size; + int ret; + + ngpio = device_property_count_u32(dev, "gpio-line-mux-states"); + if (!ngpio) + return -EINVAL; + + size = sizeof(*glm) + (sizeof(unsigned int) * ngpio); + glm = devm_kzalloc(dev, size, GFP_KERNEL); + if (!glm) + return -ENOMEM; + + mutex_init(&glm->lock); + + glm->dev = dev; + glm->gc.base = -1; + glm->gc.can_sleep = true; + glm->gc.fwnode = dev_fwnode(dev); + glm->gc.label = "gpio-line-mux"; + 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->gc.direction_input = gpio_lmux_gpio_direction_input; + glm->gc.direction_output = gpio_lmux_gpio_direction_output; + + glm->mux = devm_mux_control_get(dev, NULL); + if (IS_ERR(glm->mux)) { + if (PTR_ERR(glm->mux) == -EPROBE_DEFER) { + dev_err(dev, "mux-controller not ready, deferring probe\n"); + return -EPROBE_DEFER; + } + + dev_err(dev, "could not get mux-controller\n"); + return PTR_ERR(glm->mux); + } + + glm->shared_gpio = devm_gpiod_get(dev, "shared", GPIOD_ASIS); + if (IS_ERR(glm->shared_gpio)) { + dev_err(dev, "could not get shared-gpio\n"); + return PTR_ERR(glm->shared_gpio); + } + + ret = device_property_read_u32_array(dev, "gpio-line-mux-states", + &glm->gpio_mux_states[0], ngpio); + if (ret) { + dev_err(dev, "could not get mux states\n"); + return ret; + } + + ret = devm_gpiochip_add_data(dev, &glm->gc, glm); + if (ret) { + dev_err(dev, "failed to add gpiochip: %d\n", ret); + return ret; + } + + dev_info(dev, "providing %u virtual GPIOs for real GPIO %u\n", ngpio, + desc_to_gpio(glm->shared_gpio)); + 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"); -- 2.48.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-26 23:17 ` [PATCH v2 2/2] gpio: add gpio-line-mux driver Jonas Jelonek @ 2025-10-27 7:51 ` Peter Rosin 2025-10-27 8:26 ` Jonas Jelonek 2025-10-28 9:45 ` Thomas Richard 1 sibling, 1 reply; 12+ messages in thread From: Peter Rosin @ 2025-10-27 7:51 UTC (permalink / raw) To: Jonas Jelonek, Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel Hi! 2025-10-27 at 00:17, Jonas Jelonek wrote: > 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. > > For now, this doesn't support advanced features like IRQs, just normal > IN and OUT functionality of GPIOs. > > 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 to 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. > > Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> *snip* > +static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) > +{ > + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); > + int ret; > + > + if (offset > gc->ngpio) > + return -EINVAL; > + > + guard(gpio_lmux)(glm); > + > + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); Consider using mux_control_select_delay() here, with some suitable delay, to allow the mux to settle before reading the gpio line. > + if (ret < 0) > + return ret; > + > + ret = gpiod_get_raw_value_cansleep(glm->shared_gpio); > + mux_control_deselect(glm->mux); > + return ret; > +} > + > +static int gpio_lmux_gpio_set(struct gpio_chip *gc, unsigned int offset, > + int value) > +{ > + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); > + int ret; > + > + if (offset > gc->ngpio) > + return -EINVAL; > + > + guard(gpio_lmux)(glm); > + > + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); > + if (ret < 0) > + return ret; > + > + gpiod_set_raw_value_cansleep(glm->shared_gpio, value); > + mux_control_deselect(glm->mux); This .set implementation is completely broken. It you want to set a gpio to outout high/low, you presumably want the gpio to stay that way for at least some period of time, while whatever else happens that relies on the gpio to be in that state. But in this case only the mux select/deselect is protecting that gpio state, which is bound to be inadequate for anything real. Sure, you can probably build something trivial and see that the gpio can be manipulated, but the second something else touches the mux, the intended state of an output gpio line is (potentially) clobbered. I notice that in your target application, the sfp driver, all uses of gpios via the mux are inputs. Input is a much easier problem. At least as long as you do not require IRQ, if you need IRQs you face similar problems where the mux needs to be locked in its position for whatever period of time you can expect IRQs. TL;DR, this .set implementation needs to be removed, there is simply no reasonable way to implement a muxed gpio .set in a pure software driver. You need some hardware to preserve the state if/when the mux is manipulated. Cheers, Peter > + return 0; > +} ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-27 7:51 ` Peter Rosin @ 2025-10-27 8:26 ` Jonas Jelonek 0 siblings, 0 replies; 12+ messages in thread From: Jonas Jelonek @ 2025-10-27 8:26 UTC (permalink / raw) To: Peter Rosin, Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel Hi Peter, On 27.10.25 08:51, Peter Rosin wrote: >> +static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) >> +{ >> + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); >> + int ret; >> + >> + if (offset > gc->ngpio) >> + return -EINVAL; >> + >> + guard(gpio_lmux)(glm); >> + >> + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); > Consider using mux_control_select_delay() here, with some suitable > delay, to allow the mux to settle before reading the gpio line. Thanks, will fix this. > This .set implementation is completely broken. It you want to > set a gpio to outout high/low, you presumably want the gpio to > stay that way for at least some period of time, while whatever > else happens that relies on the gpio to be in that state. But in > this case only the mux select/deselect is protecting that gpio > state, which is bound to be inadequate for anything real. > > Sure, you can probably build something trivial and see that the > gpio can be manipulated, but the second something else touches > the mux, the intended state of an output gpio line is > (potentially) clobbered. > > I notice that in your target application, the sfp driver, all > uses of gpios via the mux are inputs. Input is a much easier > problem. At least as long as you do not require IRQ, if you > need IRQs you face similar problems where the mux needs to be > locked in its position for whatever period of time you can > expect IRQs. Yes, that why I left IRQs out here, but somehow I didn't properly transfer that I have similar issues with output functionality. > > TL;DR, this .set implementation needs to be removed, there is > simply no reasonable way to implement a muxed gpio .set in a > pure software driver. You need some hardware to preserve the > state if/when the mux is manipulated. I see, thanks for pointing it out! I'll drop this in the next iteration then and make this driver input-only. > Cheers, > Peter > Best, Jonas ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-26 23:17 ` [PATCH v2 2/2] gpio: add gpio-line-mux driver Jonas Jelonek 2025-10-27 7:51 ` Peter Rosin @ 2025-10-28 9:45 ` Thomas Richard 2025-10-28 10:09 ` Peter Rosin 2025-11-04 14:57 ` Jonas Jelonek 1 sibling, 2 replies; 12+ messages in thread From: Thomas Richard @ 2025-10-28 9:45 UTC (permalink / raw) To: Jonas Jelonek, Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Rosin, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel Hi Jonas, Linus mentioned gpio forwarder in the previous iteration, this caught my attention. So I had a look to your series. On 10/27/25 12:17 AM, Jonas Jelonek wrote: > 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. > > For now, this doesn't support advanced features like IRQs, just normal > IN and OUT functionality of GPIOs. > > 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 to 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. > > Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> > --- > MAINTAINERS | 6 ++ > drivers/gpio/Kconfig | 10 ++ > drivers/gpio/Makefile | 1 + > drivers/gpio/gpio-line-mux.c | 194 +++++++++++++++++++++++++++++++++++ > 4 files changed, 211 insertions(+) > create mode 100644 drivers/gpio/gpio-line-mux.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 46126ce2f968..4d75253fe451 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -10647,6 +10647,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 > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig > index ce237398fa00..93695b86a955 100644 > --- a/drivers/gpio/Kconfig > +++ b/drivers/gpio/Kconfig > @@ -1986,6 +1986,16 @@ 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 GPIO_AGGREGATOR You don't need GPIO_AGGREGATOR. > + 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 > diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile > index ee260a0809d3..6caee52b0356 100644 > --- a/drivers/gpio/Makefile > +++ b/drivers/gpio/Makefile > @@ -89,6 +89,7 @@ obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o > 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 > diff --git a/drivers/gpio/gpio-line-mux.c b/drivers/gpio/gpio-line-mux.c > new file mode 100644 > index 000000000000..a367e8f585c6 > --- /dev/null > +++ b/drivers/gpio/gpio-line-mux.c > @@ -0,0 +1,194 @@ > +// 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/mux/driver.h> > +#include <linux/platform_device.h> > + > +struct gpio_lmux { > + struct gpio_chip gc; > + struct mux_control *mux; > + struct device *dev; not used > + > + struct mutex lock; > + > + struct gpio_desc *shared_gpio; > + /* dynamically sized, must be last */ > + unsigned int gpio_mux_states[]; > +}; > + > +DEFINE_GUARD(gpio_lmux, struct gpio_lmux *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) > + > +static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) > +{ > + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); > + int ret; > + > + if (offset > gc->ngpio) > + return -EINVAL; > + > + guard(gpio_lmux)(glm); > + > + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); > + if (ret < 0) > + return ret; > + > + ret = gpiod_get_raw_value_cansleep(glm->shared_gpio); Why ignoring ACTIVE_LOW status ? And cansleep depends on your shared_gpio line, maybe it is not the case. > + mux_control_deselect(glm->mux); > + return ret; > +} > + [...] > + > +static int gpio_lmux_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct gpio_lmux *glm; > + unsigned int ngpio, size; > + int ret; nitpick: reverse xmas tree > + > + ngpio = device_property_count_u32(dev, "gpio-line-mux-states"); > + if (!ngpio) > + return -EINVAL; > + > + size = sizeof(*glm) + (sizeof(unsigned int) * ngpio); use struct_size() macro > + glm = devm_kzalloc(dev, size, GFP_KERNEL); > + if (!glm) > + return -ENOMEM; > + > + mutex_init(&glm->lock); > + > + glm->dev = dev; > + glm->gc.base = -1; > + glm->gc.can_sleep = true; depends on your shared_gpio line. Use gpiod_cansleep() like in the gpio-aggregator driver to know if your shared_gpio can sleep. > + glm->gc.fwnode = dev_fwnode(dev); > + glm->gc.label = "gpio-line-mux"; dev_name() ? > + 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->gc.direction_input = gpio_lmux_gpio_direction_input; > + glm->gc.direction_output = gpio_lmux_gpio_direction_output; > + > + glm->mux = devm_mux_control_get(dev, NULL); > + if (IS_ERR(glm->mux)) { > + if (PTR_ERR(glm->mux) == -EPROBE_DEFER) { > + dev_err(dev, "mux-controller not ready, deferring probe\n"); > + return -EPROBE_DEFER; > + } > + > + dev_err(dev, "could not get mux-controller\n"); > + return PTR_ERR(glm->mux); > + } You can replace the if statement by: if (IS_ERR(glm->mux)) return dev_err_probe(dev, PTR_ERR(glm->mux), "could not ..."); > + > + glm->shared_gpio = devm_gpiod_get(dev, "shared", GPIOD_ASIS); > + if (IS_ERR(glm->shared_gpio)) { > + dev_err(dev, "could not get shared-gpio\n"); > + return PTR_ERR(glm->shared_gpio); > + } ditto > + > + ret = device_property_read_u32_array(dev, "gpio-line-mux-states", > + &glm->gpio_mux_states[0], ngpio); > + if (ret) { > + dev_err(dev, "could not get mux states\n"); > + return ret; > + } ditto > + > + ret = devm_gpiochip_add_data(dev, &glm->gc, glm); > + if (ret) { > + dev_err(dev, "failed to add gpiochip: %d\n", ret); > + return ret; > + } ditto > + > + dev_info(dev, "providing %u virtual GPIOs for real GPIO %u\n", ngpio, > + desc_to_gpio(glm->shared_gpio)); No logs if device probes successfully > + 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"); The advantage of the forwarder is that it handles if the shared GPIO is sleeping or not. But I think the forwarder shall have ngpio, not 1. You will have to add ngpio times the same GPIO desc. Also unsupported operations shall be unset. So I don't really know if it shall be used in this case. Best Regards, Thomas ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-28 9:45 ` Thomas Richard @ 2025-10-28 10:09 ` Peter Rosin 2025-10-28 10:22 ` Thomas Richard 2025-11-04 14:57 ` Jonas Jelonek 1 sibling, 1 reply; 12+ messages in thread From: Peter Rosin @ 2025-10-28 10:09 UTC (permalink / raw) To: Thomas Richard, Jonas Jelonek, Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel Hi! 2025-10-28 at 10:45, Thomas Richard wrote: > On 10/27/25 12:17 AM, Jonas Jelonek wrote: >> + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); >> + if (ret < 0) >> + return ret; >> + >> + ret = gpiod_get_raw_value_cansleep(glm->shared_gpio); > > Why ignoring ACTIVE_LOW status ? > And cansleep depends on your shared_gpio line, maybe it is not the case. > >> + mux_control_deselect(glm->mux); *snip* >> + glm->gc.can_sleep = true; > > depends on your shared_gpio line. Does it? In this case, the gpio will always need to be able to sleep, since mux_control_select() may sleep. Or, what am I missing? > Use gpiod_cansleep() like in the > gpio-aggregator driver to know if your shared_gpio can sleep. Cheers, Peter ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-28 10:09 ` Peter Rosin @ 2025-10-28 10:22 ` Thomas Richard 0 siblings, 0 replies; 12+ messages in thread From: Thomas Richard @ 2025-10-28 10:22 UTC (permalink / raw) To: Peter Rosin, Jonas Jelonek, Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel Hi Peter, On 10/28/25 11:09 AM, Peter Rosin wrote: > Hi! > > 2025-10-28 at 10:45, Thomas Richard wrote: >> On 10/27/25 12:17 AM, Jonas Jelonek wrote: >>> + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); >>> + if (ret < 0) >>> + return ret; >>> + >>> + ret = gpiod_get_raw_value_cansleep(glm->shared_gpio); >> >> Why ignoring ACTIVE_LOW status ? >> And cansleep depends on your shared_gpio line, maybe it is not the case. >> >>> + mux_control_deselect(glm->mux); > > *snip* > >>> + glm->gc.can_sleep = true; >> >> depends on your shared_gpio line. > > Does it? In this case, the gpio will always need to be able to > sleep, since mux_control_select() may sleep. Or, what am I > missing? Oh yes you're right, I forgot the mux part. Best Regards, Thomas ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] gpio: add gpio-line-mux driver 2025-10-28 9:45 ` Thomas Richard 2025-10-28 10:09 ` Peter Rosin @ 2025-11-04 14:57 ` Jonas Jelonek 1 sibling, 0 replies; 12+ messages in thread From: Jonas Jelonek @ 2025-11-04 14:57 UTC (permalink / raw) To: Thomas Richard, Linus Walleij, Bartosz Golaszewski, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Rosin, Geert Uytterhoeven Cc: linux-gpio, devicetree, linux-kernel Hi Thomas, On 28.10.25 10:45, Thomas Richard wrote: > On 10/27/25 12:17 AM, Jonas Jelonek wrote: >> + >> + struct mutex lock; >> + >> + struct gpio_desc *shared_gpio; >> + /* dynamically sized, must be last */ >> + unsigned int gpio_mux_states[]; >> +}; >> + >> +DEFINE_GUARD(gpio_lmux, struct gpio_lmux *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) >> + >> +static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset) >> +{ >> + struct gpio_lmux *glm = (struct gpio_lmux *)gpiochip_get_data(gc); >> + int ret; >> + >> + if (offset > gc->ngpio) >> + return -EINVAL; >> + >> + guard(gpio_lmux)(glm); >> + >> + ret = mux_control_select(glm->mux, glm->gpio_mux_states[offset]); >> + if (ret < 0) >> + return ret; >> + >> + ret = gpiod_get_raw_value_cansleep(glm->shared_gpio); > Why ignoring ACTIVE_LOW status ? I think this would be rather error-prone and doesn't make sense to me. The consumer of this driver should decide about whether it uses ACTIVE_HIGH or ACTIVE_LOW for each one of the virtual GPIOs separately. This should then be applied as if this was a real GPIO. Following the ACTIVE_* that is given in the 'shared-gpio' property then would interfere again. > [...] > Thanks for all the suggested simplifications, I'll incorporate them. > The advantage of the forwarder is that it handles if the shared GPIO is > sleeping or not. > But I think the forwarder shall have ngpio, not 1. You will have to add > ngpio times the same GPIO desc. Also unsupported operations shall be unset. > So I don't really know if it shall be used in this case. I agree. As Peter mentioned, I need to use "can_sleep" anyway because of the mux. So there's not really an argument left to use the forwarder. > Best Regards, > > Thomas Best regards, Jonas ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-11-04 14:57 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-26 23:17 [PATCH v2 0/2] add gpio-line-mux Jonas Jelonek 2025-10-26 23:17 ` [PATCH v2 1/2] dt-bindings: gpio: add gpio-line-mux controller Jonas Jelonek 2025-10-27 11:39 ` Rob Herring (Arm) 2025-10-27 11:47 ` Jonas Jelonek 2025-10-27 13:32 ` Rob Herring 2025-10-26 23:17 ` [PATCH v2 2/2] gpio: add gpio-line-mux driver Jonas Jelonek 2025-10-27 7:51 ` Peter Rosin 2025-10-27 8:26 ` Jonas Jelonek 2025-10-28 9:45 ` Thomas Richard 2025-10-28 10:09 ` Peter Rosin 2025-10-28 10:22 ` Thomas Richard 2025-11-04 14:57 ` Jonas Jelonek
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).