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:
parent
dcb71a754f
commit
e31a96cbda
6 changed files with 173 additions and 1 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue