linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] I2C Mux per channel bus speed
@ 2025-10-02 14:41 Marcus Folkesson
  2025-10-02 14:41 ` [PATCH v2 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-02 14:41 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson,
	Bartosz Golaszewski

This was a RFC on how to implement a feature to have different bus
speeds on different channels with an I2C multiplexer/switch.
As no major complaints on the design came up during the review, I
decided to submit the series without the RFC tag.

The benefit with this feature is that you may group devices after
the fastest bus speed they can handle.
A real-world example is that you could have e.g. a display running @400kHz
and a smart battery running @100kHz using the same I2C controller.

There are many corner cases where this may cause a problem for some
hardware topologies. I've tried to describe those I could think of
in the documentation, see Patch #5.

E.g. one risk is that if the mux driver does not disconnect channels
when Idle, this may cause a higher frequency to "leak" through to
devices that are supposed to run at lower bus speed.
This is not only a "problem" for changing bus speed but could also be
an issue for potential address conflicts.

The implementation is split up into several patches:

Patch #1 Introduce a callback for the i2c controller to set bus speed
Patch #2 Introduce functionality to adjust bus speed depending on mux
         channel.
Patch #3 Cleanup i2c-davinci driver a bit to prepare it for set_clk_freq
Parch #4 Implement set_clk_freq for the i2c-davinci driver
Parch #5 Update documentation with this feature

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
Changes in v2:
- Changed bus_freq field to bus_freq_hz in davinci_i2c_dev
- Removed idle_state from mux core
- Link to v1: https://lore.kernel.org/r/20250922-i2c-mux-v1-0-28c94a610930@gmail.com

---
Marcus Folkesson (5):
      i2c: core: add callback to change bus frequency
      i2c: mux: add support for per channel bus frequency
      i2c: davinci: calculate bus freq from Hz instead of kHz
      i2c: davinci: add support for setting bus frequency
      docs: i2c: i2c-topology: add section about bus speed

 Documentation/i2c/i2c-topology.rst | 176 +++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-davinci.c   |  41 ++++++---
 drivers/i2c/i2c-mux.c              | 115 +++++++++++++++++++++---
 include/linux/i2c.h                |  13 +++
 4 files changed, 323 insertions(+), 22 deletions(-)
---
base-commit: 22f20375f5b71f30c0d6896583b93b6e4bba7279
change-id: 20250913-i2c-mux-b0063de2ae4d

Best regards,
-- 
Marcus Folkesson <marcus.folkesson@gmail.com>



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/5] i2c: core: add callback to change bus frequency
  2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
@ 2025-10-02 14:41 ` Marcus Folkesson
  2025-10-02 14:41 ` [PATCH v2 2/5] i2c: mux: add support for per channel " Marcus Folkesson
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-02 14:41 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson

All devices on the same I2C bus share the same clock line and the bus
frequency has therefor be chosen so that all attached devices are able
to tolarate that clock rate. IOW, the bus speed must be set for the
slowest attached device.

With I2C multiplexers/switches on the other hand, it would be possible
to have different "domains" that runs with different speeds.

Prepare for such a feature by provide an optional callback function to
change bus frequency.

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
 include/linux/i2c.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 20fd41b51d5c85ee1665395c07345faafd8e2fca..d147e388dbab27966beb4dfbf869928b429e082c 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -742,6 +742,8 @@ struct i2c_adapter {
 	struct rt_mutex mux_lock;
 
 	int timeout;			/* in jiffies */
+	int clock_hz;
+	int (*set_clk_freq)(struct i2c_adapter *adap, u32 clock_hz); /* Optional */
 	int retries;
 	struct device dev;		/* the adapter device */
 	unsigned long locked_flags;	/* owned by the I2C core */
@@ -835,6 +837,17 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
 	adapter->lock_ops->unlock_bus(adapter, flags);
 }
 
+static inline int
+i2c_adapter_set_clk_freq(struct i2c_adapter *adapter, u32 clock_hz)
+{
+	int ret = -EOPNOTSUPP;
+
+	if (adapter->set_clk_freq)
+		ret = adapter->set_clk_freq(adapter, clock_hz);
+
+	return ret;
+}
+
 /**
  * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core
  * @adap: Adapter to mark as suspended

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/5] i2c: mux: add support for per channel bus frequency
  2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
  2025-10-02 14:41 ` [PATCH v2 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
@ 2025-10-02 14:41 ` Marcus Folkesson
  2025-10-03 10:57   ` Dan Carpenter
  2025-10-02 14:41 ` [PATCH v2 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-02 14:41 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson

There may be several reasons why you may need to use a certain speed
on an I2C bus. E.g.

- When several devices are attached to the bus, the speed must be
  selected according to the slowest device.

- Electrical conditions may limit the usuable speed on the bus for
  different reasons.

With an I2C multiplexer, it is possible to group the attached devices
after their preferred speed by e.g. put all "slow" devices on a separate
channel on the multiplexer.

Consider the following topology:

                      .----------. 100kHz .--------.
    .--------. 400kHz |          |--------| dev D1 |
    |  root  |--+-----| I2C MUX  |        '--------'
    '--------'  |     |          |--. 400kHz .--------.
                |     '----------'  '-------| dev D2 |
                |  .--------.               '--------'
                '--| dev D3 |
                   '--------'

One requirement with this design is that a multiplexer may only use the
same or lower bus speed as its parent.
Otherwise, if the multiplexer would have to increase the bus frequency,
then all siblings (D3 in this case) would run into a clock speed it may
not support.

The bus frequency for each channel is set in the devicetree. As the
i2c-mux bindings import the i2c-controller schema, the clock-frequency
property is already allowed.
If no clock-frequency property is set, the channel inherit their parent
bus speed.

The following example uses dt bindings to illustrate the topology above:

    i2c {
	clock-frequency = <400000>;

        i2c-mux {
            i2c@0 {
                clock-frequency = <100000>;

                D1 {
                    ...
                };
            };

            i2c@1 {
                D2 {
                  ...
                };
            };
        };

        D3 {
            ...
        }
    };

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
 drivers/i2c/i2c-mux.c | 115 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 103 insertions(+), 12 deletions(-)

diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 4d8690981a55dc0e1b35454971923791e6ed9f7f..5987853afa75cb58e1c6af6c7e7d52873ea53507 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -36,6 +36,72 @@ struct i2c_mux_priv {
 	u32 chan_id;
 };
 
+static int i2c_mux_select_chan(struct i2c_adapter *adap, u32 chan_id)
+{
+	struct i2c_mux_priv *priv = adap->algo_data;
+	struct i2c_mux_core *muxc = priv->muxc;
+	struct i2c_adapter *parent = muxc->parent;
+	struct i2c_adapter *root;
+	int ret;
+
+	if (priv->adap.clock_hz && priv->adap.clock_hz != parent->clock_hz) {
+		root = i2c_root_adapter(&adap->dev);
+
+		/* if we are parent-locked and the root adapter is our parent,
+		 * we already have the lock we need. Otherwise take the bus lock for the root
+		 * adaper before changing bus clock.
+		 */
+		if ((root != parent && !muxc->mux_locked) || muxc->mux_locked)
+			i2c_lock_bus(parent, I2C_LOCK_ROOT_ADAPTER);
+
+		ret = i2c_adapter_set_clk_freq(root, priv->adap.clock_hz);
+
+		if ((root != parent && !muxc->mux_locked) || muxc->mux_locked)
+			i2c_unlock_bus(parent, I2C_LOCK_ROOT_ADAPTER);
+
+		if (ret < 0) {
+			dev_err(&adap->dev,
+				"Failed to set clock frequency %dHz on root adapter %s: %d\n",
+				priv->adap.clock_hz, root->name, ret);
+
+			return ret;
+		}
+	}
+
+	return muxc->select(muxc, priv->chan_id);
+}
+
+static void i2c_mux_deselect_chan(struct i2c_adapter *adap, u32 chan_id)
+{
+	struct i2c_mux_priv *priv = adap->algo_data;
+	struct i2c_mux_core *muxc = priv->muxc;
+	struct i2c_adapter *parent = muxc->parent;
+	struct i2c_adapter *root;
+	int ret;
+
+	if (parent->clock_hz && parent->clock_hz != priv->adap.clock_hz) {
+		root = i2c_root_adapter(&parent->dev);
+
+		/* if we are parent-locked and the root adapter is our parent,
+		 * we already have the lock we need. Otherwise take the bus lock for the root
+		 * adaper before changing bus clock.
+		 */
+		if ((root != parent && !muxc->mux_locked) || muxc->mux_locked)
+			i2c_lock_bus(parent, I2C_LOCK_ROOT_ADAPTER);
+
+		ret = i2c_adapter_set_clk_freq(root, parent->clock_hz);
+
+		if ((root != parent && !muxc->mux_locked) || muxc->mux_locked)
+			i2c_unlock_bus(parent, I2C_LOCK_ROOT_ADAPTER);
+
+		if (ret < 0)
+			return;
+	}
+
+	if (muxc->deselect)
+		muxc->deselect(muxc, priv->chan_id);
+}
+
 static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
 				 struct i2c_msg msgs[], int num)
 {
@@ -46,11 +112,11 @@ static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
 
 	/* Switch to the right mux port and perform the transfer. */
 
-	ret = muxc->select(muxc, priv->chan_id);
+	ret = i2c_mux_select_chan(adap, priv->chan_id);
 	if (ret >= 0)
 		ret = __i2c_transfer(parent, msgs, num);
-	if (muxc->deselect)
-		muxc->deselect(muxc, priv->chan_id);
+
+	i2c_mux_deselect_chan(adap, priv->chan_id);
 
 	return ret;
 }
@@ -65,11 +131,11 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
 
 	/* Switch to the right mux port and perform the transfer. */
 
-	ret = muxc->select(muxc, priv->chan_id);
+	ret = i2c_mux_select_chan(adap, priv->chan_id);
 	if (ret >= 0)
 		ret = i2c_transfer(parent, msgs, num);
-	if (muxc->deselect)
-		muxc->deselect(muxc, priv->chan_id);
+
+	i2c_mux_deselect_chan(adap, priv->chan_id);
 
 	return ret;
 }
@@ -86,12 +152,12 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
 
 	/* Select the right mux port and perform the transfer. */
 
-	ret = muxc->select(muxc, priv->chan_id);
+	ret = i2c_mux_select_chan(adap, priv->chan_id);
 	if (ret >= 0)
 		ret = __i2c_smbus_xfer(parent, addr, flags,
 				       read_write, command, size, data);
-	if (muxc->deselect)
-		muxc->deselect(muxc, priv->chan_id);
+
+	i2c_mux_deselect_chan(adap, priv->chan_id);
 
 	return ret;
 }
@@ -108,12 +174,12 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
 
 	/* Select the right mux port and perform the transfer. */
 
-	ret = muxc->select(muxc, priv->chan_id);
+	ret = i2c_mux_select_chan(adap, priv->chan_id);
 	if (ret >= 0)
 		ret = i2c_smbus_xfer(parent, addr, flags,
 				     read_write, command, size, data);
-	if (muxc->deselect)
-		muxc->deselect(muxc, priv->chan_id);
+
+	i2c_mux_deselect_chan(adap, priv->chan_id);
 
 	return ret;
 }
@@ -365,6 +431,31 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
 			}
 		}
 
+		of_property_read_u32(child, "clock-frequency", &priv->adap.clock_hz);
+
+		/*
+		 * Warn if the mux adapter is not parent-locked as
+		 * this may cause issues for some hardware topologies.
+		 */
+		if ((priv->adap.clock_hz < parent->clock_hz) && muxc->mux_locked)
+			dev_warn(muxc->dev,
+				 "channel %u is slower than parent on a non parent-locked mux\n",
+				 chan_id);
+
+		/* If the mux adapter has no clock-frequency property, inherit from parent */
+		if (!priv->adap.clock_hz)
+			priv->adap.clock_hz = parent->clock_hz;
+
+		/* We don't support mux adapters faster than their parent */
+		if (priv->adap.clock_hz > parent->clock_hz) {
+			dev_err(muxc->dev,
+				"channel (%u) is faster (%u) than parent (%u)\n",
+				chan_id, priv->adap.clock_hz, parent->clock_hz);
+
+			of_node_put(mux_node);
+			goto err_free_priv;
+		}
+
 		priv->adap.dev.of_node = child;
 		of_node_put(mux_node);
 	}

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz
  2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
  2025-10-02 14:41 ` [PATCH v2 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
  2025-10-02 14:41 ` [PATCH v2 2/5] i2c: mux: add support for per channel " Marcus Folkesson
@ 2025-10-02 14:41 ` Marcus Folkesson
  2025-10-02 14:49   ` Bartosz Golaszewski
  2025-10-02 14:41 ` [PATCH v2 4/5] i2c: davinci: add support for setting bus frequency Marcus Folkesson
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-02 14:41 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson

The bus frequency is unnecessarily converted between Hz and kHz in
several places.
This is probably an old legacy from the old times (pre-devicetrees)
when the davinci_i2c_platform_data took the bus_freq in kHz.

Stick to Hz.

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
 drivers/i2c/busses/i2c-davinci.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 6a3d4e9e07f45ecc228943e877cde1fd9d72e8cb..82f295619c4d0ef108c57d13c10004aa25014cbf 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -117,8 +117,6 @@
 /* timeout for pm runtime autosuspend */
 #define DAVINCI_I2C_PM_TIMEOUT	1000	/* ms */
 
-#define DAVINCI_I2C_DEFAULT_BUS_FREQ	100
-
 struct davinci_i2c_dev {
 	struct device           *dev;
 	void __iomem		*base;
@@ -134,8 +132,8 @@ struct davinci_i2c_dev {
 #ifdef CONFIG_CPU_FREQ
 	struct notifier_block	freq_transition;
 #endif
-	/* standard bus frequency (kHz) */
-	unsigned int		bus_freq;
+	/* standard bus frequency */
+	unsigned int		bus_freq_hz;
 	/* Chip has a ICPFUNC register */
 	bool			has_pfunc;
 };
@@ -209,16 +207,16 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
 	if (device_is_compatible(dev->dev, "ti,keystone-i2c"))
 		d = 6;
 
-	clk = ((input_clock / (psc + 1)) / (dev->bus_freq * 1000));
+	clk = ((input_clock / (psc + 1)) / (dev->bus_freq_hz));
 	/* Avoid driving the bus too fast because of rounding errors above */
-	if (input_clock / (psc + 1) / clk > dev->bus_freq * 1000)
+	if (input_clock / (psc + 1) / clk > dev->bus_freq_hz)
 		clk++;
 	/*
 	 * According to I2C-BUS Spec 2.1, in FAST-MODE LOW period should be at
 	 * least 1.3uS, which is not the case with 50% duty cycle. Driving HIGH
 	 * to LOW ratio as 1 to 2 is more safe.
 	 */
-	if (dev->bus_freq > 100)
+	if (dev->bus_freq_hz > 100000)
 		clkl = (clk << 1) / 3;
 	else
 		clkl = (clk >> 1);
@@ -269,7 +267,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
 		davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKL_REG));
 	dev_dbg(dev->dev, "CLKH = %d\n",
 		davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKH_REG));
-	dev_dbg(dev->dev, "bus_freq = %dkHz\n", dev->bus_freq);
+	dev_dbg(dev->dev, "bus_freq_hz = %dHz\n", dev->bus_freq_hz);
 
 
 	/* Take the I2C module out of reset: */
@@ -761,9 +759,9 @@ static int davinci_i2c_probe(struct platform_device *pdev)
 
 	r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop);
 	if (r)
-		prop = DAVINCI_I2C_DEFAULT_BUS_FREQ;
+		prop = I2C_MAX_STANDARD_MODE_FREQ;
 
-	dev->bus_freq = prop / 1000;
+	dev->bus_freq_hz = prop;
 
 	dev->has_pfunc = device_property_present(&pdev->dev, "ti,has-pfunc");
 

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 4/5] i2c: davinci: add support for setting bus frequency
  2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
                   ` (2 preceding siblings ...)
  2025-10-02 14:41 ` [PATCH v2 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
@ 2025-10-02 14:41 ` Marcus Folkesson
  2025-10-02 14:41 ` [PATCH v2 5/5] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
  2025-10-14 14:13 ` [PATCH v2 0/5] I2C Mux per channel " Marcus Folkesson
  5 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-02 14:41 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson,
	Bartosz Golaszewski

Populate adapter with clock_hz and set_clk_freq to enable support for
dynamic bus frequency.

Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
 drivers/i2c/busses/i2c-davinci.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 82f295619c4d0ef108c57d13c10004aa25014cbf..fdde573eb59d11a7c6a87141863df1b9b58f64fd 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -279,6 +279,27 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
 	return 0;
 }
 
+static int davinci_i2c_set_clk(struct i2c_adapter *adap, u32 clock_hz)
+{
+	struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
+
+	if (dev->bus_freq_hz == clock_hz)
+		return 0;
+
+	dev->bus_freq_hz = clock_hz;
+
+	/* put I2C into reset */
+	davinci_i2c_reset_ctrl(dev, 0);
+
+	/* compute clock dividers */
+	i2c_davinci_calc_clk_dividers(dev);
+
+	/* Take the I2C module out of reset: */
+	davinci_i2c_reset_ctrl(dev, 1);
+
+	return 0;
+}
+
 /*
  * This routine does i2c bus recovery by using i2c_generic_scl_recovery
  * which is provided by I2C Bus recovery infrastructure.
@@ -810,6 +831,8 @@ static int davinci_i2c_probe(struct platform_device *pdev)
 	adap->dev.parent = &pdev->dev;
 	adap->timeout = DAVINCI_I2C_TIMEOUT;
 	adap->dev.of_node = dev_of_node(&pdev->dev);
+	adap->clock_hz = dev->bus_freq_hz;
+	adap->set_clk_freq = davinci_i2c_set_clk;
 
 	if (dev->has_pfunc)
 		adap->bus_recovery_info = &davinci_i2c_scl_recovery_info;

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 5/5] docs: i2c: i2c-topology: add section about bus speed
  2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
                   ` (3 preceding siblings ...)
  2025-10-02 14:41 ` [PATCH v2 4/5] i2c: davinci: add support for setting bus frequency Marcus Folkesson
@ 2025-10-02 14:41 ` Marcus Folkesson
  2025-10-14 14:13 ` [PATCH v2 0/5] I2C Mux per channel " Marcus Folkesson
  5 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-02 14:41 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson

Describe what needs to be consideraed and taken into account
when using different bus speeds for different mux channels.

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
 Documentation/i2c/i2c-topology.rst | 176 +++++++++++++++++++++++++++++++++++++
 1 file changed, 176 insertions(+)

diff --git a/Documentation/i2c/i2c-topology.rst b/Documentation/i2c/i2c-topology.rst
index 48fce0f7491bf1bcd4a851f685b010386c9ce0d2..2c4a1364ce82e353f51608e60d08535f80523fea 100644
--- a/Documentation/i2c/i2c-topology.rst
+++ b/Documentation/i2c/i2c-topology.rst
@@ -367,6 +367,182 @@ When D1 or D2 are accessed, accesses to D3 and D4 are locked out while
 accesses to D5 may interleave. When D3 or D4 are accessed, accesses to
 all other devices are locked out.
 
+Bus Speed and I2C Multiplexers
+================================
+
+I2C bus multiplexers allows multiple downstream channels to be exposed
+as separate I2C adapters which also could set their own bus speed.
+
+The multiplexer itself cannot change the bus speed as it use the upstream
+clock and data lines to communicate with the downstream devices. The speed
+is therfor changed in the root adapter resulting in that the whole bus is
+affected.
+
+This increases the complexity of the topology and some considerations must
+be taken into.
+
+Bus speed
+----------
+
+Downstream channels of an I2C multiplexer can only operate at the same or
+lower bus speed as the upstream bus. This is because the upstream bus may
+have devices that cannot operate at higher speeds and those will be affected
+by the speed change.
+
+The example below illustrates the problem.
+The root adapter is operating at 100kHz. D2 can only operate with 100kHz,
+but D2 can operate at 400kHz. When D1 is selected, the bus speed of the
+root adapter would have to be is set to 400kHz, a speed that D2 may not support.
+
+This topology is therefor not allowed: ::
+
+                          .----------. 400kHz .--------.
+        .--------. 100kHz |   mux-   |--------| dev D1 |
+        |  root  |--+-----|  locked  |        '--------'
+        '--------'  |     |  mux M1  |
+                    |     '----------'
+                    |  .--------.
+                    '--| dev D2 |
+                       '--------'
+
+
+This topology is allowed: ::
+
+                          .----------. 100kHz .--------.
+        .--------. 400kHz |   mux-   |--------| dev D2 |
+        |  root  |--+-----|  locked  |        '--------'
+        '--------'        |  mux M1  |--. 400kHz .--------.
+                          '----------'  '--------| dev D1 |
+                                                 '--------'
+
+Preferred topology
+-------------------
+
+The preferred topology when using different bus speeds is to have the multiplexer
+connected directly to the root adapter without any devices as siblings.
+By this arrangement, the bus speed can be changed without affecting any other devices
+and many of the caveats are avoided.
+
+Other multiplexers in parallell is still okay as those are locked out during transfers.
+
+This is the preferred topology: ::
+
+                          .----------. 100kHz .--------.
+        .--------. 400kHz |   mux-   |--------| dev D2 |
+        |  root  |--+-----|  locked  |        '--------'
+        '--------'        |  mux M1  |--. 400kHz .--------.
+                          '----------'  '--------| dev D1 |
+                                                 '--------'
+Locking
+--------
+
+If the multiplexer is mux-locked, transfers to D3 may interleave between the
+select-transfer-deselect to D1 or D2.
+This results in a situation where the bus speed to D3 may be lower than it
+is supposed to be. This is usually not a problem.
+
+This topology is allowed but some transfers to D3 may be at 100kHz: ::
+
+                          .----------. 100kHz .--------.
+        .--------. 400kHz |   mux-   |--------| dev D1 |
+        |  root  |--+-----|  locked  |        '--------'
+        '--------'  |     |  mux M1  |--. 400kHz .--------.
+                    |     '----------'  '--------| dev D2 |
+                    |  .--------.                '--------'
+                    '--| dev D3 |
+                       '--------'
+
+Multiple muxes in series
+--------------------------
+
+When multiple muxes are used in series the same rules applies.
+
+Transfers to D3 may interleave between select-transfer-deselect to D1, which
+results that the bus speed to D2 or D3 will be at 100KHz.
+
+Transfers to D2 may interleave between select-transfer-deselect to D1, which
+results in that the bus speed to D1 may be at 400kHz as the transfer to D2
+will set the bus speed to before the transfer to D1 starts.
+
+This is probably a bad topology ::
+
+                     .----------. 400kHz .----------. 100kHz .--------.
+    .--------.400kHz |   mux-   |--------|   mux-   |--------| dev D1 |
+    |  root  |--+----|  locked  | 400kHz |  locked  |        '--------'
+    '--------'  |    |  mux M1  |--.     |  mux M2  |
+                |    '----------'  |     '----------'
+                |  .--------.      |  .--------.
+                '--| dev D3 |      '--| dev D2 |
+                   '--------'         '--------'
+
+Multiple muxes in parallell
+----------------------------
+
+When multiple muxes are used in parallell all access to other muxes are locked out
+so this is not a problem.
+
+If the muxes are mux-locked, access to D3 may still interleave though.
+
+In the example below, D3 may not interleave between select-transfer-deselect for D1
+or D2 as both muxes are parent-locked: ::
+
+
+                   .----------. 100kHz   .--------.
+                   |  parent- |----------| dev D1 |
+                .--|  locked  |          '--------'
+                |  |  mux M1  |
+                |  '----------'
+                |      .----------. 400KHz  .--------.
+    .--------. 400kHz  |  parent- |---------| dev D2 |
+    |  root  |--+------|  locked  |         '--------'
+    '--------'  |      |  mux M2  |
+                |      '----------'
+                |  .--------.
+                '--| dev D3 |
+                   '--------'
+
+Idle state
+-----------
+
+Muxes have an idle state, which is the state the channels is put into when no channel
+is active. The state is typically one of the following:
+
+- All channels are disconnected
+- The last selected channel is left as-is
+- A predefined channel is selected
+
+Muxes that support an idle state where all channels are disconnected are preferred when using
+different bus speeds. Otherwise high bus speeds may "leak" through to devices that
+may not support that higher speed.
+
+Consider the following example: ::
+
+                          .----------. 100kHz .--------.
+        .--------. 400kHz |   mux-   |--------| dev D1 |
+        |  root  |--+-----|  locked  |        '--------'
+        '--------'  |     |  mux M1  |--. 400kHz .--------.
+                    |     '----------'  '--------| dev D2 |
+                    |  .--------.                '--------'
+                    '--| dev D3 |
+                       '--------'
+
+If the idle state of M1 is:
+- All channels disconnected: No problem, D1 and D2 are not affected by communication
+  to D3.
+- Last selected channel: Problem if D1 was the last selected channel. High speed
+  communication to D3 will be "leaked" to D1.
+- Predefined channel: Problem, if the predefined channel D1. Set predefined channel
+  to D2 as D2 may handle 400kHz.
+
+Supported controllers
+-----------------------
+
+Not all I2C controllers support setting the bus speed dynamically.
+At the time of writint, the following controllers has support:
+
+============================   =============================================
+i2c-davinci                    Supports dynamic bus speed
+============================   =============================================
 
 Mux type of existing device drivers
 ===================================

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz
  2025-10-02 14:41 ` [PATCH v2 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
@ 2025-10-02 14:49   ` Bartosz Golaszewski
  0 siblings, 0 replies; 9+ messages in thread
From: Bartosz Golaszewski @ 2025-10-02 14:49 UTC (permalink / raw)
  To: Marcus Folkesson
  Cc: Wolfram Sang, Peter Rosin, Michael Hennerich, Andi Shyti,
	linux-i2c, linux-kernel, linux-arm-kernel

On Thu, Oct 2, 2025 at 4:42 PM Marcus Folkesson
<marcus.folkesson@gmail.com> wrote:
>
> The bus frequency is unnecessarily converted between Hz and kHz in
> several places.
> This is probably an old legacy from the old times (pre-devicetrees)
> when the davinci_i2c_platform_data took the bus_freq in kHz.
>
> Stick to Hz.
>
> Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
> ---

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/5] i2c: mux: add support for per channel bus frequency
  2025-10-02 14:41 ` [PATCH v2 2/5] i2c: mux: add support for per channel " Marcus Folkesson
@ 2025-10-03 10:57   ` Dan Carpenter
  0 siblings, 0 replies; 9+ messages in thread
From: Dan Carpenter @ 2025-10-03 10:57 UTC (permalink / raw)
  To: oe-kbuild, Marcus Folkesson, Wolfram Sang, Peter Rosin,
	Michael Hennerich, Bartosz Golaszewski, Andi Shyti
  Cc: lkp, oe-kbuild-all, linux-i2c, linux-kernel, linux-arm-kernel,
	Marcus Folkesson

Hi Marcus,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Marcus-Folkesson/i2c-core-add-callback-to-change-bus-frequency/20251002-224414
base:   22f20375f5b71f30c0d6896583b93b6e4bba7279
patch link:    https://lore.kernel.org/r/20251002-i2c-mux-v2-2-b698564cd956%40gmail.com
patch subject: [PATCH v2 2/5] i2c: mux: add support for per channel bus frequency
config: i386-randconfig-141-20251003 (https://download.01.org/0day-ci/archive/20251003/202510031735.TE7FVj0R-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202510031735.TE7FVj0R-lkp@intel.com/

smatch warnings:
drivers/i2c/i2c-mux.c:506 i2c_mux_add_adapter() error: uninitialized symbol 'ret'.

vim +/ret +506 drivers/i2c/i2c-mux.c

a7ab72390b7706 Peter Rosin        2016-04-20  336  int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
fec1982d70721c Heiner Kallweit    2024-04-18  337  			u32 force_nr, u32 chan_id)
a7ab72390b7706 Peter Rosin        2016-04-20  338  {
a7ab72390b7706 Peter Rosin        2016-04-20  339  	struct i2c_adapter *parent = muxc->parent;
0826374bff5741 Michael Lawnick    2010-08-11  340  	struct i2c_mux_priv *priv;
c9449affad2ae0 Gerlando Falauto   2014-11-13  341  	char symlink_name[20];
0826374bff5741 Michael Lawnick    2010-08-11  342  	int ret;
0826374bff5741 Michael Lawnick    2010-08-11  343  
a7ab72390b7706 Peter Rosin        2016-04-20  344  	if (muxc->num_adapters >= muxc->max_adapters) {
a7ab72390b7706 Peter Rosin        2016-04-20  345  		dev_err(muxc->dev, "No room for more i2c-mux adapters\n");
a7ab72390b7706 Peter Rosin        2016-04-20  346  		return -EINVAL;
a7ab72390b7706 Peter Rosin        2016-04-20  347  	}
a7ab72390b7706 Peter Rosin        2016-04-20  348  
a7ab72390b7706 Peter Rosin        2016-04-20  349  	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0826374bff5741 Michael Lawnick    2010-08-11  350  	if (!priv)
a7ab72390b7706 Peter Rosin        2016-04-20  351  		return -ENOMEM;
0826374bff5741 Michael Lawnick    2010-08-11  352  
0826374bff5741 Michael Lawnick    2010-08-11  353  	/* Set up private adapter data */
a7ab72390b7706 Peter Rosin        2016-04-20  354  	priv->muxc = muxc;
0826374bff5741 Michael Lawnick    2010-08-11  355  	priv->chan_id = chan_id;
0826374bff5741 Michael Lawnick    2010-08-11  356  
0826374bff5741 Michael Lawnick    2010-08-11  357  	/* Need to do algo dynamically because we don't know ahead
0826374bff5741 Michael Lawnick    2010-08-11  358  	 * of time what sort of physical adapter we'll be dealing with.
0826374bff5741 Michael Lawnick    2010-08-11  359  	 */
6ef91fcca8a8ba Peter Rosin        2016-05-04  360  	if (parent->algo->master_xfer) {
6ef91fcca8a8ba Peter Rosin        2016-05-04  361  		if (muxc->mux_locked)
614b1c3cbfb0ec Wolfram Sang       2025-06-12  362  			priv->algo.xfer = i2c_mux_master_xfer;
6ef91fcca8a8ba Peter Rosin        2016-05-04  363  		else
614b1c3cbfb0ec Wolfram Sang       2025-06-12  364  			priv->algo.xfer = __i2c_mux_master_xfer;
6ef91fcca8a8ba Peter Rosin        2016-05-04  365  	}
7168bff2cfd710 Wolfram Sang       2019-04-03  366  	if (parent->algo->master_xfer_atomic)
614b1c3cbfb0ec Wolfram Sang       2025-06-12  367  		priv->algo.xfer_atomic = priv->algo.master_xfer;
7168bff2cfd710 Wolfram Sang       2019-04-03  368  
6ef91fcca8a8ba Peter Rosin        2016-05-04  369  	if (parent->algo->smbus_xfer) {
6ef91fcca8a8ba Peter Rosin        2016-05-04  370  		if (muxc->mux_locked)
0826374bff5741 Michael Lawnick    2010-08-11  371  			priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
6ef91fcca8a8ba Peter Rosin        2016-05-04  372  		else
6ef91fcca8a8ba Peter Rosin        2016-05-04  373  			priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
6ef91fcca8a8ba Peter Rosin        2016-05-04  374  	}
7168bff2cfd710 Wolfram Sang       2019-04-03  375  	if (parent->algo->smbus_xfer_atomic)
7168bff2cfd710 Wolfram Sang       2019-04-03  376  		priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer;
7168bff2cfd710 Wolfram Sang       2019-04-03  377  
0826374bff5741 Michael Lawnick    2010-08-11  378  	priv->algo.functionality = i2c_mux_functionality;
0826374bff5741 Michael Lawnick    2010-08-11  379  
0826374bff5741 Michael Lawnick    2010-08-11  380  	/* Now fill out new adapter structure */
0826374bff5741 Michael Lawnick    2010-08-11  381  	snprintf(priv->adap.name, sizeof(priv->adap.name),
0826374bff5741 Michael Lawnick    2010-08-11  382  		 "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id);
0826374bff5741 Michael Lawnick    2010-08-11  383  	priv->adap.owner = THIS_MODULE;
0826374bff5741 Michael Lawnick    2010-08-11  384  	priv->adap.algo = &priv->algo;
0826374bff5741 Michael Lawnick    2010-08-11  385  	priv->adap.algo_data = priv;
0826374bff5741 Michael Lawnick    2010-08-11  386  	priv->adap.dev.parent = &parent->dev;
2212a8529eb06c Elie De Brauwer    2013-12-09  387  	priv->adap.retries = parent->retries;
2212a8529eb06c Elie De Brauwer    2013-12-09  388  	priv->adap.timeout = parent->timeout;
dc362d50ba94ea Alexander Sverdlin 2015-06-12  389  	priv->adap.quirks = parent->quirks;
d1ed7985b9a6b8 Peter Rosin        2016-08-25  390  	if (muxc->mux_locked)
d1ed7985b9a6b8 Peter Rosin        2016-08-25  391  		priv->adap.lock_ops = &i2c_mux_lock_ops;
d1ed7985b9a6b8 Peter Rosin        2016-08-25  392  	else
d1ed7985b9a6b8 Peter Rosin        2016-08-25  393  		priv->adap.lock_ops = &i2c_parent_lock_ops;
0826374bff5741 Michael Lawnick    2010-08-11  394  
bc45449b144461 David Daney        2012-04-12  395  	/*
bc45449b144461 David Daney        2012-04-12  396  	 * Try to populate the mux adapter's of_node, expands to
bc45449b144461 David Daney        2012-04-12  397  	 * nothing if !CONFIG_OF.
bc45449b144461 David Daney        2012-04-12  398  	 */
a7ab72390b7706 Peter Rosin        2016-04-20  399  	if (muxc->dev->of_node) {
b2d57b56047b99 Peter Rosin        2016-07-09  400  		struct device_node *dev_node = muxc->dev->of_node;
b2d57b56047b99 Peter Rosin        2016-07-09  401  		struct device_node *mux_node, *child = NULL;
bc45449b144461 David Daney        2012-04-12  402  		u32 reg;
bc45449b144461 David Daney        2012-04-12  403  
b2d57b56047b99 Peter Rosin        2016-07-09  404  		if (muxc->arbitrator)
b2d57b56047b99 Peter Rosin        2016-07-09  405  			mux_node = of_get_child_by_name(dev_node, "i2c-arb");
b2d57b56047b99 Peter Rosin        2016-07-09  406  		else if (muxc->gate)
b2d57b56047b99 Peter Rosin        2016-07-09  407  			mux_node = of_get_child_by_name(dev_node, "i2c-gate");
b2d57b56047b99 Peter Rosin        2016-07-09  408  		else
b2d57b56047b99 Peter Rosin        2016-07-09  409  			mux_node = of_get_child_by_name(dev_node, "i2c-mux");
b2d57b56047b99 Peter Rosin        2016-07-09  410  
b2d57b56047b99 Peter Rosin        2016-07-09  411  		if (mux_node) {
b2d57b56047b99 Peter Rosin        2016-07-09  412  			/* A "reg" property indicates an old-style DT entry */
b2d57b56047b99 Peter Rosin        2016-07-09  413  			if (!of_property_read_u32(mux_node, "reg", &reg)) {
b2d57b56047b99 Peter Rosin        2016-07-09  414  				of_node_put(mux_node);
b2d57b56047b99 Peter Rosin        2016-07-09  415  				mux_node = NULL;
b2d57b56047b99 Peter Rosin        2016-07-09  416  			}
b2d57b56047b99 Peter Rosin        2016-07-09  417  		}
b2d57b56047b99 Peter Rosin        2016-07-09  418  
b2d57b56047b99 Peter Rosin        2016-07-09  419  		if (!mux_node)
b2d57b56047b99 Peter Rosin        2016-07-09  420  			mux_node = of_node_get(dev_node);
b2d57b56047b99 Peter Rosin        2016-07-09  421  		else if (muxc->arbitrator || muxc->gate)
b2d57b56047b99 Peter Rosin        2016-07-09  422  			child = of_node_get(mux_node);
b2d57b56047b99 Peter Rosin        2016-07-09  423  
b2d57b56047b99 Peter Rosin        2016-07-09  424  		if (!child) {
b2d57b56047b99 Peter Rosin        2016-07-09  425  			for_each_child_of_node(mux_node, child) {
bc45449b144461 David Daney        2012-04-12  426  				ret = of_property_read_u32(child, "reg", &reg);
bc45449b144461 David Daney        2012-04-12  427  				if (ret)
bc45449b144461 David Daney        2012-04-12  428  					continue;
b2d57b56047b99 Peter Rosin        2016-07-09  429  				if (chan_id == reg)
bc45449b144461 David Daney        2012-04-12  430  					break;
bc45449b144461 David Daney        2012-04-12  431  			}
64f0150a54ef60 Marcus Folkesson   2025-10-02  432  		}
64f0150a54ef60 Marcus Folkesson   2025-10-02  433  
64f0150a54ef60 Marcus Folkesson   2025-10-02  434  		of_property_read_u32(child, "clock-frequency", &priv->adap.clock_hz);
64f0150a54ef60 Marcus Folkesson   2025-10-02  435  
64f0150a54ef60 Marcus Folkesson   2025-10-02  436  		/*
64f0150a54ef60 Marcus Folkesson   2025-10-02  437  		 * Warn if the mux adapter is not parent-locked as
64f0150a54ef60 Marcus Folkesson   2025-10-02  438  		 * this may cause issues for some hardware topologies.
64f0150a54ef60 Marcus Folkesson   2025-10-02  439  		 */
64f0150a54ef60 Marcus Folkesson   2025-10-02  440  		if ((priv->adap.clock_hz < parent->clock_hz) && muxc->mux_locked)
64f0150a54ef60 Marcus Folkesson   2025-10-02  441  			dev_warn(muxc->dev,
64f0150a54ef60 Marcus Folkesson   2025-10-02  442  				 "channel %u is slower than parent on a non parent-locked mux\n",
64f0150a54ef60 Marcus Folkesson   2025-10-02  443  				 chan_id);
64f0150a54ef60 Marcus Folkesson   2025-10-02  444  
64f0150a54ef60 Marcus Folkesson   2025-10-02  445  		/* If the mux adapter has no clock-frequency property, inherit from parent */
64f0150a54ef60 Marcus Folkesson   2025-10-02  446  		if (!priv->adap.clock_hz)
64f0150a54ef60 Marcus Folkesson   2025-10-02  447  			priv->adap.clock_hz = parent->clock_hz;
64f0150a54ef60 Marcus Folkesson   2025-10-02  448  
64f0150a54ef60 Marcus Folkesson   2025-10-02  449  		/* We don't support mux adapters faster than their parent */
64f0150a54ef60 Marcus Folkesson   2025-10-02  450  		if (priv->adap.clock_hz > parent->clock_hz) {
64f0150a54ef60 Marcus Folkesson   2025-10-02  451  			dev_err(muxc->dev,
64f0150a54ef60 Marcus Folkesson   2025-10-02  452  				"channel (%u) is faster (%u) than parent (%u)\n",
64f0150a54ef60 Marcus Folkesson   2025-10-02  453  				chan_id, priv->adap.clock_hz, parent->clock_hz);
64f0150a54ef60 Marcus Folkesson   2025-10-02  454  
64f0150a54ef60 Marcus Folkesson   2025-10-02  455  			of_node_put(mux_node);
64f0150a54ef60 Marcus Folkesson   2025-10-02  456  			goto err_free_priv;

ret = -EINVAL;

bc45449b144461 David Daney        2012-04-12  457  		}
b2d57b56047b99 Peter Rosin        2016-07-09  458  
b2d57b56047b99 Peter Rosin        2016-07-09  459  		priv->adap.dev.of_node = child;
b2d57b56047b99 Peter Rosin        2016-07-09  460  		of_node_put(mux_node);
bc45449b144461 David Daney        2012-04-12  461  	}
bc45449b144461 David Daney        2012-04-12  462  
8eb5c87a92c065 Dustin Byford      2015-10-23  463  	/*
8eb5c87a92c065 Dustin Byford      2015-10-23  464  	 * Associate the mux channel with an ACPI node.
8eb5c87a92c065 Dustin Byford      2015-10-23  465  	 */
a7ab72390b7706 Peter Rosin        2016-04-20  466  	if (has_acpi_companion(muxc->dev))
a7ab72390b7706 Peter Rosin        2016-04-20  467  		acpi_preset_companion(&priv->adap.dev,
a7ab72390b7706 Peter Rosin        2016-04-20  468  				      ACPI_COMPANION(muxc->dev),
8eb5c87a92c065 Dustin Byford      2015-10-23  469  				      chan_id);
8eb5c87a92c065 Dustin Byford      2015-10-23  470  
0826374bff5741 Michael Lawnick    2010-08-11  471  	if (force_nr) {
0826374bff5741 Michael Lawnick    2010-08-11  472  		priv->adap.nr = force_nr;
0826374bff5741 Michael Lawnick    2010-08-11  473  		ret = i2c_add_numbered_adapter(&priv->adap);
9fce894d03a98e Peter Rosin        2017-05-15  474  		if (ret < 0) {
8d4d159f25a79b Peter Rosin        2017-04-03  475  			dev_err(&parent->dev,
8d4d159f25a79b Peter Rosin        2017-04-03  476  				"failed to add mux-adapter %u as bus %u (error=%d)\n",
8d4d159f25a79b Peter Rosin        2017-04-03  477  				chan_id, force_nr, ret);
9fce894d03a98e Peter Rosin        2017-05-15  478  			goto err_free_priv;
9fce894d03a98e Peter Rosin        2017-05-15  479  		}
0826374bff5741 Michael Lawnick    2010-08-11  480  	} else {
0826374bff5741 Michael Lawnick    2010-08-11  481  		ret = i2c_add_adapter(&priv->adap);
9fce894d03a98e Peter Rosin        2017-05-15  482  		if (ret < 0) {
8d4d159f25a79b Peter Rosin        2017-04-03  483  			dev_err(&parent->dev,
8d4d159f25a79b Peter Rosin        2017-04-03  484  				"failed to add mux-adapter %u (error=%d)\n",
8d4d159f25a79b Peter Rosin        2017-04-03  485  				chan_id, ret);
9fce894d03a98e Peter Rosin        2017-05-15  486  			goto err_free_priv;
0826374bff5741 Michael Lawnick    2010-08-11  487  		}
0826374bff5741 Michael Lawnick    2010-08-11  488  	}
0826374bff5741 Michael Lawnick    2010-08-11  489  
a7ab72390b7706 Peter Rosin        2016-04-20  490  	WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj,
a7ab72390b7706 Peter Rosin        2016-04-20  491  			       "mux_device"),
51cf3b0e2a72bb Wolfram Sang       2014-11-13  492  	     "can't create symlink to mux device\n");
51cf3b0e2a72bb Wolfram Sang       2014-11-13  493  
c9449affad2ae0 Gerlando Falauto   2014-11-13  494  	snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
a7ab72390b7706 Peter Rosin        2016-04-20  495  	WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj,
a7ab72390b7706 Peter Rosin        2016-04-20  496  			       symlink_name),
f657c9fe268882 Wolfram Sang       2018-05-21  497  	     "can't create symlink to channel %u\n", chan_id);
0826374bff5741 Michael Lawnick    2010-08-11  498  	dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
0826374bff5741 Michael Lawnick    2010-08-11  499  		 i2c_adapter_id(&priv->adap));
0826374bff5741 Michael Lawnick    2010-08-11  500  
a7ab72390b7706 Peter Rosin        2016-04-20  501  	muxc->adapter[muxc->num_adapters++] = &priv->adap;
a7ab72390b7706 Peter Rosin        2016-04-20  502  	return 0;
9fce894d03a98e Peter Rosin        2017-05-15  503  
9fce894d03a98e Peter Rosin        2017-05-15  504  err_free_priv:
9fce894d03a98e Peter Rosin        2017-05-15  505  	kfree(priv);
9fce894d03a98e Peter Rosin        2017-05-15 @506  	return ret;
0826374bff5741 Michael Lawnick    2010-08-11  507  }

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 0/5] I2C Mux per channel bus speed
  2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
                   ` (4 preceding siblings ...)
  2025-10-02 14:41 ` [PATCH v2 5/5] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
@ 2025-10-14 14:13 ` Marcus Folkesson
  5 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2025-10-14 14:13 UTC (permalink / raw)
  To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
	Andi Shyti
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, Bartosz Golaszewski

[-- Attachment #1: Type: text/plain, Size: 1588 bytes --]

Hi all,

On Thu, Oct 02, 2025 at 04:41:31PM +0200, Marcus Folkesson wrote:
> This was a RFC on how to implement a feature to have different bus
> speeds on different channels with an I2C multiplexer/switch.
> As no major complaints on the design came up during the review, I
> decided to submit the series without the RFC tag.
> 
> The benefit with this feature is that you may group devices after
> the fastest bus speed they can handle.
> A real-world example is that you could have e.g. a display running @400kHz
> and a smart battery running @100kHz using the same I2C controller.
> 
> There are many corner cases where this may cause a problem for some
> hardware topologies. I've tried to describe those I could think of
> in the documentation, see Patch #5.
> 
> E.g. one risk is that if the mux driver does not disconnect channels
> when Idle, this may cause a higher frequency to "leak" through to
> devices that are supposed to run at lower bus speed.
> This is not only a "problem" for changing bus speed but could also be
> an issue for potential address conflicts.
> 
> The implementation is split up into several patches:
> 
> Patch #1 Introduce a callback for the i2c controller to set bus speed
> Patch #2 Introduce functionality to adjust bus speed depending on mux
>          channel.
> Patch #3 Cleanup i2c-davinci driver a bit to prepare it for set_clk_freq
> Parch #4 Implement set_clk_freq for the i2c-davinci driver
> Parch #5 Update documentation with this feature

Any more comments on this series?

Thanks,
Marcus Folkesson


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-10-14 14:13 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-02 14:41 [PATCH v2 0/5] I2C Mux per channel bus speed Marcus Folkesson
2025-10-02 14:41 ` [PATCH v2 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
2025-10-02 14:41 ` [PATCH v2 2/5] i2c: mux: add support for per channel " Marcus Folkesson
2025-10-03 10:57   ` Dan Carpenter
2025-10-02 14:41 ` [PATCH v2 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
2025-10-02 14:49   ` Bartosz Golaszewski
2025-10-02 14:41 ` [PATCH v2 4/5] i2c: davinci: add support for setting bus frequency Marcus Folkesson
2025-10-02 14:41 ` [PATCH v2 5/5] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
2025-10-14 14:13 ` [PATCH v2 0/5] I2C Mux per channel " Marcus Folkesson

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).