[RFC net-next 3/4] i2c: mux: Propagate software nodes to channel adapters
Ahmad Byagowi <[email protected]> Sun, 2 Aug 2026 08:46:19 -0700
| Newsgroups | org.kernel.vger.linux-leds,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-i2c,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <3e76f1dc25b104a2c8558142c12e23b297f9c58c.1785684064.git.ahmadexp@gmail.com> |
Device Tree channel nodes are associated with the adapters created by i2c-mux, but equivalent software-node descriptions are not. Find the software-node child whose reg value matches the channel and assign it to the new adapter. Keep a reference for the adapter lifetime so downstream clients can find the channel by firmware node. Signed-off-by: Ahmad Byagowi <[email protected]> --- drivers/i2c/i2c-mux.c | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 681a201c2..1e587ea4e 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -25,6 +25,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/sysfs.h> @@ -264,6 +265,49 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = { .unlock_bus = i2c_parent_unlock_bus, }; +static struct fwnode_handle * +i2c_mux_get_channel_swnode(struct i2c_mux_core *muxc, u32 chan_id) +{ + struct fwnode_handle *dev_node = dev_fwnode(muxc->dev); + struct fwnode_handle *mux_node, *child = NULL; + u32 reg; + + if (!is_software_node(dev_node)) + return NULL; + + if (muxc->arbitrator) + mux_node = fwnode_get_named_child_node(dev_node, "i2c-arb"); + else if (muxc->gate) + mux_node = fwnode_get_named_child_node(dev_node, "i2c-gate"); + else + mux_node = fwnode_get_named_child_node(dev_node, "i2c-mux"); + + if (mux_node) { + /* A "reg" property indicates an old-style firmware entry. */ + if (!fwnode_property_read_u32(mux_node, "reg", ®)) { + fwnode_handle_put(mux_node); + mux_node = NULL; + } + } + + if (!mux_node) + mux_node = fwnode_handle_get(dev_node); + else if (muxc->arbitrator || muxc->gate) + child = fwnode_handle_get(mux_node); + + if (!child) { + fwnode_for_each_child_node(mux_node, child) { + if (fwnode_property_read_u32(child, "reg", ®)) + continue; + if (chan_id == reg) + break; + } + } + + fwnode_handle_put(mux_node); + return child; +} + int i2c_mux_add_adapter(struct i2c_mux_core *muxc, u32 force_nr, u32 chan_id) { @@ -364,6 +408,12 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, priv->adap.dev.of_node = child; of_node_put(mux_node); + } else { + struct fwnode_handle *fwnode; + + fwnode = i2c_mux_get_channel_swnode(muxc, chan_id); + if (fwnode) + device_set_node(&priv->adap.dev, fwnode); } /* @@ -408,6 +458,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, return 0; err_free_priv: + if (is_software_node(dev_fwnode(&priv->adap.dev))) + fwnode_handle_put(dev_fwnode(&priv->adap.dev)); kfree(priv); return ret; } @@ -420,6 +472,7 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) while (muxc->num_adapters) { struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; struct i2c_mux_priv *priv = adap->algo_data; + struct fwnode_handle *fwnode = dev_fwnode(&adap->dev); struct device_node *np = adap->dev.of_node; muxc->adapter[muxc->num_adapters] = NULL; @@ -430,6 +483,8 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); i2c_del_adapter(adap); + if (is_software_node(fwnode)) + fwnode_handle_put(fwnode); of_node_put(np); kfree(priv); } -- 2.50.1 (Apple Git-155)