From b6376ccdcb4bf7163fc4cbe5f83e265bc1660d4f Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:09 +0200 Subject: [PATCH 1/9] phy: can-transceiver: rename temporary helper function to avoid conflict Rename the temporary devm_mux_state_get_optional function to avoid conflict with upcoming implementation in multiplexer subsystem. Signed-off-by: Josua Mayer Acked-by: Vinod Koul Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/phy/phy-can-transceiver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 330356706ad7..fcbca9d2bded 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -128,7 +128,7 @@ MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); /* Temporary wrapper until the multiplexer subsystem supports optional muxes */ static inline struct mux_state * -devm_mux_state_get_optional(struct device *dev, const char *mux_name) +can_transceiver_phy_mux_state_get_optional(struct device *dev, const char *mux_name) { if (!of_property_present(dev->of_node, "mux-states")) return NULL; @@ -183,7 +183,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->num_ch = num_ch; platform_set_drvdata(pdev, priv); - mux_state = devm_mux_state_get_optional(dev, NULL); + mux_state = can_transceiver_phy_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); From 80f3df6e254d860cc7a41d5f2ae37fe717644098 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:10 +0200 Subject: [PATCH 2/9] phy: renesas: rcar-gen3-usb2: rename local mux helper to avoid conflict Rename the temporary devm_mux_state_get_optional function to avoid conflict with upcoming implementation in multiplexer subsystem. Signed-off-by: Josua Mayer Reviewed-by: Wolfram Sang Acked-by: Vinod Koul Signed-off-by: Ulf Hansson --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index cfc2a8d9028d..1155b111420a 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -941,7 +941,7 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha /* Temporary wrapper until the multiplexer subsystem supports optional muxes */ static inline struct mux_state * -devm_mux_state_get_optional(struct device *dev, const char *mux_name) +rcar_gen3_phy_mux_state_get_optional(struct device *dev, const char *mux_name) { if (!of_property_present(dev->of_node, "mux-states")) return NULL; @@ -1036,7 +1036,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); } - mux_state = devm_mux_state_get_optional(dev, NULL); + mux_state = rcar_gen3_phy_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); if (mux_state) { From 993bcaf32c494014f56357f6cdd87fdfaa4e4d11 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:11 +0200 Subject: [PATCH 3/9] mux: Add helper functions for getting optional and selected mux-state In-tree phy-can-transceiver and phy_rcar_gen3_usb2 have already implemented local versions of devm_mux_state_get_optional. The omap-i2c driver gets and selects an optional mux in its probe function without using any helper. Add new helper functions covering both aforementioned use-cases: - mux_control_get_optional: Get a mux-control if specified in dt, return NULL otherwise. - devm_mux_state_get_optional: Get a mux-state if specified in dt, return NULL otherwise. - devm_mux_state_get_selected: Get and select a mux-state specified in dt, return error otherwise. - devm_mux_state_get_optional_selected: Get and select a mux-state if specified in dt, return error or NULL. Existing mux_get helper function is changed to take an extra argument indicating whether the mux is optional. In this case no error is printed, and NULL returned in case of ENOENT. Calling code is adapted to handle NULL return case, and to pass optional argument as required. To support automatic deselect for _selected helper, a new structure is created storing an exit pointer similar to clock core which is called on release. To facilitate code sharing between optional/mandatory/selected helpers, a new internal helper function is added to handle quiet (optional) and verbose (mandatory) errors, as well as storing the correct callback for devm release: __devm_mux_state_get Due to this structure devm_mux_state_get_*_selected can no longer print a useful error message when select fails. Instead callers should print errors where needed. Commit e153fdea9db04 ("phy: can-transceiver: Re-instate "mux-states" property presence check") noted that "mux_get() always prints an error message in case of an error, including when the property is not present, confusing the user." The first error message covers the case that a mux name is not matched in dt. The second error message is based on of_parse_phandle_with_args return value. In optional case no error is printed and NULL is returned. This ensures that the new helper functions will not confuse the user either. With the addition of optional helper functions it became clear that drivers should compile and link even if CONFIG_MULTIPLEXER was not enabled. Add stubs for all symbols exported by mux core. Acked-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/mux/core.c | 198 ++++++++++++++++++++++++++++++----- include/linux/mux/consumer.h | 108 ++++++++++++++++++- 2 files changed, 273 insertions(+), 33 deletions(-) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index f09ee8782e3d..23538de2c91b 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -46,6 +46,16 @@ static const struct class mux_class = { .name = "mux", }; +/** + * struct devm_mux_state_state - Tracks managed resources for mux-state objects. + * @mstate: Pointer to a mux state. + * @exit: An optional callback to execute before free. + */ +struct devm_mux_state_state { + struct mux_state *mstate; + int (*exit)(struct mux_state *mstate); +}; + static DEFINE_IDA(mux_ida); static int __init mux_init(void) @@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np) return dev ? to_mux_chip(dev) : NULL; } -/* +/** * mux_get() - Get the mux-control for a device. * @dev: The device that needs a mux-control. * @mux_name: The name identifying the mux-control. * @state: Pointer to where the requested state is returned, or NULL when * the required multiplexer states are handled by other means. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. * - * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno. + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. */ static struct mux_control *mux_get(struct device *dev, const char *mux_name, - unsigned int *state) + unsigned int *state, bool optional) { struct device_node *np = dev->of_node; struct of_phandle_args args; @@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, else index = of_property_match_string(np, "mux-control-names", mux_name); - if (index < 0) { + if (index < 0 && optional) { + return NULL; + } else if (index < 0) { dev_err(dev, "mux controller '%s' not found\n", mux_name); return ERR_PTR(index); @@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, "mux-controls", "#mux-control-cells", index, &args); if (ret) { + if (optional && ret == -ENOENT) + return NULL; + dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n", - np, state ? "state" : "control", mux_name ?: "", index); + np, state ? "state" : "control", + mux_name ?: "", index); return ERR_PTR(ret); } @@ -617,10 +635,29 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, */ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) { - return mux_get(dev, mux_name, NULL); + struct mux_control *mux = mux_get(dev, mux_name, NULL, false); + + if (!mux) + return ERR_PTR(-ENOENT); + + return mux; } EXPORT_SYMBOL_GPL(mux_control_get); +/** + * mux_control_get_optional() - Get the optional mux-control for a device. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + */ +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name) +{ + return mux_get(dev, mux_name, NULL, true); +} +EXPORT_SYMBOL_GPL(mux_control_get_optional); + /** * mux_control_put() - Put away the mux-control for good. * @mux: The mux-control to put away. @@ -670,14 +707,16 @@ struct mux_control *devm_mux_control_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_mux_control_get); -/* +/** * mux_state_get() - Get the mux-state for a device. * @dev: The device that needs a mux-state. * @mux_name: The name identifying the mux-state. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. * - * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno. + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. */ -static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) +static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional) { struct mux_state *mstate; @@ -685,12 +724,15 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) if (!mstate) return ERR_PTR(-ENOMEM); - mstate->mux = mux_get(dev, mux_name, &mstate->state); + mstate->mux = mux_get(dev, mux_name, &mstate->state, optional); if (IS_ERR(mstate->mux)) { int err = PTR_ERR(mstate->mux); kfree(mstate); return ERR_PTR(err); + } else if (!mstate->mux) { + kfree(mstate); + return optional ? NULL : ERR_PTR(-ENOENT); } return mstate; @@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate) static void devm_mux_state_release(struct device *dev, void *res) { - struct mux_state *mstate = *(struct mux_state **)res; + struct devm_mux_state_state *devm_state = res; + if (devm_state->exit) + devm_state->exit(devm_state->mstate); + + mux_state_put(devm_state->mstate); +} + +/** + * __devm_mux_state_get() - Get the optional mux-state for a device, + * with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. + * @init: Optional function pointer for mux-state object initialisation. + * @exit: Optional function pointer for mux-state object cleanup on release. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. + */ +static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name, + bool optional, + int (*init)(struct mux_state *mstate), + int (*exit)(struct mux_state *mstate)) +{ + struct devm_mux_state_state *devm_state; + struct mux_state *mstate; + int ret; + + mstate = mux_state_get(dev, mux_name, optional); + if (IS_ERR(mstate)) + return ERR_CAST(mstate); + else if (optional && !mstate) + return NULL; + else if (!mstate) + return ERR_PTR(-ENOENT); + + devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL); + if (!devm_state) { + ret = -ENOMEM; + goto err_devres_alloc; + } + + if (init) { + ret = init(mstate); + if (ret) + goto err_mux_state_init; + } + + devm_state->mstate = mstate; + devm_state->exit = exit; + devres_add(dev, devm_state); + + return mstate; + +err_mux_state_init: + devres_free(devm_state); +err_devres_alloc: mux_state_put(mstate); + return ERR_PTR(ret); } /** @@ -722,29 +821,70 @@ static void devm_mux_state_release(struct device *dev, void *res) * @mux_name: The name identifying the mux-control. * * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + * + * The mux-state will automatically be freed on release. */ -struct mux_state *devm_mux_state_get(struct device *dev, - const char *mux_name) +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) { - struct mux_state **ptr, *mstate; - - ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - mstate = mux_state_get(dev, mux_name); - if (IS_ERR(mstate)) { - devres_free(ptr); - return mstate; - } - - *ptr = mstate; - devres_add(dev, ptr); - - return mstate; + return __devm_mux_state_get(dev, mux_name, false, NULL, NULL); } EXPORT_SYMBOL_GPL(devm_mux_state_get); +/** + * devm_mux_state_get_optional() - Get the optional mux-state for a device, + * with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + * + * The mux-state will automatically be freed on release. + */ +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, true, NULL, NULL); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional); + +/** + * devm_mux_state_get_selected() - Get the mux-state for a device, with + * resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + * + * The returned mux-state (if valid) is already selected. + * + * The mux-state will automatically be deselected and freed on release. + */ +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_selected); + +/** + * devm_mux_state_get_optional_selected() - Get the optional mux-state for + * a device, with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + * + * The returned mux-state (if valid) is already selected. + * + * The mux-state will automatically be deselected and freed on release. + */ +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, + const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected); + /* * Using subsys_initcall instead of module_init here to try to ensure - for * the non-modular case - that the subsystem is initialized when mux consumers diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h index 2e25c838f831..a961861a503b 100644 --- a/include/linux/mux/consumer.h +++ b/include/linux/mux/consumer.h @@ -16,6 +16,8 @@ struct device; struct mux_control; struct mux_state; +#if IS_ENABLED(CONFIG_MULTIPLEXER) + unsigned int mux_control_states(struct mux_control *mux); int __must_check mux_control_select_delay(struct mux_control *mux, unsigned int state, @@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux); int mux_state_deselect(struct mux_state *mstate); struct mux_control *mux_control_get(struct device *dev, const char *mux_name); +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name); void mux_control_put(struct mux_control *mux); -struct mux_control *devm_mux_control_get(struct device *dev, - const char *mux_name); -struct mux_state *devm_mux_state_get(struct device *dev, - const char *mux_name); +struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name); + +#else + +static inline unsigned int mux_control_states(struct mux_control *mux) +{ + return 0; +} +static inline int __must_check mux_control_select_delay(struct mux_control *mux, + unsigned int state, unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_state_select_delay(struct mux_state *mstate, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_control_try_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_control_select(struct mux_control *mux, + unsigned int state) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_state_select(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_control_try_select(struct mux_control *mux, + unsigned int state) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_state_try_select(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline int mux_control_deselect(struct mux_control *mux) +{ + return -EOPNOTSUPP; +} +static inline int mux_state_deselect(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_control *mux_control_get_optional(struct device *dev, + const char *mux_name) +{ + return NULL; +} +static inline void mux_control_put(struct mux_control *mux) {} + +static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get_optional(struct device *dev, + const char *mux_name) +{ + return NULL; +} +static inline struct mux_state *devm_mux_state_get_selected(struct device *dev, + const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, + const char *mux_name) +{ + return NULL; +} + +#endif /* CONFIG_MULTIPLEXER */ #endif /* _LINUX_MUX_CONSUMER_H */ From d76e0c54ed8405469375177ddc7e4b863b59d0ae Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:12 +0200 Subject: [PATCH 4/9] phy: can-transceiver: drop temporary helper getting optional mux-state Multiplexer subsystem has now added helpers for getting managed optional mux-state. Switch to the new devm_mux_state_get_optional helper. This change is only compile-tested. Acked-by: Vinod Koul Reviewed-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/phy/phy-can-transceiver.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index fcbca9d2bded..2b52e47f247a 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = { }; MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); -/* Temporary wrapper until the multiplexer subsystem supports optional muxes */ -static inline struct mux_state * -can_transceiver_phy_mux_state_get_optional(struct device *dev, const char *mux_name) -{ - if (!of_property_present(dev->of_node, "mux-states")) - return NULL; - - return devm_mux_state_get(dev, mux_name); -} - static struct phy *can_transceiver_phy_xlate(struct device *dev, const struct of_phandle_args *args) { @@ -183,7 +173,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->num_ch = num_ch; platform_set_drvdata(pdev, priv); - mux_state = can_transceiver_phy_mux_state_get_optional(dev, NULL); + mux_state = devm_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); From 602236a78291c0ead8667be2c7a9199d2c290479 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:13 +0200 Subject: [PATCH 5/9] phy: renesas: rcar-gen3-usb2: drop helper getting optional mux-state Multiplexer subsystem has now added helpers for getting managed optional mux-state. Switch to the new devm_mux_state_get_optional_selected helper. This change is only compile-tested. Signed-off-by: Josua Mayer Acked-by: Vinod Koul Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 30 ++---------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 1155b111420a..79e820e2fe55 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable); } -/* Temporary wrapper until the multiplexer subsystem supports optional muxes */ -static inline struct mux_state * -rcar_gen3_phy_mux_state_get_optional(struct device *dev, const char *mux_name) -{ - if (!of_property_present(dev->of_node, "mux-states")) - return NULL; - - return devm_mux_state_get(dev, mux_name); -} - -static void rcar_gen3_phy_mux_state_deselect(void *data) -{ - mux_state_deselect(data); -} - static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); } - mux_state = rcar_gen3_phy_mux_state_get_optional(dev, NULL); + mux_state = devm_mux_state_get_optional_selected(dev, NULL); if (IS_ERR(mux_state)) - return PTR_ERR(mux_state); - if (mux_state) { - ret = mux_state_select(mux_state); - if (ret) - return dev_err_probe(dev, ret, "Failed to select USB mux\n"); - - ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect, - mux_state); - if (ret) - return dev_err_probe(dev, ret, - "Failed to register USB mux state deselect\n"); - } + return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n"); if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) { ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel); From 43c00f2bcff7c6e235f0c1b1eb18a6db0aa23815 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:14 +0200 Subject: [PATCH 6/9] i2c: omap: switch to new generic helper for getting selected mux-state Multiplexer subsystem has added generic helper functions for getting an already selected mux-state object. Replace existing logic in probe with the equivalent helper function. There is a functional difference in that the mux is now automatically deselected on release, replacing the explicit mux_state_deselect call. This change is only compile-tested. Reviewed-by: Geert Uytterhoeven Reviewed-by: Andreas Kemnade Reviewed-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/i2c/busses/i2c-omap.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index d9f590f0c384..f02d294db42a 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev) (1000 * omap->speed / 8); } - if (of_property_present(node, "mux-states")) { - struct mux_state *mux_state; - - mux_state = devm_mux_state_get(&pdev->dev, NULL); - if (IS_ERR(mux_state)) { - r = PTR_ERR(mux_state); - dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r); - goto err_put_pm; - } - omap->mux_state = mux_state; - r = mux_state_select(omap->mux_state); - if (r) { - dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r); - goto err_put_pm; - } + omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(omap->mux_state)) { + r = PTR_ERR(omap->mux_state); + goto err_put_pm; } /* reset ASAP, clearing any IRQs */ r = omap_i2c_init(omap); if (r) - goto err_mux_state_deselect; + goto err_put_pm; if (omap->rev < OMAP_I2C_OMAP1_REV_2) r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr, @@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev) err_unuse_clocks: omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); -err_mux_state_deselect: - if (omap->mux_state) - mux_state_deselect(omap->mux_state); err_put_pm: pm_runtime_put_sync(omap->dev); err_disable_pm: From bc0d3adf2f47439e8b7ffd228154d71b8acb50e6 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:15 +0200 Subject: [PATCH 7/9] dt-bindings: mmc: renesas,sdhi: Add mux-states property Add mux controller support for data or control lines that are muxed between a host and multiple cards. There are several devices supporting a choice of eMMC or SD on a single board by both dip switch and gpio, e.g. Renesas RZ/G2L SMARC SoM and SolidRun RZ/G2L SoM. In-tree dts for the Renesas boards currently rely on preprocessor macros and gpio hogs to describe the respective cards. By adding mux-states property to sdhi controller description, boards can correctly describe the mux that already exists in hardware - and drivers can coordinate between mux selection and probing for cards. Acked-by: Rob Herring (Arm) Reviewed-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index c754ea71f51f..64fac0d11329 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -106,6 +106,11 @@ properties: iommus: maxItems: 1 + mux-states: + description: + mux controller node to route the SD/SDIO/eMMC signals from SoC to cards. + maxItems: 1 + power-domains: maxItems: 1 @@ -275,6 +280,7 @@ examples: max-frequency = <195000000>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; resets = <&cpg 314>; + mux-states = <&mux 0>; }; sdhi1: mmc@ee120000 { From ce5c7c17e70640fc5635fd2252d0bdf4664d452b Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:16 +0200 Subject: [PATCH 8/9] mux: add visible config symbol to enable multiplexer subsystem The multiplexer subsystem was initially designed to be completely hidden, relying on consumers to "select MULTIPLEXER" explicitly. Drivers implementing multiplexers depend on this hidden symbol. This prevents users from manually enabling both the mux core and any of the multiplexer drivers. All multiplexer drivers in drivers/mux/ can operate standalone without a consumer. This is particularly useful in a device-tree, where a default state can be set through the idle-state property. Over time, several drivers have added "select MULTIPLEXER" dependencies, some of which require a mux and some consider it optional. v7.0-rc1 shows 15 such occurrences in Kconfig files, in a variety of subsystems. The natural step forward to allow enabling mux core and drivers would be adding a prompt and help text to the existing symbol. This violates the general Kbuild advice to avoid selecting visible symbols for all existing consumers of the mux core. Add the new config symbol MUX_CORE with a prompt and help text as a wrapper for users to enable manually. This avoids existing consumers automatically selecting a visible symbol. Change the MULTIPLEXER symbol from tristate to bool. This avoids complex dependencies if users were to attempt a configuration where the mux is a module but one of its consumers is built-in, as well as difficulties keeping the state of visible and invisible symbols in sync. Further convert the "menu ... depends on ..." structure to "if ... menu ... endmenu endif". These are functionally equivalent, but the new structure is more efficient and can support future source statements within the conditional block. Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/mux/Kconfig | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index c68132e38138..6d17dfa25dad 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -4,10 +4,21 @@ # config MULTIPLEXER - tristate + bool + +config MUX_CORE + bool "Generic Multiplexer Support" + select MULTIPLEXER + help + This framework is designed to abstract multiplexer handling for + devices via various GPIO-, MMIO/Regmap or specific multiplexer + controller chips. + + If unsure, say no. + +if MULTIPLEXER menu "Multiplexer drivers" - depends on MULTIPLEXER config MUX_ADG792A tristate "Analog Devices ADG792A/ADG792G Multiplexers" @@ -60,3 +71,5 @@ config MUX_MMIO be called mux-mmio. endmenu + +endif # MULTIPLEXER From 3c080bb2639d7b17bf83ca561f60252774a77b37 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:17 +0200 Subject: [PATCH 9/9] mmc: host: renesas_sdhi_core: support selecting an optional mux Some hardware designs route data or control signals through a mux to support multiple devices on a single sdhi controller. In particular SolidRun RZ/G2L/G2LC/V2L System on Module use a mux for switching between soldered eMMC and an optional microSD on a carrier board, e.g. for development or provisioning. SD/SDIO/eMMC are not well suited for runtime switching between different cards, however boot-time selection is possible and useful - in particular considering dt overlays. Add support for an optional SD/SDIO/eMMC mux defined in dt, and select it during probe. Similar functionality already exists in other places, e.g. i2c-omap. Signed-off-by: Josua Mayer Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 2a310a145785..f9ec78d699f4 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, struct regulator_dev *rdev; struct renesas_sdhi_dma *dma_priv; struct device *dev = &pdev->dev; + struct mux_state *mux_state; struct tmio_mmc_host *host; struct renesas_sdhi *priv; int num_irqs, irq, ret, i; @@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, "state_uhs"); } + mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(mux_state)) + return PTR_ERR(mux_state); + host = tmio_mmc_host_alloc(pdev, mmc_data); if (IS_ERR(host)) return PTR_ERR(host);