* [PATCH v6 1/5] i2c: core: add callback to change bus frequency
2026-02-16 12:38 [PATCH v6 0/5] I2C Mux per channel bus speed Marcus Folkesson
@ 2026-02-16 12:38 ` Marcus Folkesson
2026-02-16 12:38 ` [PATCH v6 2/5] i2c: mux: add support for per channel " Marcus Folkesson
` (3 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-16 12:38 UTC (permalink / raw)
To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Andy Shevchenko, Bartosz Golaszewski
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.
As a side effect, several bus drivers keep the bus speed in a
private structure and can now have this value stored in a uniform way
instead.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
include/linux/i2c.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 20fd41b51d5c..87654fab2f1b 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 */
+ u32 clock_hz; /* bus clock speed */
+ 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,15 @@ 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)
+{
+ if (adapter->set_clk_freq)
+ return adapter->set_clk_freq(adapter, clock_hz);
+
+ return -EOPNOTSUPP;
+}
+
/**
* i2c_mark_adapter_suspended - Report suspended state of the adapter to the core
* @adap: Adapter to mark as suspended
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v6 2/5] i2c: mux: add support for per channel bus frequency
2026-02-16 12:38 [PATCH v6 0/5] I2C Mux per channel bus speed Marcus Folkesson
2026-02-16 12:38 ` [PATCH v6 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
@ 2026-02-16 12:38 ` Marcus Folkesson
2026-02-16 16:40 ` Peter Rosin
2026-02-16 12:38 ` [PATCH v6 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
` (2 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-16 12:38 UTC (permalink / raw)
To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Andy Shevchenko, Bartosz Golaszewski
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 | 145 +++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 133 insertions(+), 12 deletions(-)
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index d59644e50f14..f125b4b8ae58 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -36,21 +36,113 @@ struct i2c_mux_priv {
u32 chan_id;
};
+static struct i2c_mux_core *i2c_mux_first_mux_locked(struct i2c_adapter *adap)
+{
+ struct i2c_adapter *parent;
+
+ while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
+ struct i2c_mux_priv *priv = adap->algo_data;
+
+ if (priv && priv->muxc && priv->muxc->mux_locked)
+ return priv->muxc;
+
+ adap = parent;
+ }
+
+ return NULL;
+}
+
+static int i2c_mux_select_chan(struct i2c_adapter *adap, u32 chan_id, u32 *oldclock)
+{
+ struct i2c_adapter *root = i2c_root_adapter(&adap->dev);
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_mux_core *mux_locked_ancestor;
+ int ret;
+
+ if (priv->adap.clock_hz && priv->adap.clock_hz < root->clock_hz) {
+ mux_locked_ancestor = i2c_mux_first_mux_locked(adap);
+ *oldclock = root->clock_hz;
+
+ /*
+ * If there's a mux-locked mux in our ancestry, lock the parent
+ * of the first one. When locked with I2C_LOCK_ROOT_ADAPTER,
+ * this will recurse through all intermediate muxes (both mux-locked
+ * and parent-locked) up to the root adapter, ensuring the entire
+ * chain is locked.
+ */
+ if (mux_locked_ancestor)
+ i2c_lock_bus(mux_locked_ancestor->parent, I2C_LOCK_ROOT_ADAPTER);
+
+ ret = i2c_adapter_set_clk_freq(root, priv->adap.clock_hz);
+
+ if (mux_locked_ancestor)
+ i2c_unlock_bus(mux_locked_ancestor->parent, I2C_LOCK_ROOT_ADAPTER);
+
+ if (ret)
+ dev_err(&adap->dev,
+ "Failed to set clock frequency %dHz on root adapter %s: %d\n",
+ priv->adap.clock_hz, root->name, ret);
+ }
+
+ return muxc->select(muxc, priv->chan_id);
+}
+
+static void i2c_mux_deselect_chan(struct i2c_adapter *adap, u32 chan_id, u32 oldclock)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *mux_locked_ancestor;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+ struct i2c_adapter *root;
+ int ret;
+
+ if (muxc->deselect)
+ muxc->deselect(muxc, priv->chan_id);
+
+ if (oldclock && oldclock != priv->adap.clock_hz) {
+ mux_locked_ancestor = i2c_mux_first_mux_locked(adap);
+ root = i2c_root_adapter(&parent->dev);
+
+ /*
+ * If there's a mux-locked mux in our ancestry, lock the parent
+ * of the first one. When locked with I2C_LOCK_ROOT_ADAPTER,
+ * this will recurse through all intermediate muxes (both mux-locked
+ * and parent-locked) up to the root adapter, ensuring the entire
+ * chain is locked.
+ */
+ if (mux_locked_ancestor)
+ i2c_lock_bus(mux_locked_ancestor->parent, I2C_LOCK_ROOT_ADAPTER);
+
+ ret = i2c_adapter_set_clk_freq(root, oldclock);
+
+ if (mux_locked_ancestor)
+ i2c_unlock_bus(mux_locked_ancestor->parent, I2C_LOCK_ROOT_ADAPTER);
+
+ if (ret)
+ dev_err(&adap->dev,
+ "Failed to set clock frequency %dHz on root adapter %s: %d\n",
+ oldclock, root->name, ret);
+
+ }
+}
+
static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)
{
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_mux_core *muxc = priv->muxc;
struct i2c_adapter *parent = muxc->parent;
+ u32 oldclock = 0;
int ret;
/* 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, &oldclock);
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, oldclock);
return ret;
}
@@ -61,15 +153,16 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_mux_core *muxc = priv->muxc;
struct i2c_adapter *parent = muxc->parent;
+ u32 oldclock = 0;
int ret;
/* 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, &oldclock);
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, oldclock);
return ret;
}
@@ -82,16 +175,17 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_mux_core *muxc = priv->muxc;
struct i2c_adapter *parent = muxc->parent;
+ u32 oldclock = 0;
int ret;
/* 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, &oldclock);
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, oldclock);
return ret;
}
@@ -104,16 +198,17 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_mux_core *muxc = priv->muxc;
struct i2c_adapter *parent = muxc->parent;
+ u32 oldclock = 0;
int ret;
/* 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, &oldclock);
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, oldclock);
return ret;
}
@@ -362,6 +457,32 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
}
}
+ of_property_read_u32(child, "clock-frequency", &priv->adap.clock_hz);
+
+ /* If the mux adapter has no clock-frequency property, inherit from parent */
+ if (!priv->adap.clock_hz)
+ priv->adap.clock_hz = parent->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);
+
+ /* 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);
+ ret = -EINVAL;
+ goto err_free_priv;
+ }
+
priv->adap.dev.of_node = child;
of_node_put(mux_node);
}
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 2/5] i2c: mux: add support for per channel bus frequency
2026-02-16 12:38 ` [PATCH v6 2/5] i2c: mux: add support for per channel " Marcus Folkesson
@ 2026-02-16 16:40 ` Peter Rosin
2026-02-16 18:50 ` Marcus Folkesson
0 siblings, 1 reply; 13+ messages in thread
From: Peter Rosin @ 2026-02-16 16:40 UTC (permalink / raw)
To: Marcus Folkesson, Wolfram Sang, Michael Hennerich,
Bartosz Golaszewski, Andi Shyti, Andy Shevchenko,
Bartosz Golaszewski
Cc: linux-i2c, linux-kernel, linux-arm-kernel
Hi!
> +static struct i2c_mux_core *i2c_mux_first_mux_locked(struct i2c_adapter *adap)
> +{
> + struct i2c_adapter *parent;
> +
> + while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> + struct i2c_mux_priv *priv = adap->algo_data;
This assumption does not hold, making the cast pretty wild indeed. There
are other i2c_adapters with a parent besides muxes. See e.g. i2c_atr.c
Cheers,
Peter
> +
> + if (priv && priv->muxc && priv->muxc->mux_locked)
> + return priv->muxc;
> +
> + adap = parent;
> + }
> +
> + return NULL;
> +}
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v6 2/5] i2c: mux: add support for per channel bus frequency
2026-02-16 16:40 ` Peter Rosin
@ 2026-02-16 18:50 ` Marcus Folkesson
2026-02-17 9:37 ` Peter Rosin
0 siblings, 1 reply; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-16 18:50 UTC (permalink / raw)
To: Peter Rosin
Cc: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, linux-i2c, linux-kernel,
linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 1180 bytes --]
Hi Peter!
On Mon, Feb 16, 2026 at 05:40:37PM +0100, Peter Rosin wrote:
> Hi!
>
> > +static struct i2c_mux_core *i2c_mux_first_mux_locked(struct i2c_adapter *adap)
> > +{
> > + struct i2c_adapter *parent;
> > +
> > + while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> > + struct i2c_mux_priv *priv = adap->algo_data;
>
> This assumption does not hold, making the cast pretty wild indeed. There
> are other i2c_adapters with a parent besides muxes. See e.g. i2c_atr.c
I see. Hrm, not sure how to decide if it is a mux or not. The best I
could come up with is to look at the i2c_adapter.lock_ops. E.g.
while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
/*
* Check if this adapter is a mux channel by verifying its
* lock_ops. Only mux channels use these specific lock operations.
*/
if (adap->lock_ops == &i2c_mux_lock_ops ||
adap->lock_ops == &i2c_parent_lock_ops) {
struct i2c_mux_priv *priv = adap->algo_data;
if (priv->muxc->mux_locked)
return priv->muxc;
}
adap = parent;
}
Or do you have a better idea?
>
> Cheers,
> Peter
Thank you in advance,
Marcus Folkesson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v6 2/5] i2c: mux: add support for per channel bus frequency
2026-02-16 18:50 ` Marcus Folkesson
@ 2026-02-17 9:37 ` Peter Rosin
2026-02-18 7:02 ` Marcus Folkesson
0 siblings, 1 reply; 13+ messages in thread
From: Peter Rosin @ 2026-02-17 9:37 UTC (permalink / raw)
To: Marcus Folkesson
Cc: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, linux-i2c, linux-kernel,
linux-arm-kernel
Hi!
2026-02-16 at 19:50, Marcus Folkesson wrote:
> Hi Peter!
>
> On Mon, Feb 16, 2026 at 05:40:37PM +0100, Peter Rosin wrote:
>> Hi!
>>
>>> +static struct i2c_mux_core *i2c_mux_first_mux_locked(struct i2c_adapter *adap)
>>> +{
>>> + struct i2c_adapter *parent;
>>> +
>>> + while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
>>> + struct i2c_mux_priv *priv = adap->algo_data;
>>
>> This assumption does not hold, making the cast pretty wild indeed. There
>> are other i2c_adapters with a parent besides muxes. See e.g. i2c_atr.c
>
> I see. Hrm, not sure how to decide if it is a mux or not. The best I
> could come up with is to look at the i2c_adapter.lock_ops. E.g.
>
>
> while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> /*
> * Check if this adapter is a mux channel by verifying its
> * lock_ops. Only mux channels use these specific lock operations.
> */
> if (adap->lock_ops == &i2c_mux_lock_ops ||
> adap->lock_ops == &i2c_parent_lock_ops) {
> struct i2c_mux_priv *priv = adap->algo_data;
>
> if (priv->muxc->mux_locked)
> return priv->muxc;
> }
> adap = parent;
> }
>
> Or do you have a better idea?
That looks fragile. My recommendation would be to avoid trying to
guess how a potentially diverse adapter tree should be handled
locally in the mux code. To me, it would feel better to introduce
locking/recursion in i2c_adapter_set_clk_freq() for muxes (and
address translators), i.e. take inspiration from i2c_transfer()
and i2c_smbus_xfer().
I guess an unlocked __i2c_adapter_set_clk_freq() is needed.
Cheers,
Peter
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v6 2/5] i2c: mux: add support for per channel bus frequency
2026-02-17 9:37 ` Peter Rosin
@ 2026-02-18 7:02 ` Marcus Folkesson
2026-02-23 7:29 ` Marcus Folkesson
0 siblings, 1 reply; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-18 7:02 UTC (permalink / raw)
To: Peter Rosin
Cc: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, linux-i2c, linux-kernel,
linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 2384 bytes --]
Hi Peter!
On Tue, Feb 17, 2026 at 10:37:39AM +0100, Peter Rosin wrote:
> Hi!
>
> 2026-02-16 at 19:50, Marcus Folkesson wrote:
> > Hi Peter!
> >
> > On Mon, Feb 16, 2026 at 05:40:37PM +0100, Peter Rosin wrote:
> >> Hi!
> >>
> >>> +static struct i2c_mux_core *i2c_mux_first_mux_locked(struct i2c_adapter *adap)
> >>> +{
> >>> + struct i2c_adapter *parent;
> >>> +
> >>> + while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> >>> + struct i2c_mux_priv *priv = adap->algo_data;
> >>
> >> This assumption does not hold, making the cast pretty wild indeed. There
> >> are other i2c_adapters with a parent besides muxes. See e.g. i2c_atr.c
> >
> > I see. Hrm, not sure how to decide if it is a mux or not. The best I
> > could come up with is to look at the i2c_adapter.lock_ops. E.g.
> >
> >
> > while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> > /*
> > * Check if this adapter is a mux channel by verifying its
> > * lock_ops. Only mux channels use these specific lock operations.
> > */
> > if (adap->lock_ops == &i2c_mux_lock_ops ||
> > adap->lock_ops == &i2c_parent_lock_ops) {
> > struct i2c_mux_priv *priv = adap->algo_data;
> >
> > if (priv->muxc->mux_locked)
> > return priv->muxc;
> > }
> > adap = parent;
> > }
> >
> > Or do you have a better idea?
>
> That looks fragile. My recommendation would be to avoid trying to
> guess how a potentially diverse adapter tree should be handled
> locally in the mux code. To me, it would feel better to introduce
> locking/recursion in i2c_adapter_set_clk_freq() for muxes (and
> address translators), i.e. take inspiration from i2c_transfer()
> and i2c_smbus_xfer().
That would be a more robust solution indeed.
>
> I guess an unlocked __i2c_adapter_set_clk_freq() is needed.
>
Rethinking the whole locking approach;
If I follow the same locking logic as in i2c_transfer/__i2c_transfer, do I
really need do take any more locks than the root adapter with
I2C_LOCK_ROOT_ADAPTER?
As the frequency can only be lowered, an intermediate i2c message will
not mess anything up?
If so, do you think i2c_root_adapter() should be moved to i2c-core-base.c?
Then i2c_adapter_set(_root?)_clk_freq() could lookup the root adapter
and take the lock there.
> Cheers,
> Peter
Best regards,
Marcus Folkesson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v6 2/5] i2c: mux: add support for per channel bus frequency
2026-02-18 7:02 ` Marcus Folkesson
@ 2026-02-23 7:29 ` Marcus Folkesson
0 siblings, 0 replies; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-23 7:29 UTC (permalink / raw)
To: Peter Rosin
Cc: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, linux-i2c, linux-kernel,
linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 2702 bytes --]
Hi Peter,
On Wed, Feb 18, 2026 at 08:02:54AM +0100, Marcus Folkesson wrote:
> Hi Peter!
>
> On Tue, Feb 17, 2026 at 10:37:39AM +0100, Peter Rosin wrote:
> > Hi!
> >
> > 2026-02-16 at 19:50, Marcus Folkesson wrote:
> > > Hi Peter!
> > >
> > > On Mon, Feb 16, 2026 at 05:40:37PM +0100, Peter Rosin wrote:
> > >> Hi!
> > >>
> > >>> +static struct i2c_mux_core *i2c_mux_first_mux_locked(struct i2c_adapter *adap)
> > >>> +{
> > >>> + struct i2c_adapter *parent;
> > >>> +
> > >>> + while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> > >>> + struct i2c_mux_priv *priv = adap->algo_data;
> > >>
> > >> This assumption does not hold, making the cast pretty wild indeed. There
> > >> are other i2c_adapters with a parent besides muxes. See e.g. i2c_atr.c
> > >
> > > I see. Hrm, not sure how to decide if it is a mux or not. The best I
> > > could come up with is to look at the i2c_adapter.lock_ops. E.g.
> > >
> > >
> > > while ((parent = i2c_parent_is_i2c_adapter(adap)) != NULL) {
> > > /*
> > > * Check if this adapter is a mux channel by verifying its
> > > * lock_ops. Only mux channels use these specific lock operations.
> > > */
> > > if (adap->lock_ops == &i2c_mux_lock_ops ||
> > > adap->lock_ops == &i2c_parent_lock_ops) {
> > > struct i2c_mux_priv *priv = adap->algo_data;
> > >
> > > if (priv->muxc->mux_locked)
> > > return priv->muxc;
> > > }
> > > adap = parent;
> > > }
> > >
> > > Or do you have a better idea?
> >
> > That looks fragile. My recommendation would be to avoid trying to
> > guess how a potentially diverse adapter tree should be handled
> > locally in the mux code. To me, it would feel better to introduce
> > locking/recursion in i2c_adapter_set_clk_freq() for muxes (and
> > address translators), i.e. take inspiration from i2c_transfer()
> > and i2c_smbus_xfer().
>
> That would be a more robust solution indeed.
>
> >
> > I guess an unlocked __i2c_adapter_set_clk_freq() is needed.
> >
>
> Rethinking the whole locking approach;
> If I follow the same locking logic as in i2c_transfer/__i2c_transfer, do I
> really need do take any more locks than the root adapter with
> I2C_LOCK_ROOT_ADAPTER?
> As the frequency can only be lowered, an intermediate i2c message will
> not mess anything up?
>
> If so, do you think i2c_root_adapter() should be moved to i2c-core-base.c?
> Then i2c_adapter_set(_root?)_clk_freq() could lookup the root adapter
> and take the lock there.
I've reworked this during the weekend and think I've got some my
answers. I will send out a new version later today.
Thanks!
Best regards,
Marcus Folkesson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz
2026-02-16 12:38 [PATCH v6 0/5] I2C Mux per channel bus speed Marcus Folkesson
2026-02-16 12:38 ` [PATCH v6 1/5] i2c: core: add callback to change bus frequency Marcus Folkesson
2026-02-16 12:38 ` [PATCH v6 2/5] i2c: mux: add support for per channel " Marcus Folkesson
@ 2026-02-16 12:38 ` Marcus Folkesson
2026-02-17 9:07 ` Andy Shevchenko
2026-02-16 12:38 ` [PATCH v6 4/5] i2c: davinci: add support for setting bus frequency Marcus Folkesson
2026-02-16 12:38 ` [PATCH v6 5/5] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
4 siblings, 1 reply; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-16 12:38 UTC (permalink / raw)
To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Andy Shevchenko, Bartosz Golaszewski
Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson,
Bartosz Golaszewski
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.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
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 a773ba082321..761de5a814df 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: */
@@ -760,9 +758,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.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v6 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz
2026-02-16 12:38 ` [PATCH v6 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
@ 2026-02-17 9:07 ` Andy Shevchenko
2026-02-17 9:22 ` Marcus Folkesson
0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-02-17 9:07 UTC (permalink / raw)
To: Marcus Folkesson
Cc: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Bartosz Golaszewski, linux-i2c, linux-kernel,
linux-arm-kernel
On Mon, Feb 16, 2026 at 01:38:15PM +0100, Marcus Folkesson 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.
...
> /*
> * 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)
The 100000 has a definition in i2c.h....
> clkl = (clk << 1) / 3;
> else
> clkl = (clk >> 1);
...
> r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop);
(Why not using i2c timings? The I²C core has an API to parse firmware properties.)
> if (r)
> - prop = DAVINCI_I2C_DEFAULT_BUS_FREQ;
> + prop = I2C_MAX_STANDARD_MODE_FREQ;
...like this one.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v6 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz
2026-02-17 9:07 ` Andy Shevchenko
@ 2026-02-17 9:22 ` Marcus Folkesson
0 siblings, 0 replies; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-17 9:22 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Bartosz Golaszewski, linux-i2c, linux-kernel,
linux-arm-kernel
[-- Attachment #1: Type: text/plain, Size: 1436 bytes --]
Hi Andy,
On Tue, Feb 17, 2026 at 11:07:06AM +0200, Andy Shevchenko wrote:
> On Mon, Feb 16, 2026 at 01:38:15PM +0100, Marcus Folkesson 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.
>
> ...
>
> > /*
> > * 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)
>
> The 100000 has a definition in i2c.h....
Good point, thanks.
>
> > clkl = (clk << 1) / 3;
> > else
> > clkl = (clk >> 1);
>
> ...
>
> > r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop);
>
> (Why not using i2c timings? The I²C core has an API to parse firmware properties.)
Actually, I found i2c_parse_fw_timings() just a day ago and have on my
TODO-list to convert all bus drivers to use it right after the work with this
series is done.
>
> > if (r)
> > - prop = DAVINCI_I2C_DEFAULT_BUS_FREQ;
> > + prop = I2C_MAX_STANDARD_MODE_FREQ;
>
> ...like this one.
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
Best regards,
Marcus Folkesson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v6 4/5] i2c: davinci: add support for setting bus frequency
2026-02-16 12:38 [PATCH v6 0/5] I2C Mux per channel bus speed Marcus Folkesson
` (2 preceding siblings ...)
2026-02-16 12:38 ` [PATCH v6 3/5] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
@ 2026-02-16 12:38 ` Marcus Folkesson
2026-02-16 12:38 ` [PATCH v6 5/5] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
4 siblings, 0 replies; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-16 12:38 UTC (permalink / raw)
To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Andy Shevchenko, Bartosz Golaszewski
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.
Remove bus_freq_hz entirely and only use clock_hz instead.
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/i2c/busses/i2c-davinci.c | 38 ++++++++++++++++++++++++++++++--------
1 file changed, 30 insertions(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 761de5a814df..ca41e6d6b623 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -132,8 +132,6 @@ struct davinci_i2c_dev {
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
- /* standard bus frequency */
- unsigned int bus_freq_hz;
/* Chip has a ICPFUNC register */
bool has_pfunc;
};
@@ -171,6 +169,8 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
u32 clkh;
u32 clkl;
u32 input_clock = clk_get_rate(dev->clk);
+ u32 bus_freq_hz = dev->adapter.clock_hz;
+
/* NOTE: I2C Clock divider programming info
* As per I2C specs the following formulas provide prescaler
@@ -207,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_hz));
+ clk = ((input_clock / (psc + 1)) / (bus_freq_hz));
/* Avoid driving the bus too fast because of rounding errors above */
- if (input_clock / (psc + 1) / clk > dev->bus_freq_hz)
+ if (input_clock / (psc + 1) / clk > 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_hz > 100000)
+ if (bus_freq_hz > 100000)
clkl = (clk << 1) / 3;
else
clkl = (clk >> 1);
@@ -267,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_hz = %dHz\n", dev->bus_freq_hz);
+ dev_dbg(dev->dev, "bus_freq_hz = %dHz\n", dev->adapter.clock_hz);
/* Take the I2C module out of reset: */
@@ -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 (adap->clock_hz == clock_hz)
+ return 0;
+
+ adap->clock_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.
@@ -755,12 +776,13 @@ static int davinci_i2c_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->irq = irq;
platform_set_drvdata(pdev, dev);
+ adap = &dev->adapter;
r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop);
if (r)
prop = I2C_MAX_STANDARD_MODE_FREQ;
- dev->bus_freq_hz = prop;
+ adap->clock_hz = prop;
dev->has_pfunc = device_property_present(&pdev->dev, "ti,has-pfunc");
@@ -800,7 +822,6 @@ static int davinci_i2c_probe(struct platform_device *pdev)
goto err_unuse_clocks;
}
- adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
@@ -809,6 +830,7 @@ 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->set_clk_freq = davinci_i2c_set_clk;
if (dev->has_pfunc)
adap->bus_recovery_info = &davinci_i2c_scl_recovery_info;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v6 5/5] docs: i2c: i2c-topology: add section about bus speed
2026-02-16 12:38 [PATCH v6 0/5] I2C Mux per channel bus speed Marcus Folkesson
` (3 preceding siblings ...)
2026-02-16 12:38 ` [PATCH v6 4/5] i2c: davinci: add support for setting bus frequency Marcus Folkesson
@ 2026-02-16 12:38 ` Marcus Folkesson
4 siblings, 0 replies; 13+ messages in thread
From: Marcus Folkesson @ 2026-02-16 12:38 UTC (permalink / raw)
To: Wolfram Sang, Peter Rosin, Michael Hennerich, Bartosz Golaszewski,
Andi Shyti, Andy Shevchenko, Bartosz Golaszewski
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 | 178 +++++++++++++++++++++++++++++++++++++
1 file changed, 178 insertions(+)
diff --git a/Documentation/i2c/i2c-topology.rst b/Documentation/i2c/i2c-topology.rst
index 48fce0f7491b..24df553ca8c7 100644
--- a/Documentation/i2c/i2c-topology.rst
+++ b/Documentation/i2c/i2c-topology.rst
@@ -367,6 +367,184 @@ 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 allow 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 therefore 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 account.
+
+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 D1 can operate at 400kHz. When D1 is selected, the bus speed of the
+root adapter would have to be 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 parallel 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 parallel
+----------------------------
+
+When multiple muxes are used in parallel 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 are 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 writing, the following controllers have support:
+
+============================ =============================================
+i2c-davinci Supports dynamic bus speed
+============================ =============================================
Mux type of existing device drivers
===================================
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread