* [PATCH v13 1/8] i2c: core: add callback to change bus frequency
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 2/8] i2c: mux: add idle_state property to i2c_mux_core Marcus Folkesson
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
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 must therefore be chosen so that all attached devices are able
to tolerate 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 run with different speeds.
Prepare for such a feature by providing 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.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
include/linux/i2c.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 6a847fc88d25..d2d60e937244 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -635,6 +635,15 @@ struct i2c_timings {
u32 analog_filter_cutoff_freq_hz;
};
+/**
+ * struct i2c_bus_ops - I2C bus control operations
+ * @set_clk_freq: Set clock frequency for the adapter.
+ * Returns negative errno, or 0 on success.
+ */
+struct i2c_bus_ops {
+ int (*set_clk_freq)(struct i2c_adapter *adap, u32 clock_Hz);
+};
+
/**
* struct i2c_bus_recovery_info - I2C bus recovery information
* @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
@@ -744,6 +753,7 @@ struct i2c_adapter_quirks {
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
+ *
*/
struct i2c_adapter {
struct module *owner;
@@ -760,6 +770,7 @@ struct i2c_adapter {
#ifdef CONFIG_I2C_DYNAMIC_TIMEOUT
int user_timeout; /* I2C_TIMEOUT ioctl value in jiffies */
#endif
+ u32 clock_Hz; /* bus clock speed */
int retries;
struct device dev; /* the adapter device */
unsigned long locked_flags; /* owned by the I2C core */
@@ -774,6 +785,7 @@ struct i2c_adapter {
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
+ const struct i2c_bus_ops *bus_ops;
const struct i2c_adapter_quirks *quirks;
struct irq_domain *host_notify_domain;
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 2/8] i2c: mux: add idle_state property to i2c_mux_core
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 1/8] i2c: core: add callback to change bus frequency Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 3/8] i2c: mux: add support for per channel bus frequency Marcus Folkesson
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson
Muxes treat their channels differently when idle.
Let the mux core have this information to make it available for
internal use.
Possible idle states are:
- I2C_MUX_IDLE_AS_IS: Leave channels as is when idle
- I2C_MUX_IDLE_DISCONNECT: Disconnect channel (set HiZ when idle)
- I2C_MUX_IDLE_UNKNOWN: Unknown idle state
- <n>: Enable channel n (starting from 0) when idle"
Default value is set to I2C_MUX_IDLE_UNKNOWN.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/i2c/i2c-mux.c | 1 +
include/linux/i2c-mux.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 53428098ddc7..94cc019b222f 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -248,6 +248,7 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
muxc->select = select;
muxc->deselect = deselect;
muxc->max_adapters = max_adapters;
+ muxc->idle_state = I2C_MUX_IDLE_UNKNOWN;
return muxc;
}
diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h
index 1784ac7afb11..17690f4f0dbe 100644
--- a/include/linux/i2c-mux.h
+++ b/include/linux/i2c-mux.h
@@ -14,6 +14,11 @@
#ifdef __KERNEL__
#include <linux/bitops.h>
+#include <linux/i2c.h>
+
+#define I2C_MUX_IDLE_AS_IS (-1)
+#define I2C_MUX_IDLE_DISCONNECT (-2)
+#define I2C_MUX_IDLE_UNKNOWN (-3)
struct i2c_mux_core {
struct i2c_adapter *parent;
@@ -22,6 +27,18 @@ struct i2c_mux_core {
unsigned int arbitrator:1;
unsigned int gate:1;
+ /*
+ * The mux state used by the driver when idle.
+ * Possible idle states are:
+ * - I2C_MUX_IDLE_AS_IS: Leave channels as is when idle
+ * - I2C_MUX_IDLE_DISCONNECT: Disconnect channel (set HiZ when idle)
+ * - I2C_MUX_IDLE_UNKNOWN: Unknown idle state
+ * - <n>: Enable channel n (starting from 0) when idle"
+ *
+ * Default value is set to I2C_MUX_IDLE_UNKNOWN.
+ */
+ int idle_state;
+
void *priv;
int (*select)(struct i2c_mux_core *, u32 chan_id);
@@ -38,6 +55,46 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
int (*select)(struct i2c_mux_core *, u32),
int (*deselect)(struct i2c_mux_core *, u32));
+static inline int i2c_mux_set_idle_state(struct i2c_mux_core *muxc, int state)
+{
+ switch (state) {
+ case I2C_MUX_IDLE_AS_IS:
+ case I2C_MUX_IDLE_UNKNOWN:
+ for (int i = 0; i < muxc->num_adapters; i++) {
+ /*
+ * idle_state is incompatible with channels that have a
+ * different clock frequency than the parent adapter.
+ */
+ if (muxc->adapter[i]->clock_Hz != muxc->parent->clock_Hz)
+ return -EINVAL;
+ }
+ break;
+
+ case I2C_MUX_IDLE_DISCONNECT:
+ break;
+
+ default:
+ if (state < 0 || state >= muxc->num_adapters)
+ return -EINVAL;
+
+ /*
+ * idle_state cannot select a channel with a different clock
+ * frequency than the parent adapter.
+ */
+ if (muxc->adapter[state]->clock_Hz != muxc->parent->clock_Hz)
+ return -EINVAL;
+ break;
+ }
+
+ muxc->idle_state = state;
+ return 0;
+}
+
+static inline int i2c_mux_idle_state(struct i2c_mux_core *muxc)
+{
+ return muxc->idle_state;
+}
+
/* flags for i2c_mux_alloc */
#define I2C_MUX_LOCKED BIT(0)
#define I2C_MUX_ARBITRATOR BIT(1)
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 3/8] i2c: mux: add support for per channel bus frequency
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 1/8] i2c: core: add callback to change bus frequency Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 2/8] i2c: mux: add idle_state property to i2c_mux_core Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 4/8] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
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 usable 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. putting 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 inherits the 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 | 195 ++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 183 insertions(+), 12 deletions(-)
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 94cc019b222f..008257624ba8 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -37,21 +37,141 @@ struct i2c_mux_priv {
u32 chan_id;
};
+static inline int
+__i2c_adapter_set_clk_freq(struct i2c_adapter *adapter, u32 clock_Hz)
+{
+ int ret;
+
+ /* Nothing to do if the requested frequency is already set. */
+ if (adapter->clock_Hz == clock_Hz)
+ return 0;
+
+ if (adapter->bus_ops && adapter->bus_ops->set_clk_freq) {
+ ret = adapter->bus_ops->set_clk_freq(adapter, clock_Hz);
+ if (ret < 0) {
+ dev_err(&adapter->dev, "Could not set requested frequency (%uHz)\n",
+ clock_Hz);
+ return ret;
+ }
+
+ /* Everything is fine, update the clock_Hz to the new value. */
+ clock_Hz = ret;
+ }
+
+ /*
+ * If the adapter is a root adapter without .set_clk_freq()
+ * implemented, this feature is not supported.
+ */
+ if (!i2c_parent_is_i2c_adapter(adapter))
+ return -EOPNOTSUPP;
+
+ /*
+ * Update the clock_Hz for non-root adapters, even if .set_clk_freq()
+ * is not implemented, to allow the clock frequency to be propagated
+ * to root adapters that do support it.
+ */
+ adapter->clock_Hz = clock_Hz;
+ return 0;
+}
+
+static inline int
+i2c_adapter_set_clk_freq(struct i2c_adapter *adapter, u32 clock_Hz)
+{
+ int ret;
+
+ i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
+ ret = __i2c_adapter_set_clk_freq(adapter, clock_Hz);
+ i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
+
+ return ret;
+}
+
+/*
+ * Lower the parent bus frequency to what this channel tolerates and select
+ * the channel. On success, @oldclock holds the frequency to restore in
+ * i2c_mux_deselect_chan(), or zero if the frequency was left untouched.
+ */
+static int i2c_mux_select_chan(struct i2c_adapter *adap, u32 *oldclock)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+ int ret;
+
+ if (priv->adap.clock_Hz && priv->adap.clock_Hz < parent->clock_Hz) {
+ u32 clock_Hz = parent->clock_Hz;
+
+ if (muxc->mux_locked)
+ ret = i2c_adapter_set_clk_freq(parent,
+ priv->adap.clock_Hz);
+ else
+ ret = __i2c_adapter_set_clk_freq(parent,
+ priv->adap.clock_Hz);
+
+ /*
+ * Never transfer on a segment that is still running too
+ * fast for the devices behind this channel.
+ */
+ if (ret) {
+ dev_err(&adap->dev,
+ "Failed to set clock frequency %uHz on adapter %s: %d\n",
+ priv->adap.clock_Hz, parent->name, ret);
+ return ret;
+ }
+
+ dev_dbg(&adap->dev, "Set clock frequency %uHz on %s\n",
+ priv->adap.clock_Hz, parent->name);
+
+ *oldclock = clock_Hz;
+ }
+
+ return muxc->select(muxc, priv->chan_id);
+}
+
+static void i2c_mux_deselect_chan(struct i2c_adapter *adap, u32 oldclock)
+{
+ struct i2c_mux_priv *priv = adap->algo_data;
+ struct i2c_mux_core *muxc = priv->muxc;
+ struct i2c_adapter *parent = muxc->parent;
+ int ret;
+
+ if (muxc->deselect)
+ muxc->deselect(muxc, priv->chan_id);
+
+ if (oldclock && oldclock != priv->adap.clock_Hz) {
+ if (muxc->mux_locked)
+ ret = i2c_adapter_set_clk_freq(parent, oldclock);
+ else
+ ret = __i2c_adapter_set_clk_freq(parent, oldclock);
+
+ if (ret) {
+ dev_err(&adap->dev,
+ "Failed to restore clock frequency %uHz on adapter %s: %d\n",
+ oldclock, parent->name, ret);
+ return;
+ }
+
+ dev_dbg(&adap->dev, "Restored clock frequency %uHz on %s\n",
+ oldclock, parent->name);
+ }
+}
+
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, &oldclock);
if (ret >= 0)
ret = __i2c_transfer(parent, msgs, num);
- if (muxc->deselect)
- muxc->deselect(muxc, priv->chan_id);
+
+ i2c_mux_deselect_chan(adap, oldclock);
return ret;
}
@@ -62,15 +182,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, &oldclock);
if (ret >= 0)
ret = i2c_transfer(parent, msgs, num);
- if (muxc->deselect)
- muxc->deselect(muxc, priv->chan_id);
+
+ i2c_mux_deselect_chan(adap, oldclock);
return ret;
}
@@ -83,16 +204,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, &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, oldclock);
return ret;
}
@@ -105,16 +227,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, &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, oldclock);
return ret;
}
@@ -374,6 +497,54 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
priv->channel_node = i2c_mux_get_channel_node(muxc, chan_id);
device_set_node(&priv->adap.dev, priv->channel_node);
+ if (dev_fwnode(muxc->dev)) {
+ fwnode_property_read_u32(priv->channel_node, "clock-frequency",
+ &priv->adap.clock_Hz);
+
+ /*
+ * If the mux adapter has no clock-frequency property, the
+ * channel inherits the parent bus speed.
+ */
+ 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);
+
+ ret = -EINVAL;
+ goto err_free_priv;
+ }
+
+ if (priv->adap.clock_Hz < parent->clock_Hz) {
+ /*
+ * A slow channel must not be left connected while the
+ * mux is idle, or it is exposed to the full parent
+ * bus speed whenever a sibling is accessed.
+ */
+ if (muxc->idle_state == I2C_MUX_IDLE_UNKNOWN ||
+ muxc->idle_state == I2C_MUX_IDLE_AS_IS ||
+ muxc->idle_state == (int)chan_id) {
+ dev_err(muxc->dev,
+ "channel %u has improper idle state for this configuration\n",
+ chan_id);
+ ret = -EINVAL;
+ goto err_free_priv;
+ }
+
+ /*
+ * Warn if the mux adapter is not parent-locked as
+ * this may cause issues for some hardware topologies.
+ */
+ if (muxc->mux_locked)
+ dev_warn(muxc->dev,
+ "channel %u is slower than parent on a non parent-locked mux\n",
+ chan_id);
+ }
+ }
+
/*
* Associate the mux channel with an ACPI node.
*/
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 4/8] i2c: davinci: calculate bus freq from Hz instead of kHz
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
` (2 preceding siblings ...)
2026-10-15 11:01 ` [PATCH v13 3/8] i2c: mux: add support for per channel bus frequency Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 5/8] i2c: davinci: add support for setting bus frequency Marcus Folkesson
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
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 | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index e15eef163a8f..f543997c31e9 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -132,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;
};
@@ -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 * 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 > I2C_MAX_STANDARD_MODE_FREQ)
clkl = (clk << 1) / 3;
else
clkl = (clk >> 1);
@@ -240,7 +240,7 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh);
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl);
- dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk);
+ dev_dbg(dev->dev, "input_clock = %u, CLK = %u\n", input_clock, clk);
}
/*
@@ -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 = %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,7 +760,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
if (r)
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.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 5/8] i2c: davinci: add support for setting bus frequency
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
` (3 preceding siblings ...)
2026-10-15 11:01 ` [PATCH v13 4/8] i2c: davinci: calculate bus freq from Hz instead of kHz Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 6/8] i2c: mux: ltc4306: set correct idle_state in i2c_mux_core Marcus Folkesson
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
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 | 37 +++++++++++++++++++++++++++++--------
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index f543997c31e9..e27cb3bbf34a 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,7 @@ 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 +206,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 > I2C_MAX_STANDARD_MODE_FREQ)
+ if (bus_freq_Hz > I2C_MAX_STANDARD_MODE_FREQ)
clkl = (clk << 1) / 3;
else
clkl = (clk >> 1);
@@ -267,7 +266,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 +278,23 @@ 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);
+
+ /* put I2C into reset */
+ davinci_i2c_reset_ctrl(dev, 0);
+
+ /* compute clock dividers */
+ adap->clock_Hz = clock_Hz;
+ i2c_davinci_calc_clk_dividers(dev);
+
+ /* Take the I2C module out of reset: */
+ davinci_i2c_reset_ctrl(dev, 1);
+
+ return clock_Hz;
+}
+
/*
* This routine does i2c bus recovery by using i2c_generic_scl_recovery
* which is provided by I2C Bus recovery infrastructure.
@@ -723,6 +739,10 @@ static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev)
}
#endif
+static const struct i2c_bus_ops davinci_bus_ops = {
+ .set_clk_freq = davinci_i2c_set_clk,
+};
+
static const struct i2c_algorithm i2c_davinci_algo = {
.xfer = i2c_davinci_xfer,
.functionality = i2c_davinci_func,
@@ -755,12 +775,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,12 +821,12 @@ 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;
strscpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name));
adap->algo = &i2c_davinci_algo;
+ adap->bus_ops = &davinci_bus_ops;
adap->dev.parent = &pdev->dev;
adap->timeout = DAVINCI_I2C_TIMEOUT;
adap->dev.of_node = dev_of_node(&pdev->dev);
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 6/8] i2c: mux: ltc4306: set correct idle_state in i2c_mux_core
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
` (4 preceding siblings ...)
2026-10-15 11:01 ` [PATCH v13 5/8] i2c: davinci: add support for setting bus frequency Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 7/8] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 8/8] i2c: mux: pca954x: use idle state from i2c_mux_core Marcus Folkesson
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson
Inform the core if we intend to disconnect channels during idle.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/i2c/muxes/i2c-mux-ltc4306.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c
index 18228f5b80e8..b1150a18ed06 100644
--- a/drivers/i2c/muxes/i2c-mux-ltc4306.c
+++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c
@@ -232,6 +232,11 @@ static int ltc4306_probe(struct i2c_client *client)
data = i2c_mux_priv(muxc);
data->chip = chip;
+ if (idle_disc)
+ i2c_mux_set_idle_state(muxc, I2C_MUX_IDLE_DISCONNECT);
+ else
+ i2c_mux_set_idle_state(muxc, I2C_MUX_IDLE_AS_IS);
+
i2c_set_clientdata(client, muxc);
data->regmap = devm_regmap_init_i2c(client, <c4306_regmap_config);
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 7/8] docs: i2c: i2c-topology: add section about bus speed
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
` (5 preceding siblings ...)
2026-10-15 11:01 ` [PATCH v13 6/8] i2c: mux: ltc4306: set correct idle_state in i2c_mux_core Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
2026-10-15 11:01 ` [PATCH v13 8/8] i2c: mux: pca954x: use idle state from i2c_mux_core Marcus Folkesson
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson
Describe what needs to be considered 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 | 180 +++++++++++++++++++++++++++++++++++++
1 file changed, 180 insertions(+)
diff --git a/Documentation/i2c/i2c-topology.rst b/Documentation/i2c/i2c-topology.rst
index 48fce0f7491b..97fe0e34a7ba 100644
--- a/Documentation/i2c/i2c-topology.rst
+++ b/Documentation/i2c/i2c-topology.rst
@@ -367,6 +367,186 @@ 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 uses 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 been set to 400kHz, a speed that D2 may not
+support.
+
+This topology is therefore 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 are 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 apply.
+
+Transfers to D3 may interleave between select-transfer-deselect to D1, which
+results in that the bus speed to D2 or D3 will be at 100KHz.
+
+Transfers to D2 may interleave between select-transfer-deselect to D1,
+which may result in a bus speed to D1 at 400kHz as the transfer to D2 may
+clobber the bus speed 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 is D1. Set the
+ 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.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v13 8/8] i2c: mux: pca954x: use idle state from i2c_mux_core
2026-10-15 11:01 [PATCH v13 0/8] I2C Mux per channel bus speed Marcus Folkesson
` (6 preceding siblings ...)
2026-10-15 11:01 ` [PATCH v13 7/8] docs: i2c: i2c-topology: add section about bus speed Marcus Folkesson
@ 2026-10-15 11:01 ` Marcus Folkesson
7 siblings, 0 replies; 9+ messages in thread
From: Marcus Folkesson @ 2026-10-15 11:01 UTC (permalink / raw)
To: Wolfram Sang, Michael Hennerich, Bartosz Golaszewski, Andi Shyti,
Andy Shevchenko, Bartosz Golaszewski, Peter Rosin, Peter Rosin
Cc: linux-i2c, linux-kernel, linux-arm-kernel, Marcus Folkesson
The core now have support for keeping the track of the idle state.
Make use of that instead of handle this on our own.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/i2c/muxes/i2c-mux-pca954x.c | 76 ++++++++++++++++++++++++-------------
1 file changed, 50 insertions(+), 26 deletions(-)
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 8fca709ed279..87bde07374d2 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -52,7 +52,6 @@
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <dt-bindings/mux/mux.h>
#define PCA954X_MAX_NCHANS 8
@@ -108,8 +107,6 @@ struct pca954x {
const struct chip_desc *chip;
u8 last_chan; /* last register value */
- /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
- s32 idle_state;
struct i2c_client *client;
@@ -337,14 +334,14 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
- s32 idle_state;
+ int idle_state;
- idle_state = READ_ONCE(data->idle_state);
+ idle_state = i2c_mux_idle_state(muxc);
if (idle_state >= 0)
/* Set the mux back to a predetermined channel */
return pca954x_select_chan(muxc, idle_state);
- if (idle_state == MUX_IDLE_DISCONNECT) {
+ if (idle_state == I2C_MUX_IDLE_DISCONNECT) {
/* Deselect active channel */
data->last_chan = 0;
return pca954x_reg_write(muxc->parent, client,
@@ -356,15 +353,30 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
return 0;
}
+/*
+ * The mux core only knows the maximum number of channels the driver may
+ * register, so range-check against the actual channel count of this chip
+ * before handing the state over.
+ */
+static int pca954x_set_idle_state(struct i2c_mux_core *muxc, int state)
+{
+ struct pca954x *data = i2c_mux_priv(muxc);
+
+ /* I2C_MUX_IDLE_UNKNOWN is internal to the mux core. */
+ if (state == I2C_MUX_IDLE_UNKNOWN || state >= data->chip->nchans)
+ return -EINVAL;
+
+ return i2c_mux_set_idle_state(muxc, state);
+}
+
static ssize_t idle_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
- struct pca954x *data = i2c_mux_priv(muxc);
- return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
+ return sprintf(buf, "%d\n", i2c_mux_idle_state(muxc));
}
static ssize_t idle_state_store(struct device *dev,
@@ -381,18 +393,19 @@ static ssize_t idle_state_store(struct device *dev,
if (ret < 0)
return ret;
- if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
- (val < 0 || val >= data->chip->nchans))
- return -EINVAL;
-
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
- WRITE_ONCE(data->idle_state, val);
+ ret = pca954x_set_idle_state(muxc, val);
+ if (ret) {
+ i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
+ return ret;
+ }
+
/*
* Set the mux into a state consistent with the new
* idle_state.
*/
- if (data->last_chan || val != MUX_IDLE_DISCONNECT)
+ if (data->last_chan || val != I2C_MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
@@ -478,12 +491,15 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc)
i2c_mux_del_adapters(muxc);
}
-static int pca954x_init(struct i2c_client *client, struct pca954x *data)
+static int pca954x_init(struct i2c_mux_core *muxc)
{
+ struct pca954x *data = i2c_mux_priv(muxc);
+ struct i2c_client *client = data->client;
+ int idle_state = i2c_mux_idle_state(muxc);
int ret;
- if (data->idle_state >= 0)
- data->last_chan = pca954x_regval(data, data->idle_state);
+ if (idle_state >= 0)
+ data->last_chan = pca954x_regval(data, idle_state);
else
data->last_chan = 0; /* Disconnect multiplexer */
@@ -560,6 +576,8 @@ static int pca954x_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct i2c_mux_core *muxc;
struct pca954x *data;
+ int idle_state;
+ u32 prop;
int num;
int ret;
@@ -618,25 +636,32 @@ static int pca954x_probe(struct i2c_client *client)
}
}
- data->idle_state = MUX_IDLE_AS_IS;
- if (device_property_read_u32(dev, "idle-state", &data->idle_state)) {
- if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
- data->idle_state = MUX_IDLE_DISCONNECT;
- }
-
/*
* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to a channel
* or disconnected state.
*/
- ret = pca954x_init(client, data);
+ ret = pca954x_init(muxc);
if (ret < 0) {
dev_warn(dev, "probe failed\n");
ret = -ENODEV;
goto fail_cleanup;
}
+ /* Set idle state to the default value, or to the value specified in the device tree */
+ idle_state = I2C_MUX_IDLE_AS_IS;
+ if (!device_property_read_u32(dev, "idle-state", &prop))
+ idle_state = (int)prop;
+ else if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
+ idle_state = I2C_MUX_IDLE_DISCONNECT;
+
+ ret = pca954x_set_idle_state(muxc, idle_state);
+ if (ret) {
+ dev_err(dev, "invalid idle state %d\n", idle_state);
+ goto fail_cleanup;
+ }
+
ret = pca954x_irq_setup(muxc);
if (ret)
goto fail_cleanup;
@@ -687,10 +712,9 @@ static int pca954x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
- struct pca954x *data = i2c_mux_priv(muxc);
int ret;
- ret = pca954x_init(client, data);
+ ret = pca954x_init(muxc);
if (ret < 0)
dev_err(&client->dev, "failed to verify mux presence\n");
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread