diff --git a/drivers/i3c/i3c_ccc.c b/drivers/i3c/i3c_ccc.c index 7f4a15b5889..7488162e554 100644 --- a/drivers/i3c/i3c_ccc.c +++ b/drivers/i3c/i3c_ccc.c @@ -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, diff --git a/drivers/i3c/i3c_common.c b/drivers/i3c/i3c_common.c index d36a5ca6ddc..8d003051a75 100644 --- a/drivers/i3c/i3c_common.c +++ b/drivers/i3c/i3c_common.c @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -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. diff --git a/drivers/i3c/i3c_ibi_workq.c b/drivers/i3c/i3c_ibi_workq.c index bc1ec94f0f5..108f20f44e5 100644 --- a/drivers/i3c/i3c_ibi_workq.c +++ b/drivers/i3c/i3c_ibi_workq.c @@ -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: diff --git a/include/zephyr/drivers/i3c.h b/include/zephyr/drivers/i3c.h index 9c0a5e241d1..c910db02125 100644 --- a/include/zephyr/drivers/i3c.h +++ b/include/zephyr/drivers/i3c.h @@ -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 diff --git a/include/zephyr/drivers/i3c/ccc.h b/include/zephyr/drivers/i3c/ccc.h index 14d4addea91..ac2b65c40b1 100644 --- a/include/zephyr/drivers/i3c/ccc.h +++ b/include/zephyr/drivers/i3c/ccc.h @@ -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 diff --git a/include/zephyr/drivers/i3c/target_device.h b/include/zephyr/drivers/i3c/target_device.h index bf099b7ec4c..b2901f084ad 100644 --- a/include/zephyr/drivers/i3c/target_device.h +++ b/include/zephyr/drivers/i3c/target_device.h @@ -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.