Skip to content

Commit

Permalink
common: patch: i2c: Add multi-slave behavior support (#2170)
Browse files Browse the repository at this point in the history
Summary:
Description
- Support up to 3 slave address on the same bus

Motivation
- Some project will use 2 more slave address on the same bus

Pull Request resolved: #2170

Test Plan:
- Build code: Pass
- Stress 3 salve address with ENABLE_I2C_MULTI_SLAVE
- Stress without ENABLE_I2C_MULTI_SLAVE

Reviewed By: waffle2k

Differential Revision: D68142179

fbshipit-source-id: ffeb8938f57ea537f91a451274f4676dcb5d8019
  • Loading branch information
YoungLi123 authored and facebook-github-bot committed Jan 14, 2025
1 parent 19e451e commit 7c7cbb3
Show file tree
Hide file tree
Showing 3 changed files with 679 additions and 6 deletions.
80 changes: 74 additions & 6 deletions common/hal/hal_i2c_target.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <drivers/i2c.h>
#include "hal_i2c_target.h"
#include "libutil.h"
#include "plat_def.h"

/* LOG SET */
#include <logging/log.h>
Expand Down Expand Up @@ -49,12 +50,41 @@ static void do_something_after_wr_rcv(void *arg)
ARG_UNUSED(arg);
}

static struct i2c_target_data *i2c_find_slave_config(struct i2c_slave_config *config)
{
#ifdef ENABLE_I2C_MULTI_SLAVE
for (int i = 0; i < MAX_TARGET_NUM; i++) {
if (i2c_target_device_global[i].is_init &&
i2c_target_device_global[i].is_register) {
if (&i2c_target_device_global[i].data.config == config) {
return CONTAINER_OF(config, struct i2c_target_data, config);
}
if (i2c_target_device_global[i].is_enable_sec) {
if (&i2c_target_device_global[i].data.config_sec == config) {
return CONTAINER_OF(config, struct i2c_target_data,
config_sec);
}
}
if (i2c_target_device_global[i].is_enable_thd) {
if (&i2c_target_device_global[i].data.config_thd == config) {
return CONTAINER_OF(config, struct i2c_target_data,
config_thd);
}
}
}
}
#endif
return CONTAINER_OF(config, struct i2c_target_data, config);
}

static int i2c_target_write_requested(struct i2c_slave_config *config)
{
CHECK_NULL_ARG_WITH_RETURN(config, 1);

struct i2c_target_data *data;
data = CONTAINER_OF(config, struct i2c_target_data, config);
data = i2c_find_slave_config(config);

data->req_address = config->address & 0xFF;

data->target_wr_msg.msg_length = 0;
memset(data->target_wr_msg.msg, 0x0, MAX_I2C_TARGET_BUFF);
Expand All @@ -69,7 +99,7 @@ static int i2c_target_write_received(struct i2c_slave_config *config, uint8_t va
CHECK_NULL_ARG_WITH_RETURN(config, 1);

struct i2c_target_data *data;
data = CONTAINER_OF(config, struct i2c_target_data, config);
data = i2c_find_slave_config(config);

if (data->wr_buffer_idx >= MAX_I2C_TARGET_BUFF) {
LOG_ERR("Buffer_idx over limit!");
Expand All @@ -89,7 +119,7 @@ static int i2c_target_read_requested(struct i2c_slave_config *config, uint8_t *v
CHECK_NULL_ARG_WITH_RETURN(val, 1);

struct i2c_target_data *data;
data = CONTAINER_OF(config, struct i2c_target_data, config);
data = i2c_find_slave_config(config);

if (data->rd_data_collect_func) {
if (data->rd_data_collect_func(data) == false)
Expand All @@ -113,7 +143,7 @@ static int i2c_target_read_processed(struct i2c_slave_config *config, uint8_t *v
CHECK_NULL_ARG_WITH_RETURN(val, 1);

struct i2c_target_data *data;
data = CONTAINER_OF(config, struct i2c_target_data, config);
data = i2c_find_slave_config(config);

if (data->target_rd_msg.msg_length - data->rd_buffer_idx == 0) {
LOG_DBG("No remain buffer to read!");
Expand All @@ -135,7 +165,7 @@ static int i2c_target_stop(struct i2c_slave_config *config)
CHECK_NULL_ARG_WITH_RETURN(config, 1);

struct i2c_target_data *data;
data = CONTAINER_OF(config, struct i2c_target_data, config);
data = i2c_find_slave_config(config);

int ret = 1;

Expand Down Expand Up @@ -165,7 +195,7 @@ static int i2c_target_stop(struct i2c_slave_config *config)

if (data->rd_buffer_idx) {
if (data->target_rd_msg.msg_length - data->rd_buffer_idx) {
LOG_WRN("Read buffer doesn't read complete");
LOG_DBG("Read buffer doesn't read complete");
}
}

Expand Down Expand Up @@ -479,6 +509,8 @@ static int do_i2c_target_cfg(uint8_t bus_num, struct _i2c_target_config *cfg)
}

/* need unregister first */
i2c_target_device_global[bus_num].is_enable_sec = cfg->is_enable_sec;
i2c_target_device_global[bus_num].is_enable_thd = cfg->is_enable_thd;
if (!(target_status & I2C_TARGET_NOT_REGISTER)) {
int status = do_i2c_target_unregister(bus_num);
if (status) {
Expand Down Expand Up @@ -526,6 +558,14 @@ static int do_i2c_target_cfg(uint8_t bus_num, struct _i2c_target_config *cfg)
}

data->config.callbacks = &i2c_target_cb;
#ifdef ENABLE_I2C_MULTI_SLAVE
if (cfg->is_enable_sec) {
data->config_sec.callbacks = &i2c_target_cb;
}
if (cfg->is_enable_thd) {
data->config_thd.callbacks = &i2c_target_cb;
}
#endif
}
/* do modify, modify config set after init */
else {
Expand All @@ -538,6 +578,14 @@ static int do_i2c_target_cfg(uint8_t bus_num, struct _i2c_target_config *cfg)

data->max_msg_count = cfg->i2c_msg_count;
data->config.address = cfg->address >> 1; // to 7-bit target address
#ifdef ENABLE_I2C_MULTI_SLAVE
if (cfg->is_enable_sec) {
data->config_sec.address = cfg->address_sec >> 1;
}
if (cfg->is_enable_thd) {
data->config_thd.address = cfg->address_thd >> 1;
}
#endif

if (cfg->rd_data_collect_func)
data->rd_data_collect_func = cfg->rd_data_collect_func;
Expand Down Expand Up @@ -611,6 +659,16 @@ static int do_i2c_target_register(uint8_t bus_num)
if (ret)
return ret;

#ifdef ENABLE_I2C_MULTI_SLAVE
if (i2c_target_device_global[bus_num].is_enable_sec) {
ret = i2c_slave_register(data->i2c_controller, &data->config_sec);
}

if (i2c_target_device_global[bus_num].is_enable_thd) {
ret = i2c_slave_register(data->i2c_controller, &data->config_thd);
}
#endif

i2c_target_device_global[bus_num].is_register = 1;

return I2C_TARGET_API_NO_ERR;
Expand Down Expand Up @@ -645,6 +703,16 @@ static int do_i2c_target_unregister(uint8_t bus_num)

struct i2c_target_data *data = &i2c_target_device_global[bus_num].data;

#ifdef ENABLE_I2C_MULTI_SLAVE
if (i2c_target_device_global[bus_num].is_enable_thd) {
i2c_slave_unregister(data->i2c_controller, &data->config_thd);
}

if (i2c_target_device_global[bus_num].is_enable_sec) {
i2c_slave_unregister(data->i2c_controller, &data->config_sec);
}
#endif

int ret = i2c_slave_unregister(data->i2c_controller, &data->config);
if (ret)
return ret;
Expand Down
9 changes: 9 additions & 0 deletions common/hal/hal_i2c_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ struct i2c_target_data {
uint8_t i2c_bus; // i2c bus number
const struct device *i2c_controller; // i2c controller for one target bus
struct i2c_slave_config config; // i2c target relative config
struct i2c_slave_config config_sec; // i2c target relative config for 2nd address
struct i2c_slave_config config_thd; // i2c target relative config for 3rd address
uint8_t req_address; // current request address

/* TARGET WRITE - Support message queue for massive pending messages storage */
uint16_t wr_buffer_idx; // message buffer index
Expand All @@ -54,12 +57,18 @@ struct _i2c_target_config {
uint32_t i2c_msg_count;
bool (*rd_data_collect_func)(void *);
void (*post_wr_rcv_func)(void *);
uint8_t address_sec;
bool is_enable_sec;
uint8_t address_thd;
bool is_enable_thd;
};

struct i2c_target_device {
struct i2c_target_data data;
bool is_init;
bool is_register;
bool is_enable_sec;
bool is_enable_thd;
};

/* Retern value set for i2c target status */
Expand Down
Loading

0 comments on commit 7c7cbb3

Please sign in to comment.