drivers: i3c: add ccc deftgts

Add support the ccc deftgts. This also includes a function to check if
there is a secondary controller on the bus, and will transmit deftgts
after initialization or a hotjoin event.

This also adds dynamic_addr to the config_target in order to retrieve
the currently configured dynamic address to be used with deftgts.

Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
This commit is contained in:
Ryan McClelland 2024-09-06 15:31:51 -07:00 committed by Anas Nashif
parent dcb71a754f
commit e31a96cbda
6 changed files with 173 additions and 1 deletions

View file

@ -472,6 +472,25 @@ int i3c_ccc_do_enttm(const struct device *controller,
return i3c_do_ccc(controller, &ccc_payload);
}
int i3c_ccc_do_deftgts_all(const struct device *controller,
struct i3c_ccc_deftgts *deftgts)
{
struct i3c_ccc_payload ccc_payload;
__ASSERT_NO_MSG(controller != NULL);
__ASSERT_NO_MSG(deftgts != NULL);
memset(&ccc_payload, 0, sizeof(ccc_payload));
ccc_payload.ccc.id = I3C_CCC_DEFTGTS;
ccc_payload.ccc.data = (uint8_t *)deftgts;
ccc_payload.ccc.data_len = sizeof(uint8_t) +
sizeof(struct i3c_ccc_deftgts_active_controller) +
(deftgts->count * sizeof(struct i3c_ccc_deftgts_target));
return i3c_do_ccc(controller, &ccc_payload);
}
int i3c_ccc_do_getstatus(const struct i3c_device_desc *target,
union i3c_ccc_getstatus *status,
enum i3c_ccc_getstatus_fmt fmt,

View file

@ -5,6 +5,7 @@
*/
#include <string.h>
#include <stdlib.h>
#include <zephyr/toolchain.h>
#include <zephyr/sys/__assert.h>
@ -647,6 +648,101 @@ static int i3c_bus_setdasa(const struct device *dev,
return 0;
}
bool i3c_bus_has_sec_controller(const struct device *dev)
{
struct i3c_driver_data *data = (struct i3c_driver_data *)dev->data;
sys_snode_t *node;
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *i3c_desc = CONTAINER_OF(node, struct i3c_device_desc, node);
if (I3C_BCR_DEVICE_ROLE(i3c_desc->bcr) ==
I3C_BCR_DEVICE_ROLE_I3C_CONTROLLER_CAPABLE) {
return true;
}
}
return false;
}
int i3c_bus_deftgts(const struct device *dev)
{
struct i3c_driver_data *data = (struct i3c_driver_data *)dev->data;
struct i3c_config_target config_target;
struct i3c_ccc_deftgts *deftgts;
sys_snode_t *node;
int ret;
uint8_t n = 0;
size_t num_of_targets = sys_slist_len(&data->attached_dev.devices.i3c) +
sys_slist_len(&data->attached_dev.devices.i2c);
size_t data_len = sizeof(uint8_t) +
sizeof(struct i3c_ccc_deftgts_active_controller) +
(num_of_targets * sizeof(struct i3c_ccc_deftgts_target));
/*
* Retrieve the active controller information
*/
ret = i3c_config_get(dev, I3C_CONFIG_TARGET, &config_target);
if (ret != 0) {
LOG_ERR("Failed to retrieve active controller info");
return ret;
}
/* Allocate memory for the struct with enough space for the targets */
deftgts = malloc(data_len);
if (!deftgts) {
return -ENOMEM;
}
/*
* Write the total number of I3C and I2C targets to the payload
*/
deftgts->count = num_of_targets;
/*
* Add the active controller information to the payload
*/
deftgts->active_controller.addr = config_target.dynamic_addr << 1;
deftgts->active_controller.dcr = config_target.dcr;
deftgts->active_controller.bcr = config_target.bcr;
deftgts->active_controller.static_addr = config_target.static_addr << 1;
/*
* Loop through each attached I3C device and add it to the payload
*/
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *i3c_desc = CONTAINER_OF(node, struct i3c_device_desc, node);
deftgts->targets[n].addr = i3c_desc->dynamic_addr << 1;
deftgts->targets[n].dcr = i3c_desc->dcr;
deftgts->targets[n].bcr = i3c_desc->bcr;
deftgts->targets[n].static_addr = i3c_desc->static_addr << 1;
n++;
}
/*
* Loop through each attached I2C device and add it to the payload
*/
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i2c, node) {
struct i3c_i2c_device_desc *i3c_i2c_desc =
CONTAINER_OF(node, struct i3c_i2c_device_desc, node);
deftgts->targets[n].addr = 0;
deftgts->targets[n].lvr = i3c_i2c_desc->lvr;
deftgts->targets[n].bcr = 0;
deftgts->targets[n].static_addr = (uint8_t)(i3c_i2c_desc->addr << 1);
n++;
}
/* TODO: add support for Group Addr in DEFTGTS when that comes */
ret = i3c_ccc_do_deftgts_all(dev, deftgts);
free(deftgts);
return ret;
}
int i3c_bus_init(const struct device *dev, const struct i3c_dev_list *dev_list)
{
int i, ret = 0;
@ -769,6 +865,13 @@ int i3c_bus_init(const struct device *dev, const struct i3c_dev_list *dev_list)
}
}
if (i3c_bus_has_sec_controller(dev)) {
ret = i3c_bus_deftgts(dev);
if (ret != 0) {
LOG_ERR("Error sending DEFTGTS");
}
}
/*
* Only re-enable Hot-Join from targets.
* Target interrupts will be enabled when IBI is enabled.

View file

@ -178,6 +178,13 @@ static void i3c_ibi_work_handler(struct k_work *work)
if ((ret != 0) && (ret != -EBUSY)) {
LOG_ERR("i3c_do_daa returns %d", ret);
}
if (i3c_bus_has_sec_controller(ibi_node->controller)) {
ret = i3c_bus_deftgts(ibi_node->controller);
if (ret != 0) {
LOG_ERR("Error sending DEFTGTS");
}
}
break;
case I3C_IBI_WORKQUEUE_CB:

View file

@ -2083,6 +2083,27 @@ int i3c_bus_init(const struct device *dev,
*/
int i3c_device_basic_info_get(struct i3c_device_desc *target);
/**
* @brief Check if the bus has a secondary controller.
*
* This reads the BCR from the device descriptor struct of all targets
* to determine whether a device is a secondary controller.
*
* @return True if the bus has a secondary controller, false otherwise.
*/
bool i3c_bus_has_sec_controller(const struct device *dev);
/**
* @brief Send the CCC DEFTGTS
*
* This builds the payload required for DEFTGTS and transmits it out
*
* @retval 0 if successful.
* @retval -ENOMEM No memory to build the payload.
* @retval -EIO General Input/Output error.
*/
int i3c_bus_deftgts(const struct device *dev);
/*
* This needs to be after declaration of struct i3c_driver_api,
* or else compiler complains about undefined type inside

View file

@ -451,6 +451,9 @@ struct i3c_ccc_deftgts_target {
* this CCC.
*/
struct i3c_ccc_deftgts {
/** Number of Targets (and Groups) present on the I3C Bus */
uint8_t count;
/** Data describing the active controller */
struct i3c_ccc_deftgts_active_controller active_controller;
@ -1827,6 +1830,17 @@ int i3c_ccc_do_setvendor_all(const struct device *controller,
*/
int i3c_ccc_do_setaasa_all(const struct device *controller);
/**
* @brief Broadcast DEFTGTS
*
* @param[in] controller Pointer to the controller device driver instance.
* @param[in] deftgts Pointer to the deftgts payload.
*
* @return @see i3c_do_ccc
*/
int i3c_ccc_do_deftgts_all(const struct device *controller,
struct i3c_ccc_deftgts *deftgts);
#ifdef __cplusplus
}
#endif

View file

@ -41,7 +41,15 @@ struct i3c_config_target {
bool enable;
/**
* I3C target address.
* I3C target dynamic address.
*
* Used used when operates as secondary controller
* or as a target device.
*/
uint8_t dynamic_addr;
/**
* I3C target static address.
*
* Used used when operates as secondary controller
* or as a target device.