driver: i2c: TCA9546A: Rename to TCA954x

Prepare the driver to upcoming support of more mux references.
Rename all TCA9546A related files to TCA954x.
Keep ti,tca9546a and ti,tca9546a-channel compatible
for backward compatibility reasons.
New tca954x-base binding embedding common properties,
tca9546a binding inherits from it and define its own compatibles fields.

Signed-off-by: Aymeric Aillet <aymeric.aillet@iot.bzh>
This commit is contained in:
Aymeric Aillet 2021-12-06 15:59:40 +01:00 committed by Carles Cufí
commit 50540ed594
14 changed files with 261 additions and 237 deletions

View file

@ -387,7 +387,7 @@
/drivers/i2c/i2c_rv32m1_lpi2c* @henrikbrixandersen
/drivers/i2c/*sam0* @Sizurka
/drivers/i2c/i2c_dw* @dcpleung
/drivers/i2c/*tca9546a* @kurddt
/drivers/i2c/*tca954x* @kurddt
/drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg
/drivers/watchdog/*gecko* @oanerer
/drivers/watchdog/*sifive* @katsuster

View file

@ -34,7 +34,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_NPCX i2c_npcx_controller.c)
zephyr_library_sources_ifdef(CONFIG_I2C_NPCX i2c_npcx_port.c)
zephyr_library_sources_ifdef(CONFIG_I2C_DW i2c_dw.c)
zephyr_library_sources_ifdef(CONFIG_I2C_RCAR i2c_rcar.c)
zephyr_library_sources_ifdef(CONFIG_I2C_TCA9546A i2c_tca9546a.c)
zephyr_library_sources_ifdef(CONFIG_I2C_TCA954X i2c_tca954x.c)
zephyr_library_sources_ifdef(CONFIG_I2C_XEC_V2 i2c_mchp_xec_v2.c)
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1

View file

@ -49,7 +49,7 @@ source "drivers/i2c/Kconfig.lpc11u6x"
source "drivers/i2c/Kconfig.npcx"
source "drivers/i2c/Kconfig.test"
source "drivers/i2c/Kconfig.rcar"
source "drivers/i2c/Kconfig.tca9546a"
source "drivers/i2c/Kconfig.tca954x"
config I2C_INIT_PRIORITY
int "Init priority"

View file

@ -1,19 +0,0 @@
# Copyright (c) 2020 Innoseis BV
# SPDX-License-Identifier: Apache-2.0
menuconfig I2C_TCA9546A
bool "I2C addressable switch"
help
Enable TCA9546A i2C bus switch
if I2C_TCA9546A
config I2C_TCA9546_ROOT_INIT_PRIO
int "TCA9546a root driver init priority"
default I2C_INIT_PRIORITY
config I2C_TCA9546_CHANNEL_INIT_PRIO
int "TCA9546a channel driver init priority"
default I2C_INIT_PRIORITY
endif

View file

@ -0,0 +1,19 @@
# Copyright (c) 2020 Innoseis BV
# SPDX-License-Identifier: Apache-2.0
menuconfig I2C_TCA954X
bool "I2C addressable switch"
help
Enable TCA954x series I2C bus switch
if I2C_TCA954X
config I2C_TCA954X_ROOT_INIT_PRIO
int "TCA954x root driver init priority"
default I2C_INIT_PRIORITY
config I2C_TCA954X_CHANNEL_INIT_PRIO
int "TCA954x channel driver init priority"
default I2C_INIT_PRIORITY
endif

View file

@ -1,168 +0,0 @@
/*
* Copyright (c) 2020 Innoseis BV
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ti_tca9546a
#include <zephyr.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/i2c.h>
#include <logging/log.h>
#include <stdint.h>
LOG_MODULE_REGISTER(tca9546a, CONFIG_I2C_LOG_LEVEL);
#define MAX_CHANNEL_MASK BIT_MASK(4)
struct tca9546a_root_config {
const struct device *bus;
uint16_t slave_addr;
};
struct tca9546a_root_data {
struct k_mutex lock;
uint8_t selected_chan;
};
struct tca9546a_channel_config {
const struct device *root;
uint8_t chan_mask;
};
static inline struct tca9546a_root_data *
get_root_data_from_channel(const struct device *dev)
{
const struct tca9546a_channel_config *channel_config = dev->config;
return channel_config->root->data;
}
static inline const struct tca9546a_root_config *
get_root_config_from_channel(const struct device *dev)
{
const struct tca9546a_channel_config *channel_config = dev->config;
return channel_config->root->config;
}
static int tca9546a_configure(const struct device *dev, uint32_t dev_config)
{
const struct tca9546a_root_config *cfg = get_root_config_from_channel(
dev);
return i2c_configure(cfg->bus, dev_config);
}
static int tca9546a_set_channel(const struct device *dev, uint8_t select_mask)
{
int res = 0;
struct tca9546a_root_data *data = dev->data;
const struct tca9546a_root_config *cfg = dev->config;
if (data->selected_chan != select_mask) {
res = i2c_write(cfg->bus, &select_mask, 1, cfg->slave_addr);
if (res == 0) {
data->selected_chan = select_mask;
}
}
return res;
}
static int tca9546a_transfer(const struct device *dev,
struct i2c_msg *msgs,
uint8_t num_msgs,
uint16_t addr)
{
struct tca9546a_root_data *data = get_root_data_from_channel(dev);
const struct tca9546a_root_config *config = get_root_config_from_channel(
dev);
const struct tca9546a_channel_config *down_cfg = dev->config;
int res;
res = k_mutex_lock(&data->lock, K_MSEC(5000));
if (res != 0) {
return res;
}
res = tca9546a_set_channel(down_cfg->root, down_cfg->chan_mask);
if (res != 0) {
goto end_trans;
}
res = i2c_transfer(config->bus, msgs, num_msgs, addr);
end_trans:
k_mutex_unlock(&data->lock);
return res;
}
static int tca9546_root_init(const struct device *dev)
{
struct tca9546a_root_data *i2c_tca9546a = dev->data;
const struct tca9546a_root_config *config = dev->config;
if (!device_is_ready(config->bus)) {
LOG_ERR("I2C bus %s not ready", config->bus->name);
return -ENODEV;
}
i2c_tca9546a->selected_chan = 0;
return 0;
}
static int tca9546a_channel_init(const struct device *dev)
{
const struct tca9546a_channel_config *cfg = dev->config;
if (!device_is_ready(cfg->root)) {
LOG_ERR("I2C mux root %s not ready", cfg->root->name);
return -ENODEV;
}
if (cfg->chan_mask > MAX_CHANNEL_MASK) {
LOG_ERR("Wrong DTS address provided for %s", dev->name);
return -EINVAL;
}
return 0;
}
const struct i2c_driver_api tca9546a_api_funcs = {
.configure = tca9546a_configure,
.transfer = tca9546a_transfer,
};
#define TCA9546A_CHILD_DEFINE(node_id) \
static const struct tca9546a_channel_config \
tca9546a_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, \
tca9546a_channel_init, \
NULL, \
NULL, \
&tca9546a_down_config_##node_id, \
POST_KERNEL, CONFIG_I2C_TCA9546_CHANNEL_INIT_PRIO, \
&tca9546a_api_funcs);
#define TCA9546A_ROOT_CHILD_DEFINE(inst) \
static const struct tca9546a_root_config tca9546a_cfg_##inst = { \
.slave_addr = DT_INST_REG_ADDR(inst), \
.bus = DEVICE_DT_GET(DT_INST_BUS(inst)) \
}; \
static struct tca9546a_root_data tca9546a_data_##inst = { \
.lock = Z_MUTEX_INITIALIZER(tca9546a_data_##inst.lock), \
}; \
I2C_DEVICE_DT_INST_DEFINE(inst, \
tca9546_root_init, NULL, \
&tca9546a_data_##inst, &tca9546a_cfg_##inst, \
POST_KERNEL, CONFIG_I2C_TCA9546_ROOT_INIT_PRIO, \
NULL); \
DT_INST_FOREACH_CHILD(inst, TCA9546A_CHILD_DEFINE);
DT_INST_FOREACH_STATUS_OKAY(TCA9546A_ROOT_CHILD_DEFINE)

178
drivers/i2c/i2c_tca954x.c Normal file
View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2020 Innoseis BV
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <drivers/i2c.h>
#include <logging/log.h>
#include <stdint.h>
LOG_MODULE_REGISTER(tca954x, CONFIG_I2C_LOG_LEVEL);
struct tca954x_root_config {
const struct device *bus;
uint16_t slave_addr;
uint8_t nchans;
};
struct tca954x_root_data {
struct k_mutex lock;
uint8_t selected_chan;
};
struct tca954x_channel_config {
const struct device *root;
uint8_t chan_mask;
};
static inline struct tca954x_root_data *
get_root_data_from_channel(const struct device *dev)
{
const struct tca954x_channel_config *channel_config = dev->config;
return channel_config->root->data;
}
static inline const struct tca954x_root_config *
get_root_config_from_channel(const struct device *dev)
{
const struct tca954x_channel_config *channel_config = dev->config;
return channel_config->root->config;
}
static int tca954x_configure(const struct device *dev, uint32_t dev_config)
{
const struct tca954x_root_config *cfg =
get_root_config_from_channel(dev);
return i2c_configure(cfg->bus, dev_config);
}
static int tca954x_set_channel(const struct device *dev, uint8_t select_mask)
{
int res = 0;
struct tca954x_root_data *data = dev->data;
const struct tca954x_root_config *cfg = dev->config;
/* Only select the channel if its different from the last channel */
if (data->selected_chan != select_mask) {
res = i2c_write(cfg->bus, &select_mask, 1, cfg->slave_addr);
if (res == 0) {
data->selected_chan = select_mask;
} else {
LOG_DBG("tca954x: failed to set channel");
}
}
return res;
}
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_channel_config *down_cfg = dev->config;
int res;
res = k_mutex_lock(&data->lock, K_MSEC(5000));
if (res != 0) {
return res;
}
res = tca954x_set_channel(down_cfg->root, down_cfg->chan_mask);
if (res != 0) {
goto end_trans;
}
res = i2c_transfer(config->bus, msgs, num_msgs, addr);
end_trans:
k_mutex_unlock(&data->lock);
return res;
}
static int tca954x_root_init(const struct device *dev)
{
struct tca954x_root_data *i2c_tca954x = dev->data;
const struct tca954x_root_config *config = dev->config;
if (!device_is_ready(config->bus)) {
LOG_ERR("I2C bus %s not ready", config->bus->name);
return -ENODEV;
}
i2c_tca954x->selected_chan = 0;
return 0;
}
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);
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)) {
LOG_ERR("Wrong DTS address provided for %s", dev->name);
return -EINVAL;
}
return 0;
}
const struct i2c_driver_api tca954x_api_funcs = {
.configure = tca954x_configure,
.transfer = tca954x_transfer,
};
#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 = { \
.slave_addr = DT_REG_ADDR_BY_IDX(DT_INST(inst, ti_tca##n##a), 0), \
.bus = DEVICE_DT_GET(DT_BUS(DT_INST(inst, ti_tca##n##a))), \
.nchans = ch, \
}; \
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);
/*
* TCA9546A: 4 channels
*/
#define TCA9546A_INIT(n) TCA954x_ROOT_DEFINE(9546, n, 4)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT ti_tca9546a
DT_INST_FOREACH_STATUS_OKAY(TCA9546A_INIT)

View file

@ -1,46 +1,10 @@
# Copyright (c) 2020, Innoseis BV
# SPDX-License-Identifier: Apache-2.0
#Binding for TI TCA9546A, compatible with NXP PCA9546A
description: |
TCA9546a I2C switch
Each channel is represented by a separate devicetree child node
with a "ti,tca9546a-channel" compatible.
The channel node can then be used as a standard i2c bus controller
like in the following simplified example:
/* The tca9546a node must be a child of an i2c controller */
mux: tca9546a@77 {
compatible = "ti,tca9546a";
reg = <0x77>;
mux_i2c@0 {
compatible = "ti,tca9546a-channel";
reg = <0>;
temp_sens_0: tmp116@49 {
compatible = "ti,tmp116";
reg = <0x49>;
};
};
mux_i2c@1 {
compatible = "ti,tca9546a-channel";
reg = <1>;
temp_sens_1: tmp116@49 {
compatible = "ti,tmp116";
reg = <0x49>;
};
};
};
description: Texas Instruments TCA9546A binding
compatible: "ti,tca9546a"
include: [i2c-device.yaml]
include: ti,tca954x-base.yaml
child-binding:
description: TCA9546a I2C switch channel node
compatible: "ti,tca9546a-channel"
include: [i2c-controller.yaml]
on-bus: i2c

View file

@ -0,0 +1,52 @@
# Copyright (c) 2020, Innoseis BV
# SPDX-License-Identifier: Apache-2.0
# Common fields for TI TCA954X I2C mux family
description: |
TCA954x I2C switch
Each channel is represented by a separate devicetree child node.
The channel node can then be used as a standard i2c bus controller
like in the following simplified example:
/* The tca954x node must be a child of an i2c controller */
mux: tca9546a@77 {
compatible = "ti,tca9546a";
reg = <0x77>;
status = "okay";
label = "i2c_mux";
#address-cells = <1>;
#size-cells = <0>;
mux_i2c@0 {
compatible: "ti,tca9546a-channel"
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
temp_sens_0: tmp116@49 {
compatible = "ti,tmp116";
reg = <0x49>;
};
};
mux_i2c@1 {
compatible: "ti,tca9546a-channel"
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
temp_sens_1: tmp116@49 {
compatible = "ti,tmp116";
reg = <0x49>;
};
};
};
include: [i2c-device.yaml]
child-binding:
description: TCA954x I2C switch channel node
include: [i2c-controller.yaml]
on-bus: i2c

View file

@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(i2c_tca9546a)
project(i2c_tca954x)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -15,21 +15,19 @@
#size-cells = <0>;
ch0: mux_i2c@0 {
compatible = "ti,tca9546a-channel";
label = "mux_dw_0";
reg = <0>;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,tca9546a-channel";
};
ch1: mux_i2c@1 {
compatible = "ti,tca9546a-channel";
label = "mux_dw_1";
reg = <1>;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,tca9546a-channel";
};
};
};

View file

@ -1,3 +1,3 @@
CONFIG_ZTEST=y
CONFIG_I2C=y
CONFIG_I2C_TCA9546A=y
CONFIG_I2C_TCA954X=y

View file

@ -31,7 +31,7 @@
* This test verifies various assert macros provided by ztest.
*
*/
static void test_tca9546a(void)
static void test_tca954x(void)
{
uint8_t buff[1];
@ -48,7 +48,7 @@ static void test_tca9546a(void)
void test_main(void)
{
ztest_test_suite(framework_tests,
ztest_unit_test(test_tca9546a)
ztest_unit_test(test_tca954x)
);
ztest_run_test_suite(framework_tests);