Skip to content

Commit

Permalink
drivers: i2c: tca9544a
Browse files Browse the repository at this point in the history
Extend tca954x (tca9546a, tca9548a) driver to support tca9544a i2c MUX.
(different bitmask and flag for enable bit in register)

Signed-off-by: Florian Weber <Florian.Weber@live.de>
  • Loading branch information
FlorianWeber1018 committed Oct 8, 2024
1 parent 86b6665 commit 8b254a4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 50 deletions.
3 changes: 2 additions & 1 deletion drivers/i2c/Kconfig.tca954x
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
menuconfig I2C_TCA954X
bool "I2C addressable switch"
default y
depends on DT_HAS_TI_TCA9546A_ENABLED || DT_HAS_TI_TCA9548A_ENABLED
depends on DT_HAS_TI_TCA9546A_ENABLED || DT_HAS_TI_TCA9548A_ENABLED \
|| DT_HAS_TI_TCA9544A_ENABLED
help
Enable TCA954x series I2C bus switch

Expand Down
96 changes: 47 additions & 49 deletions drivers/i2c/i2c_tca954x.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ struct tca954x_root_data {
struct tca954x_channel_config {
const struct device *root;
uint8_t chan_mask;
bool has_enable;
};

static inline struct tca954x_root_data *
get_root_data_from_channel(const struct device *dev)
static inline struct tca954x_root_data *get_root_data_from_channel(const struct device *dev)
{
const struct tca954x_channel_config *channel_config = dev->config;

Expand All @@ -48,8 +48,7 @@ get_root_config_from_channel(const struct device *dev)

static int tca954x_configure(const struct device *dev, uint32_t dev_config)
{
const struct tca954x_root_config *cfg =
get_root_config_from_channel(dev);
const struct tca954x_root_config *cfg = get_root_config_from_channel(dev);

return i2c_configure(cfg->i2c.bus, dev_config);
}
Expand All @@ -72,14 +71,11 @@ static int tca954x_set_channel(const struct device *dev, uint8_t select_mask)
return res;
}

static int tca954x_transfer(const struct device *dev,
struct i2c_msg *msgs,
uint8_t num_msgs,
uint16_t addr)
static int tca954x_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr)
{
struct tca954x_root_data *data = get_root_data_from_channel(dev);
const struct tca954x_root_config *config =
get_root_config_from_channel(dev);
const struct tca954x_root_config *config = get_root_config_from_channel(dev);
const struct tca954x_channel_config *down_cfg = dev->config;
int res;

Expand Down Expand Up @@ -113,8 +109,7 @@ static int tca954x_root_init(const struct device *dev)
/* If the RESET line is available, configure it. */
if (config->reset_gpios.port) {
if (!gpio_is_ready_dt(&config->reset_gpios)) {
LOG_ERR("%s is not ready",
config->reset_gpios.port->name);
LOG_ERR("%s is not ready", config->reset_gpios.port->name);
return -ENODEV;
}

Expand All @@ -135,15 +130,15 @@ static int tca954x_root_init(const struct device *dev)
static int tca954x_channel_init(const struct device *dev)
{
const struct tca954x_channel_config *chan_cfg = dev->config;
const struct tca954x_root_config *root_cfg =
get_root_config_from_channel(dev);
const struct tca954x_root_config *root_cfg = get_root_config_from_channel(dev);

if (!device_is_ready(chan_cfg->root)) {
LOG_ERR("I2C mux root %s not ready", chan_cfg->root->name);
return -ENODEV;
}

if (chan_cfg->chan_mask >= BIT(root_cfg->nchans)) {
if ((chan_cfg->chan_mask >= BIT(root_cfg->nchans) && !chan_cfg->has_enable) ||
(chan_cfg->chan_mask > (BIT(2) | (root_cfg->nchans - 1)) && chan_cfg->has_enable)) {
LOG_ERR("Wrong DTS address provided for %s", dev->name);
return -EINVAL;
}
Expand All @@ -162,49 +157,52 @@ static const struct i2c_driver_api tca954x_api_funcs = {
BUILD_ASSERT(CONFIG_I2C_TCA954X_CHANNEL_INIT_PRIO > CONFIG_I2C_TCA954X_ROOT_INIT_PRIO,
"I2C multiplexer channels must be initialized after their root");

#define TCA954x_CHILD_DEFINE(node_id, n) \
static const struct tca954x_channel_config \
tca##n##a_down_config_##node_id = { \
.chan_mask = BIT(DT_REG_ADDR(node_id)), \
.root = DEVICE_DT_GET(DT_PARENT(node_id)), \
}; \
DEVICE_DT_DEFINE(node_id, \
tca954x_channel_init, \
NULL, \
NULL, \
&tca##n##a_down_config_##node_id, \
POST_KERNEL, CONFIG_I2C_TCA954X_CHANNEL_INIT_PRIO, \
&tca954x_api_funcs);

#define TCA954x_ROOT_DEFINE(n, inst, ch) \
static const struct tca954x_root_config tca##n##a_cfg_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.nchans = ch, \
.reset_gpios = GPIO_DT_SPEC_GET_OR( \
DT_INST(inst, ti_tca##n##a), reset_gpios, {0}), \
}; \
static struct tca954x_root_data tca##n##a_data_##inst = { \
.lock = Z_MUTEX_INITIALIZER(tca##n##a_data_##inst.lock), \
}; \
I2C_DEVICE_DT_DEFINE(DT_INST(inst, ti_tca##n##a), \
tca954x_root_init, NULL, \
&tca##n##a_data_##inst, &tca##n##a_cfg_##inst, \
POST_KERNEL, CONFIG_I2C_TCA954X_ROOT_INIT_PRIO, \
NULL); \
DT_FOREACH_CHILD_VARGS(DT_INST(inst, ti_tca##n##a), TCA954x_CHILD_DEFINE, n);
#define TCA954x_CHILD_DEFINE(node_id, n, has_enable_bit) \
static const struct tca954x_channel_config tca##n##a_down_config_##node_id = { \
.chan_mask = has_enable_bit ? BIT(2) | DT_REG_ADDR(node_id) \
: BIT(DT_REG_ADDR(node_id)), \
.root = DEVICE_DT_GET(DT_PARENT(node_id)), \
.has_enable = has_enable_bit, \
}; \
DEVICE_DT_DEFINE(node_id, tca954x_channel_init, NULL, NULL, \
&tca##n##a_down_config_##node_id, POST_KERNEL, \
CONFIG_I2C_TCA954X_CHANNEL_INIT_PRIO, &tca954x_api_funcs);

#define TCA954x_ROOT_DEFINE(n, inst, ch, has_enable_bit) \
static const struct tca954x_root_config tca##n##a_cfg_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.nchans = ch, \
.reset_gpios = GPIO_DT_SPEC_GET_OR(DT_INST(inst, ti_tca##n##a), reset_gpios, {0}), \
}; \
static struct tca954x_root_data tca##n##a_data_##inst = { \
.lock = Z_MUTEX_INITIALIZER(tca##n##a_data_##inst.lock), \
}; \
I2C_DEVICE_DT_DEFINE(DT_INST(inst, ti_tca##n##a), tca954x_root_init, NULL, \
&tca##n##a_data_##inst, &tca##n##a_cfg_##inst, POST_KERNEL, \
CONFIG_I2C_TCA954X_ROOT_INIT_PRIO, NULL); \
DT_FOREACH_CHILD_VARGS(DT_INST(inst, ti_tca##n##a), TCA954x_CHILD_DEFINE, n, \
has_enable_bit);

/*
* TCA9546A: 4 channels
* TCA9544A: 4 channels mux
*/
#define TCA9546A_INIT(n) TCA954x_ROOT_DEFINE(9546, n, 4)
#define TCA9544A_INIT(n) TCA954x_ROOT_DEFINE(9544, n, 4, true)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT ti_tca9544a
DT_INST_FOREACH_STATUS_OKAY(TCA9544A_INIT)

/*
* TCA9546A: 4 channels switch
*/
#define TCA9546A_INIT(n) TCA954x_ROOT_DEFINE(9546, n, 4, false)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT ti_tca9546a
DT_INST_FOREACH_STATUS_OKAY(TCA9546A_INIT)

/*
* TCA9548A: 8 channels
* TCA9548A: 8 channels switch
*/
#define TCA9548A_INIT(n) TCA954x_ROOT_DEFINE(9548, n, 8)
#define TCA9548A_INIT(n) TCA954x_ROOT_DEFINE(9548, n, 8, false)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT ti_tca9548a
DT_INST_FOREACH_STATUS_OKAY(TCA9548A_INIT)
10 changes: 10 additions & 0 deletions dts/bindings/i2c/ti,tca9544a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Binding for TI TCA9544A, compatible with NXP PCA9544A

description: Texas Instruments TCA9544A binding

compatible: "ti,tca9544a"

include: ti,tca954x-base.yaml

child-binding:
compatible: "ti,tca9544a-channel"

0 comments on commit 8b254a4

Please sign in to comment.