Skip to content

Commit e11634e

Browse files
WangHanChikartben
authored andcommitted
drivers: i2c: tca954x: add support for idle disconnect
Add support for an optional "idle disconnect" feature in the TCA954x I2C multiplexer. When enabled via the `i2c-mux-idle-disconnect` device tree property, the driver will disconnect all channels after each transfer. This helps avoid address conflicts when multiple multiplexers are present on the same I2C bus. Even if the I2C transfer fails, the driver will still attempt to disconnect the channels to ensure the bus is left in a consistent state. If the disconnect operation itself fails, its error code will be returned unless the transfer already failed with a different error. This implementation is inspired by the Linux kernel driver for PCA954x I2C multiplexers. Special thanks to Ofir Shemesh for valuable suggestion. Signed-off-by: Hank Wang <wanghanchi2000@gmail.com>
1 parent 2dbc10a commit e11634e

File tree

2 files changed

+18
-1
lines changed

2 files changed

+18
-1
lines changed

drivers/i2c/i2c_tca954x.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct tca954x_root_config {
1818
struct i2c_dt_spec i2c;
1919
uint8_t nchans;
2020
const struct gpio_dt_spec reset_gpios;
21+
bool idle_disconnect;
2122
};
2223

2324
struct tca954x_root_data {
@@ -82,7 +83,7 @@ static int tca954x_transfer(const struct device *dev,
8283
const struct tca954x_root_config *config =
8384
get_root_config_from_channel(dev);
8485
const struct tca954x_channel_config *down_cfg = dev->config;
85-
int res;
86+
int res, disconnect_res;
8687

8788
res = k_mutex_lock(&data->lock, K_MSEC(5000));
8889
if (res != 0) {
@@ -96,6 +97,14 @@ static int tca954x_transfer(const struct device *dev,
9697

9798
res = i2c_transfer(config->i2c.bus, msgs, num_msgs, addr);
9899

100+
if (config->idle_disconnect) {
101+
/* Always attempt to disconnect, even if i2c_transfer fails */
102+
disconnect_res = tca954x_set_channel(down_cfg->root, 0);
103+
if (disconnect_res != 0 && res == 0) {
104+
res = disconnect_res;
105+
}
106+
}
107+
99108
end_trans:
100109
k_mutex_unlock(&data->lock);
101110
return res;
@@ -186,6 +195,7 @@ BUILD_ASSERT(CONFIG_I2C_TCA954X_CHANNEL_INIT_PRIO > CONFIG_I2C_TCA954X_ROOT_INIT
186195
.nchans = ch, \
187196
.reset_gpios = GPIO_DT_SPEC_GET_OR( \
188197
DT_INST(inst, ti_tca##n##a), reset_gpios, {0}), \
198+
.idle_disconnect = DT_INST_PROP(inst, i2c_mux_idle_disconnect), \
189199
}; \
190200
static struct tca954x_root_data tca##n##a_data_##inst = { \
191201
.lock = Z_MUTEX_INITIALIZER(tca##n##a_data_##inst.lock), \

dts/bindings/i2c/ti,tca954x-base.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ properties:
5252
description: |
5353
GPIO connected to the controller RESET pin. This pin is active-low.
5454
55+
i2c-mux-idle-disconnect:
56+
type: boolean
57+
description: |
58+
Forces mux to disconnect all children in idle state. This is
59+
necessary for example, if there are several multiplexers on the bus and
60+
the devices behind them use same I2C addresses.
61+
5562
child-binding:
5663
description: TCA954x I2C switch channel node
5764
include: [i2c-controller.yaml]

0 commit comments

Comments
 (0)