* [PATCH v3 0/2] mux: gpio: Add optional enable gpio
@ 2026-09-04 6:17 Tapio Reijonen
2026-09-04 6:17 ` [PATCH v3 1/2] dt-bindings: mux: gpio-mux: Add enable-gpios Tapio Reijonen
2026-09-04 6:17 ` [PATCH v3 2/2] mux: gpio: Add optional enable gpio Tapio Reijonen
0 siblings, 2 replies; 4+ messages in thread
From: Tapio Reijonen @ 2026-09-04 6:17 UTC (permalink / raw)
To: Peter Rosin, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Greg Kroah-Hartman, devicetree, linux-kernel, Tapio Reijonen
Analog multiplexers have an enable input that puts all channels in the
high-impedance off state independent of the address inputs - the E input
of a 74HC4051, for example. gpio-mux has no way to describe it, so it can
neither reach the idle state of disconnect that the mux-controller
binding defines, nor keep the mux disabled while the address gpios are
updated one gpio controller at a time.
Add enable-gpios for that input, and use it in the driver.
Without it the driver rejects MUX_IDLE_DISCONNECT outright.
mux-controller.yaml allows it - idle-state is an int32 with a minimum of
-2 - but mux_gpio_probe() treats every negative value other than
MUX_IDLE_AS_IS as invalid, so a device tree asking for the disconnect
idle state does not probe at all. On the board below that takes out the
i2c-mux behind the mux controller, both SFP i2c buses and both sfp
devices with it:
gpio-mux muxes:mux-controller-slot@0: invalid idle-state 4294967294
gpio-mux muxes:mux-controller-slot@0: probe with driver gpio-mux failed with error -22
4294967294 is MUX_IDLE_DISCONNECT, -2, printed through %u.
Tested on an i.MX6SX board where two SFP cages share one SMBus behind
DG9411 analog switches: the address input selects the cage and the
enable input gates the bus, both driven by a PCA9501 I2C gpio expander.
With enable-gpios and an idle state of disconnect, the parent bus scans
empty between transfers while each cage stays reachable through its own
child bus. A logic analyser on the enable, address and bus lines over a
full boot shows 97 enable windows and 82 address changes, with no bus
activity outside a window and no address change inside one. Dropping
enable-gpios while keeping the disconnect idle state makes the driver
refuse to probe, as intended.
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
---
Changes in v3:
- Address Peter Rosin's review of 2/2. Cosmetic, except for the last
item below.
- Say "Some analog multiplexers", not all of them, and note that the mux
gpios are not guaranteed to be updated atomically and that only
controllers implementing set_multiple() can possibly update theirs in
a single write.
- Drop the _gpio suffix from the struct member: enable_gpio -> enable.
- Reflow the comment in mux_gpio_set() and soften it to "might not be
updated atomically".
- Flatten the idle-state handling in probe, read the property into the
existing default when it is absent, and assign mux->idle_state once at
the end. Use dev_err() and return -EINVAL rather than dev_err_probe(),
as suggested. Written without the empty if branches of the suggested
snippet, but with the same behaviour.
- Beyond the review: return the status of the gpio writes from
mux_gpio_set() instead of zero. The other mux drivers pass the write
status back, and mux_control_set() only invalidates the cached state
when set() reports an error, so a failed write reported as success is
not retried. Happy to drop this hunk if it should be a separate patch.
- Link to v2: https://lore.kernel.org/r/20260831-add-external-mux-enable-gpio-v2-0-f6027a4afe61@vaisala.com
Changes in v2:
- Describe the enable input in terms of real multiplexers (the E input of
a 74HC4051) instead of repeating the binding text, and say what needs
it, as requested by Krzysztof.
- Add the missing gpio-mux: prefix to the binding patch subject.
- Limit enable-gpios to one entry, and require it for an idle state of
disconnect in both the schema and the driver: without an enable gpio
mux_gpio_set() drove the address gpios to the bit pattern of
MUX_IDLE_DISCONNECT instead of disconnecting.
- Deassert the enable gpio while the mux gpios are updated, not only for
the disconnect idle state, since they are not updated atomically.
- Show enable-gpios in the binding example.
- Link to v1: https://lore.kernel.org/r/20251105-add-external-mux-enable-gpio-v1-0-e59cba6f9e47@vaisala.com
---
Tapio Reijonen (2):
dt-bindings: mux: gpio-mux: Add enable-gpios
mux: gpio: Add optional enable gpio
.../devicetree/bindings/mux/gpio-mux.yaml | 22 +++++++++++
drivers/mux/gpio.c | 43 ++++++++++++++++++----
2 files changed, 57 insertions(+), 8 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20250925-add-external-mux-enable-gpio-c9ba5beddd49
Best regards,
--
Tapio Reijonen <tapio.reijonen@vaisala.com>
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v3 1/2] dt-bindings: mux: gpio-mux: Add enable-gpios
2026-09-04 6:17 [PATCH v3 0/2] mux: gpio: Add optional enable gpio Tapio Reijonen
@ 2026-09-04 6:17 ` Tapio Reijonen
2026-09-04 6:17 ` [PATCH v3 2/2] mux: gpio: Add optional enable gpio Tapio Reijonen
1 sibling, 0 replies; 4+ messages in thread
From: Tapio Reijonen @ 2026-09-04 6:17 UTC (permalink / raw)
To: Peter Rosin, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Greg Kroah-Hartman, devicetree, linux-kernel, Tapio Reijonen
Analog multiplexers have an enable input besides the address inputs. On a
74HC4051 it is the E input: with E high, all eight switches are in the
high-impedance off state, independent of S0 to S2. A 74HC4067 works the
same way with four address inputs.
Two things need that input. The mux-controller binding describes an idle
state of disconnect, which a gpio-mux cannot reach today because the
address inputs always select a channel. And the address inputs are plain
gpios that may live on different gpio controllers, so they are not
updated in one go and the mux passes through the intermediate addresses
on every channel change unless it is disabled meanwhile.
Add enable-gpios to describe the enable input, and require it for an idle
state of disconnect.
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
---
.../devicetree/bindings/mux/gpio-mux.yaml | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/Documentation/devicetree/bindings/mux/gpio-mux.yaml b/Documentation/devicetree/bindings/mux/gpio-mux.yaml
index ef7e33ec85d479ce57c5fe4a780f81a08a6ab6ba..2dd94704fd7bde238af4e84af87c2ad8e955eb14 100644
--- a/Documentation/devicetree/bindings/mux/gpio-mux.yaml
+++ b/Documentation/devicetree/bindings/mux/gpio-mux.yaml
@@ -29,6 +29,14 @@ properties:
description:
Regulator to power on the multiplexer.
+ enable-gpios:
+ maxItems: 1
+ description:
+ Enable input of the multiplexer, which disconnects all channels when
+ deasserted, independent of mux-gpios. Deasserted while the mux-gpios
+ are updated and for an idle state of disconnect, which cannot be
+ reached with the mux-gpios alone and thus requires this gpio.
+
'#mux-control-cells':
enum: [ 0, 1 ]
@@ -47,11 +55,23 @@ anyOf:
- required:
- "#mux-state-cells"
+allOf:
+ - if:
+ properties:
+ idle-state:
+ const: -2
+ required:
+ - idle-state
+ then:
+ required:
+ - enable-gpios
+
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/mux/mux.h>
mux: mux-controller {
compatible = "gpio-mux";
@@ -59,6 +79,8 @@ examples:
mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
<&pioA 1 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&pioA 2 GPIO_ACTIVE_LOW>;
+ idle-state = <MUX_IDLE_DISCONNECT>;
};
adc-mux {
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v3 2/2] mux: gpio: Add optional enable gpio
2026-09-04 6:17 [PATCH v3 0/2] mux: gpio: Add optional enable gpio Tapio Reijonen
2026-09-04 6:17 ` [PATCH v3 1/2] dt-bindings: mux: gpio-mux: Add enable-gpios Tapio Reijonen
@ 2026-09-04 6:17 ` Tapio Reijonen
2026-09-04 6:28 ` sashiko-bot
1 sibling, 1 reply; 4+ messages in thread
From: Tapio Reijonen @ 2026-09-04 6:17 UTC (permalink / raw)
To: Peter Rosin, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Greg Kroah-Hartman, devicetree, linux-kernel, Tapio Reijonen
Some analog multiplexers have an enable input that disconnects all
channels when deasserted, independent of the address inputs; on a
74HC4051 it is the E input. Add it as an optional gpio.
The mux gpios are not guaranteed to be updated atomically.
gpiod_multi_set_value_cansleep() groups them per gpio controller, and
only controllers implementing set_multiple() can possibly update theirs
in a single write, so a mux with its address inputs spread over two
controllers passes through the intermediate addresses on every change.
Deassert the enable gpio for the duration of the update and assert it
once the address is settled.
The enable gpio is also what makes an idle state of MUX_IDLE_DISCONNECT
possible, which the mux core applies when the chip is registered and after
every deselect: it leaves the enable gpio deasserted and the address
inputs alone. Refuse that idle state without an enable gpio, because the
address inputs cannot disconnect anything on their own and mux_gpio_set()
would instead drive them to the bit pattern of MUX_IDLE_DISCONNECT.
Return the status of the gpio writes instead of zero. For a gpio on an
i2c expander the write can fail, and mux_control_set() only invalidates
the cached state when set() reports an error, so a failure reported as
success would leave the mux on the previous channel with later selects
of the same state skipping the write entirely.
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
---
drivers/mux/gpio.c | 43 +++++++++++++++++++++++++++++++++++--------
1 file changed, 35 insertions(+), 8 deletions(-)
diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index f9c7863e51b82fb76ed03cf17308fae74716cf6d..2fd8c83dc22e10ceeb0b402f1a2fada9a3032319 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -18,6 +18,7 @@
struct mux_gpio {
struct gpio_descs *gpios;
+ struct gpio_desc *enable;
};
static int mux_gpio_set(struct mux_control *mux, int state)
@@ -25,12 +26,26 @@ static int mux_gpio_set(struct mux_control *mux, int state)
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
DECLARE_BITMAP(values, BITS_PER_TYPE(state));
u32 value = state;
+ int ret;
+
+ /*
+ * The gpios might not be updated atomically, disable the mux
+ * meanwhile.
+ */
+ ret = gpiod_set_value_cansleep(mux_gpio->enable, 0);
+ if (ret)
+ return ret;
+
+ if (state == MUX_IDLE_DISCONNECT)
+ return 0;
bitmap_from_arr32(values, &value, BITS_PER_TYPE(value));
- gpiod_multi_set_value_cansleep(mux_gpio->gpios, values);
+ ret = gpiod_multi_set_value_cansleep(mux_gpio->gpios, values);
+ if (ret)
+ return ret;
- return 0;
+ return gpiod_set_value_cansleep(mux_gpio->enable, 1);
}
static const struct mux_control_ops mux_gpio_ops = {
@@ -70,16 +85,28 @@ static int mux_gpio_probe(struct platform_device *pdev)
WARN_ON(pins != mux_gpio->gpios->ndescs);
mux_chip->mux->states = BIT(pins);
+ mux_gpio->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(mux_gpio->enable))
+ return dev_err_probe(dev, PTR_ERR(mux_gpio->enable),
+ "failed to get optional enable gpio\n");
+
ret = device_property_read_u32(dev, "idle-state", (u32 *)&idle_state);
- if (ret >= 0 && idle_state != MUX_IDLE_AS_IS) {
- if (idle_state < 0 || idle_state >= mux_chip->mux->states) {
- dev_err(dev, "invalid idle-state %u\n", idle_state);
- return -EINVAL;
- }
+ if (ret < 0)
+ idle_state = mux_chip->mux->idle_state;
- mux_chip->mux->idle_state = idle_state;
+ if (idle_state == MUX_IDLE_DISCONNECT && !mux_gpio->enable) {
+ dev_err(dev, "idle-state disconnect requires enable-gpios\n");
+ return -EINVAL;
}
+ if (idle_state != MUX_IDLE_AS_IS && idle_state != MUX_IDLE_DISCONNECT &&
+ (idle_state < 0 || idle_state >= mux_chip->mux->states)) {
+ dev_err(dev, "invalid idle-state %d\n", idle_state);
+ return -EINVAL;
+ }
+
+ mux_chip->mux->idle_state = idle_state;
+
ret = devm_regulator_get_enable_optional(dev, "mux");
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to get/enable mux supply\n");
--
2.47.3
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH v3 2/2] mux: gpio: Add optional enable gpio
2026-09-04 6:17 ` [PATCH v3 2/2] mux: gpio: Add optional enable gpio Tapio Reijonen
@ 2026-09-04 6:28 ` sashiko-bot
0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-09-04 6:28 UTC (permalink / raw)
To: Tapio Reijonen; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Out-of-bounds stack read in `mux_gpio_set` when the number of GPIOs specified in the device tree exceeds the bit width of `unsigned long`.
--
commit e5d22f785ccbb077940b83f814cc7d4237449e05
Author: Tapio Reijonen <tapio.reijonen@vaisala.com>
mux: gpio: Add optional enable gpio
Some analog multiplexers have an enable input that disconnects all
channels when deasserted, independent of the address inputs. This patch
adds support for an optional enable gpio to handle this. It also updates
the mux idle state logic to require this enable gpio when disconnecting.
> diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
> index f9c7863e51b82..2fd8c83dc22e1 100644
> --- a/drivers/mux/gpio.c
> +++ b/drivers/mux/gpio.c
[ ... ]
> @@ -25,12 +26,26 @@ static int mux_gpio_set(struct mux_control *mux, int state)
> struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
> DECLARE_BITMAP(values, BITS_PER_TYPE(state));
> u32 value = state;
> + int ret;
> +
> + /*
> + * The gpios might not be updated atomically, disable the mux
> + * meanwhile.
> + */
> + ret = gpiod_set_value_cansleep(mux_gpio->enable, 0);
> + if (ret)
> + return ret;
> +
> + if (state == MUX_IDLE_DISCONNECT)
> + return 0;
>
> bitmap_from_arr32(values, &value, BITS_PER_TYPE(value));
>
> - gpiod_multi_set_value_cansleep(mux_gpio->gpios, values);
> + ret = gpiod_multi_set_value_cansleep(mux_gpio->gpios, values);
[Severity: High]
This is a pre-existing issue, but is it possible for an out-of-bounds stack
read to occur here?
In mux_gpio_probe(), the number of pins is parsed from the device tree
using gpiod_count() which sets ndescs. There doesn't appear to be an upper
limit check on this value.
The values bitmap allocated earlier in mux_gpio_set():
DECLARE_BITMAP(values, BITS_PER_TYPE(state));
limits the capacity to a single unsigned long. If the number of GPIOs
specified in the device tree exceeds the bit width of state, it looks like
gpiod_multi_set_value_cansleep() will iterate past the bounds of the
allocated stack array when reading from values.
Could this lead to leaking adjacent kernel stack memory or triggering a
KASAN panic if an oversized number of pins is provided?
> + if (ret)
> + return ret;
>
> - return 0;
> + return gpiod_set_value_cansleep(mux_gpio->enable, 1);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-add-external-mux-enable-gpio-v3-0-1b868e669e43@vaisala.com?part=2
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-04 6:28 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 6:17 [PATCH v3 0/2] mux: gpio: Add optional enable gpio Tapio Reijonen
2026-09-04 6:17 ` [PATCH v3 1/2] dt-bindings: mux: gpio-mux: Add enable-gpios Tapio Reijonen
2026-09-04 6:17 ` [PATCH v3 2/2] mux: gpio: Add optional enable gpio Tapio Reijonen
2026-09-04 6:28 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox