diff --git a/drivers/i3c/i3c_ccc.c b/drivers/i3c/i3c_ccc.c index b4690b583c8..efe65e6756e 100644 --- a/drivers/i3c/i3c_ccc.c +++ b/drivers/i3c/i3c_ccc.c @@ -115,6 +115,7 @@ int i3c_ccc_do_rstdaa_all(const struct device *controller) int i3c_ccc_do_setdasa(const struct i3c_device_desc *target) { + struct i3c_driver_data *bus_data = (struct i3c_driver_data *)target->bus->data; struct i3c_ccc_payload ccc_payload; struct i3c_ccc_target_payload ccc_tgt_payload; uint8_t dyn_addr; @@ -133,6 +134,14 @@ int i3c_ccc_do_setdasa(const struct i3c_device_desc *target) dyn_addr = (target->init_dynamic_addr ? target->init_dynamic_addr : target->static_addr) << 1; + /* check that initial dynamic address is free before setting it */ + if ((target->init_dynamic_addr != 0) && + (target->init_dynamic_addr != target->static_addr)) { + if (!i3c_addr_slots_is_free(&bus_data->attached_dev.addr_slots, dyn_addr >> 1)) { + return -EINVAL; + } + } + ccc_tgt_payload.addr = target->static_addr; ccc_tgt_payload.rnw = 0; ccc_tgt_payload.data = &dyn_addr; @@ -148,6 +157,7 @@ int i3c_ccc_do_setdasa(const struct i3c_device_desc *target) int i3c_ccc_do_setnewda(const struct i3c_device_desc *target, struct i3c_ccc_address new_da) { + struct i3c_driver_data *bus_data = (struct i3c_driver_data *)target->bus->data; struct i3c_ccc_payload ccc_payload; struct i3c_ccc_target_payload ccc_tgt_payload; uint8_t new_dyn_addr; @@ -165,6 +175,13 @@ int i3c_ccc_do_setnewda(const struct i3c_device_desc *target, struct i3c_ccc_add */ new_dyn_addr = new_da.addr << 1; + /* check that initial dynamic address is free before setting it */ + if (target->dynamic_addr != new_da.addr) { + if (!i3c_addr_slots_is_free(&bus_data->attached_dev.addr_slots, new_da.addr)) { + return -EINVAL; + } + } + ccc_tgt_payload.addr = target->dynamic_addr; ccc_tgt_payload.rnw = 0; ccc_tgt_payload.data = &new_dyn_addr; diff --git a/drivers/i3c/i3c_common.c b/drivers/i3c/i3c_common.c index 7f00217440b..29746f3cf68 100644 --- a/drivers/i3c/i3c_common.c +++ b/drivers/i3c/i3c_common.c @@ -80,65 +80,61 @@ i3c_addr_slots_status(struct i3c_addr_slots *slots, } -int i3c_addr_slots_init(struct i3c_addr_slots *slots, - const struct i3c_dev_list *dev_list) +int i3c_addr_slots_init(const struct device *dev) { + struct i3c_driver_data *data = + (struct i3c_driver_data *)dev->data; + const struct i3c_driver_config *config = + (const struct i3c_driver_config *)dev->config; int i, ret = 0; struct i3c_device_desc *i3c_dev; struct i3c_i2c_device_desc *i2c_dev; - __ASSERT_NO_MSG(slots != NULL); + __ASSERT_NO_MSG(dev != NULL); - (void)memset(slots, 0, sizeof(*slots)); + (void)memset(&data->attached_dev.addr_slots, 0, sizeof(data->attached_dev.addr_slots)); + sys_slist_init(&data->attached_dev.devices.i3c); + sys_slist_init(&data->attached_dev.devices.i2c); for (i = 0; i <= 7; i++) { /* Addresses 0 to 7 are reserved */ - i3c_addr_slots_set(slots, i, I3C_ADDR_SLOT_STATUS_RSVD); + i3c_addr_slots_set(&data->attached_dev.addr_slots, i, I3C_ADDR_SLOT_STATUS_RSVD); /* * Addresses within a single bit error of broadcast address * are also reserved. */ - i3c_addr_slots_set(slots, I3C_BROADCAST_ADDR ^ BIT(i), + i3c_addr_slots_set(&data->attached_dev.addr_slots, I3C_BROADCAST_ADDR ^ BIT(i), I3C_ADDR_SLOT_STATUS_RSVD); } /* The broadcast address is reserved */ - i3c_addr_slots_set(slots, I3C_BROADCAST_ADDR, + i3c_addr_slots_set(&data->attached_dev.addr_slots, I3C_BROADCAST_ADDR, I3C_ADDR_SLOT_STATUS_RSVD); + /* + * Mark all I2C addresses first. + */ + for (i = 0; i < config->dev_list.num_i2c; i++) { + i2c_dev = &config->dev_list.i2c[i]; + ret = i3c_attach_i2c_device(i2c_dev); + if (ret != 0) { + /* Address slot is not free */ + ret = -EINVAL; + goto out; + } + } + /* * If there is a static address for the I3C devices, check * if this address is free, and there is no other devices of * the same (pre-assigned) address on the bus. */ - for (i = 0; i < dev_list->num_i3c; i++) { - i3c_dev = &dev_list->i3c[i]; - if (i3c_dev->static_addr != 0U) { - if (i3c_addr_slots_is_free(slots, i3c_dev->static_addr)) { - /* - * Mark address slot as I3C device for now to - * detect address collisons. This marking may be - * released during address assignment. - */ - i3c_addr_slots_mark_i3c(slots, i3c_dev->static_addr); - } else { - /* Address slot is not free */ - ret = -EINVAL; - goto out; - } - } - } - - /* - * Mark all I2C addresses. - */ - for (i = 0; i < dev_list->num_i2c; i++) { - i2c_dev = &dev_list->i2c[i]; - if (i3c_addr_slots_is_free(slots, i2c_dev->addr)) { - i3c_addr_slots_mark_i2c(slots, i2c_dev->addr); - } else { + for (i = 0; i < config->dev_list.num_i3c; i++) { + i3c_dev = &config->dev_list.i3c[i]; + ret = i3c_attach_i3c_device(i3c_dev); + if (ret != 0) { /* Address slot is not free */ ret = -EINVAL; goto out; @@ -185,6 +181,7 @@ struct i3c_device_desc *i3c_dev_list_find(const struct i3c_dev_list *dev_list, __ASSERT_NO_MSG(dev_list != NULL); + /* this only searches known I3C PIDs */ for (i = 0; i < dev_list->num_i3c; i++) { struct i3c_device_desc *desc = &dev_list->i3c[i]; @@ -197,16 +194,16 @@ struct i3c_device_desc *i3c_dev_list_find(const struct i3c_dev_list *dev_list, return ret; } -struct i3c_device_desc *i3c_dev_list_i3c_addr_find(const struct i3c_dev_list *dev_list, +struct i3c_device_desc *i3c_dev_list_i3c_addr_find(struct i3c_dev_attached_list *dev_list, uint8_t addr) { - int i; + sys_snode_t *node; struct i3c_device_desc *ret = NULL; __ASSERT_NO_MSG(dev_list != NULL); - for (i = 0; i < dev_list->num_i3c; i++) { - struct i3c_device_desc *desc = &dev_list->i3c[i]; + SYS_SLIST_FOR_EACH_NODE(&dev_list->devices.i3c, node) { + struct i3c_device_desc *desc = (void *)node; if (desc->dynamic_addr == addr) { ret = desc; @@ -217,16 +214,16 @@ struct i3c_device_desc *i3c_dev_list_i3c_addr_find(const struct i3c_dev_list *de return ret; } -struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(const struct i3c_dev_list *dev_list, - uint16_t addr) +struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(struct i3c_dev_attached_list *dev_list, + uint16_t addr) { - int i; + sys_snode_t *node; struct i3c_i2c_device_desc *ret = NULL; __ASSERT_NO_MSG(dev_list != NULL); - for (i = 0; i < dev_list->num_i2c; i++) { - struct i3c_i2c_device_desc *desc = &dev_list->i2c[i]; + SYS_SLIST_FOR_EACH_NODE(&dev_list->devices.i2c, node) { + struct i3c_i2c_device_desc *desc = (void *)node; if (desc->addr == addr) { ret = desc; @@ -237,6 +234,198 @@ struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(const struct i3c_dev_list return ret; } +int i3c_determine_default_addr(struct i3c_device_desc *target, uint8_t *addr) +{ + struct i3c_driver_data *data = (struct i3c_driver_data *)target->bus->data; + + /* If dynamic addr is set, then it assumed that it was assigned by a primary controller */ + if (target->dynamic_addr == 0) { + /* It is assumed that SETDASA or ENTDAA will be run after this */ + if (target->init_dynamic_addr != 0U) { + /* initial dynamic address is requested */ + if (target->static_addr == 0) { + /* SA is set to 0, so DA will be set with ENTDAA */ + if (i3c_addr_slots_is_free(&data->attached_dev.addr_slots, + target->init_dynamic_addr)) { + /* Set DA during ENTDAA */ + *addr = target->init_dynamic_addr; + } else { + /* address is not free, get the next one */ + *addr = i3c_addr_slots_next_free_find( + &data->attached_dev.addr_slots); + } + } else { + /* Use the init dynamic address as it's DA, but the RR will need to + * be first set with it's SA to run SETDASA, the RR address will + * need be updated after SETDASA with the request dynamic address + */ + if (i3c_addr_slots_is_free(&data->attached_dev.addr_slots, + target->static_addr)) { + *addr = target->static_addr; + } else { + /* static address has already been taken */ + return -EINVAL; + } + } + } else { + /* no init dynamic address is requested */ + if (target->static_addr != 0) { + if (i3c_addr_slots_is_free(&data->attached_dev.addr_slots, + target->static_addr)) { + /* static exists, set DA with same SA during SETDASA*/ + *addr = target->static_addr; + } else { + /* static address has already been taken */ + return -EINVAL; + } + } else { + /* pick a DA to use */ + *addr = i3c_addr_slots_next_free_find( + &data->attached_dev.addr_slots); + } + } + } else { + *addr = target->dynamic_addr; + } + + return 0; +} + +int i3c_attach_i3c_device(struct i3c_device_desc *target) +{ + struct i3c_driver_data *data = (struct i3c_driver_data *)target->bus->data; + const struct i3c_driver_api *api = (const struct i3c_driver_api *)target->bus->api; + sys_snode_t *node; + uint8_t addr = 0; + int status = 0; + + /* check to see if the device has already been attached */ + if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) { + SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) { + if (node == &target->node) { + return -EINVAL; + } + } + } + + status = i3c_determine_default_addr(target, &addr); + if (status != 0) { + return status; + } + + sys_slist_append(&data->attached_dev.devices.i3c, &target->node); + + if (api->attach_i3c_device != NULL) { + status = api->attach_i3c_device(target->bus, target, addr); + } + + i3c_addr_slots_mark_i3c(&data->attached_dev.addr_slots, addr); + + return status; +} + +int i3c_reattach_i3c_device(struct i3c_device_desc *target, uint8_t old_dyn_addr) +{ + struct i3c_driver_data *data = (struct i3c_driver_data *)target->bus->data; + const struct i3c_driver_api *api = (const struct i3c_driver_api *)target->bus->api; + int status = 0; + + if (!i3c_addr_slots_is_free(&data->attached_dev.addr_slots, target->dynamic_addr)) { + return -EINVAL; + } + + if (api->reattach_i3c_device != NULL) { + status = api->reattach_i3c_device(target->bus, target, old_dyn_addr); + } + + if (old_dyn_addr) { + /* mark the old address as free */ + i3c_addr_slots_mark_free(&data->attached_dev.addr_slots, old_dyn_addr); + } + + i3c_addr_slots_mark_i3c(&data->attached_dev.addr_slots, target->dynamic_addr); + + return status; +} + +int i3c_detach_i3c_device(struct i3c_device_desc *target) +{ + struct i3c_driver_data *data = (struct i3c_driver_data *)target->bus->data; + const struct i3c_driver_api *api = (const struct i3c_driver_api *)target->bus->api; + int status = 0; + + if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) { + if (!sys_slist_find_and_remove(&data->attached_dev.devices.i3c, &target->node)) { + return -EINVAL; + } + } else { + return -EINVAL; + } + + if (api->detach_i3c_device != NULL) { + status = api->detach_i3c_device(target->bus, target); + } + + i3c_addr_slots_mark_free(&data->attached_dev.addr_slots, + target->dynamic_addr ? target->dynamic_addr : target->static_addr); + + return status; +} + +int i3c_attach_i2c_device(struct i3c_i2c_device_desc *target) +{ + struct i3c_driver_data *data = (struct i3c_driver_data *)target->bus->data; + const struct i3c_driver_api *api = (const struct i3c_driver_api *)target->bus->api; + sys_snode_t *node; + int status = 0; + + /* check to see if the device has already been attached */ + if (!sys_slist_is_empty(&data->attached_dev.devices.i2c)) { + SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i2c, node) { + if (node == &target->node) { + return -EINVAL; + } + } + } + + if (!i3c_addr_slots_is_free(&data->attached_dev.addr_slots, target->addr)) { + return -EINVAL; + } + + sys_slist_append(&data->attached_dev.devices.i2c, &target->node); + + if (api->attach_i2c_device != NULL) { + status = api->attach_i2c_device(target->bus, target); + } + + i3c_addr_slots_mark_i2c(&data->attached_dev.addr_slots, target->addr); + + return status; +} + +int i3c_detach_i2c_device(struct i3c_i2c_device_desc *target) +{ + struct i3c_driver_data *data = (struct i3c_driver_data *)target->bus->data; + const struct i3c_driver_api *api = (const struct i3c_driver_api *)target->bus->api; + int status = 0; + + if (!sys_slist_is_empty(&data->attached_dev.devices.i2c)) { + if (!sys_slist_find_and_remove(&data->attached_dev.devices.i2c, &target->node)) { + return -EINVAL; + } + } else { + return -EINVAL; + } + + if (api->detach_i2c_device != NULL) { + status = api->detach_i2c_device(target->bus, target); + } + + i3c_addr_slots_mark_free(&data->attached_dev.addr_slots, target->addr); + + return status; +} + int i3c_dev_list_daa_addr_helper(struct i3c_addr_slots *addr_slots, const struct i3c_dev_list *dev_list, uint64_t pid, bool must_match, @@ -408,8 +597,16 @@ static int i3c_bus_setdasa(const struct device *dev, if (ret == 0) { desc->dynamic_addr = (desc->init_dynamic_addr ? desc->init_dynamic_addr : desc->static_addr); - i3c_reattach_i3c_device(desc, desc->static_addr); + if (desc->dynamic_addr != desc->static_addr) { + if (i3c_reattach_i3c_device(desc, desc->static_addr) != 0) { + LOG_ERR("Failed to reattach %s (%d)", desc->dev->name, ret); + } + } } else { + /* SETDASA failed, detach it from the controller */ + if (i3c_detach_i3c_device(desc) != 0) { + LOG_ERR("Failed to detach %s (%d)", desc->dev->name, ret); + } LOG_ERR("SETDASA error on address 0x%x (%d)", desc->static_addr, ret); continue; diff --git a/drivers/i3c/i3c_mcux.c b/drivers/i3c/i3c_mcux.c index f3b3d5005c5..2dbd68cac9e 100644 --- a/drivers/i3c/i3c_mcux.c +++ b/drivers/i3c/i3c_mcux.c @@ -66,6 +66,9 @@ LOG_MODULE_REGISTER(i3c_mcux, CONFIG_I3C_MCUX_LOG_LEVEL); #define I3C_MSTATUS_IBITYPE_HJ I3C_MSTATUS_IBITYPE(3) struct mcux_i3c_config { + /** Common I3C Driver Config */ + struct i3c_driver_config common; + /** Pointer to controller registers. */ I3C_Type *base; @@ -80,23 +83,17 @@ struct mcux_i3c_config { const struct pinctrl_dev_config *pincfg; #endif - /** I3C/I2C device list struct. */ - struct i3c_dev_list device_list; - /** Interrupt configuration function. */ void (*irq_config_func)(const struct device *dev); }; struct mcux_i3c_data { + /** Common I3C Driver Data */ + struct i3c_driver_data common; + /** Configuration parameter to be used with HAL. */ i3c_master_config_t ctrl_config_hal; - /** Controller configuration parameters */ - struct i3c_config_controller ctrl_config; - - /** Address slots */ - struct i3c_addr_slots addr_slots; - /** Semaphore to serialize access for applications. */ struct k_sem lock; @@ -820,7 +817,7 @@ struct i3c_device_desc *mcux_i3c_device_find(const struct device *dev, { const struct mcux_i3c_config *config = dev->config; - return i3c_dev_list_find(&config->device_list, id); + return i3c_dev_list_find(&config->common.dev_list, id); } /** @@ -839,9 +836,9 @@ struct i3c_device_desc *mcux_i3c_device_find(const struct device *dev, static struct i3c_i2c_device_desc * mcux_i3c_i2c_device_find(const struct device *dev, uint16_t addr) { - const struct mcux_i3c_config *config = dev->config; + struct cdns_i3c_data *data = dev->data; - return i3c_dev_list_i2c_addr_find(&config->device_list, addr); + return i3c_dev_list_i2c_addr_find(&data->common.attached_dev, addr); } /** @@ -1348,8 +1345,8 @@ static int mcux_i3c_do_daa(const struct device *dev) LOG_DBG("DAA: Rcvd PID 0x%04x%08x", vendor_id, part_no); - ret = i3c_dev_list_daa_addr_helper(&data->addr_slots, - &config->device_list, pid, + ret = i3c_dev_list_daa_addr_helper(&data->common.attached_dev.addr_slots, + &config->common.dev_list, pid, false, false, &target, &dyn_addr); if (ret != 0) { @@ -1362,7 +1359,7 @@ static int mcux_i3c_do_daa(const struct device *dev) target->dcr = rx_buf[7]; /* Mark the address as I3C device */ - i3c_addr_slots_mark_i3c(&data->addr_slots, dyn_addr); + i3c_addr_slots_mark_i3c(&data->common.attached_dev.addr_slots, dyn_addr); /* * If the device has static address, after address assignment, @@ -1371,7 +1368,8 @@ static int mcux_i3c_do_daa(const struct device *dev) * newly assigned one. */ if ((target->static_addr != 0U) && (dyn_addr != target->static_addr)) { - i3c_addr_slots_mark_free(&data->addr_slots, dyn_addr); + i3c_addr_slots_mark_free(&data->common.attached_dev.addr_slots, + dyn_addr); } /* Emit process DAA again to send the address to the device */ @@ -1533,7 +1531,7 @@ static void mcux_i3c_ibi_work(struct k_work *work) const struct device *dev = i3c_ibi_work->controller; const struct mcux_i3c_config *config = dev->config; struct mcux_i3c_data *data = dev->data; - const struct i3c_dev_list *dev_list = &config->device_list; + struct i3c_dev_attached_list *dev_list = &data->common.attached_dev; I3C_Type *base = config->base; struct i3c_device_desc *target = NULL; uint32_t mstatus, ibitype, ibiaddr; @@ -1980,7 +1978,7 @@ static int mcux_i3c_config_get(const struct device *dev, goto out_configure; } - (void)memcpy(config, &data->ctrl_config, sizeof(data->ctrl_config)); + (void)memcpy(config, &data->common.ctrl_config, sizeof(data->common.ctrl_config)); out_configure: return ret; @@ -1996,10 +1994,10 @@ static int mcux_i3c_init(const struct device *dev) const struct mcux_i3c_config *config = dev->config; struct mcux_i3c_data *data = dev->data; I3C_Type *base = config->base; - struct i3c_config_controller *ctrl_config = &data->ctrl_config; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; int ret = 0; - ret = i3c_addr_slots_init(&data->addr_slots, &config->device_list); + ret = i3c_addr_slots_init(dev); if (ret != 0) { goto err_out; } @@ -2038,10 +2036,10 @@ static int mcux_i3c_init(const struct device *dev) } /* Currently can only act as primary controller. */ - data->ctrl_config.is_secondary = false; + data->common.ctrl_config.is_secondary = false; /* HDR mode not supported at the moment. */ - data->ctrl_config.supported_hdr = 0U; + data->common.ctrl_config.supported_hdr = 0U; ret = mcux_i3c_configure(dev, I3C_CONFIG_CONTROLLER, ctrl_config); if (ret != 0) { @@ -2070,7 +2068,7 @@ static int mcux_i3c_init(const struct device *dev) config->irq_config_func(dev); /* Perform bus initialization */ - ret = i3c_bus_init(dev, &config->device_list); + ret = i3c_bus_init(dev, &config->common.dev_list); err_out: return ret; @@ -2133,9 +2131,9 @@ static const struct i3c_driver_api mcux_i3c_driver_api = { #define I3C_MCUX_DEVICE(id) \ I3C_MCUX_PINCTRL_DEFINE(id) \ static void mcux_i3c_config_func_##id(const struct device *dev); \ - static struct i3c_device_desc mcux_i3c_device_array[] = \ + static struct i3c_device_desc mcux_i3c_device_array_##id[] = \ I3C_DEVICE_ARRAY_DT_INST(id); \ - static struct i3c_i2c_device_desc mcux_i3c_i2c_device_array[] = \ + static struct i3c_i2c_device_desc mcux_i3c_i2c_device_array_##id[] = \ I3C_I2C_DEVICE_ARRAY_DT_INST(id); \ static const struct mcux_i3c_config mcux_i3c_config_##id = { \ .base = (I3C_Type *) DT_INST_REG_ADDR(id), \ @@ -2143,16 +2141,16 @@ static const struct i3c_driver_api mcux_i3c_driver_api = { .clock_subsys = \ (clock_control_subsys_t)DT_INST_CLOCKS_CELL(id, name), \ .irq_config_func = mcux_i3c_config_func_##id, \ - .device_list.i3c = mcux_i3c_device_array, \ - .device_list.num_i3c = ARRAY_SIZE(mcux_i3c_device_array), \ - .device_list.i2c = mcux_i3c_i2c_device_array, \ - .device_list.num_i2c = ARRAY_SIZE(mcux_i3c_i2c_device_array), \ + .common.dev_list.i3c = mcux_i3c_device_array_##id, \ + .common.dev_list.num_i3c = ARRAY_SIZE(mcux_i3c_device_array_##id), \ + .common.dev_list.i2c = mcux_i3c_i2c_device_array_##id, \ + .common.dev_list.num_i2c = ARRAY_SIZE(mcux_i3c_i2c_device_array_##id), \ I3C_MCUX_PINCTRL_INIT(id) \ }; \ static struct mcux_i3c_data mcux_i3c_data_##id = { \ .clocks.i3c_od_scl_hz = DT_INST_PROP_OR(id, i3c_od_scl_hz, 0), \ - .ctrl_config.scl.i3c = DT_INST_PROP_OR(id, i3c_scl_hz, 0), \ - .ctrl_config.scl.i2c = DT_INST_PROP_OR(id, i2c_scl_hz, 0), \ + .common.ctrl_config.scl.i3c = DT_INST_PROP_OR(id, i3c_scl_hz, 0), \ + .common.ctrl_config.scl.i2c = DT_INST_PROP_OR(id, i2c_scl_hz, 0), \ .clocks.clk_div_pp = DT_INST_PROP(id, clk_divider), \ .clocks.clk_div_od = DT_INST_PROP(id, clk_divider_slow), \ .clocks.clk_div_tc = DT_INST_PROP(id, clk_divider_tc), \ diff --git a/include/zephyr/drivers/i3c.h b/include/zephyr/drivers/i3c.h index 57501de7979..58a85d35566 100644 --- a/include/zephyr/drivers/i3c.h +++ b/include/zephyr/drivers/i3c.h @@ -487,10 +487,27 @@ __subsystem struct i3c_driver_api { */ int (*recover_bus)(const struct device *dev); + /** + * I3C Device Attach + * + * Optional API. + * + * @see i3c_attach_i3c_device + * + * @param dev Pointer to controller device driver instance. + * @param target Pointer to target device descriptor. + * @param addr Address to attach with + * + * @return @see i3c_attach_i3c_device + */ + int (*attach_i3c_device)(const struct device *dev, + struct i3c_device_desc *target, + uint8_t addr); + /** * I3C Address Update * - * Optional Controller only API. + * Optional API. * * @see i3c_reattach_i3c_device * @@ -504,6 +521,51 @@ __subsystem struct i3c_driver_api { struct i3c_device_desc *target, uint8_t old_dyn_addr); + /** + * I3C Device Detach + * + * Optional API. + * + * @see i3c_detach_i3c_device + * + * @param dev Pointer to controller device driver instance. + * @param target Pointer to target device descriptor. + * + * @return @see i3c_detach_i3c_device + */ + int (*detach_i3c_device)(const struct device *dev, + struct i3c_device_desc *target); + + /** + * I2C Device Attach + * + * Optional API. + * + * @see i3c_attach_i2c_device + * + * @param dev Pointer to controller device driver instance. + * @param target Pointer to target device descriptor. + * + * @return @see i3c_attach_i2c_device + */ + int (*attach_i2c_device)(const struct device *dev, + struct i3c_i2c_device_desc *target); + + /** + * I2C Device Detach + * + * Optional API. + * + * @see i3c_detach_i2c_device + * + * @param dev Pointer to controller device driver instance. + * @param target Pointer to target device descriptor. + * + * @return @see i3c_detach_i2c_device + */ + int (*detach_i2c_device)(const struct device *dev, + struct i3c_i2c_device_desc *target); + /** * Perform Dynamic Address Assignment via ENTDAA. * @@ -889,19 +951,49 @@ struct i3c_i2c_device_desc { /** * @brief Structure for describing attached devices for a controller. * - * This contains arrays of attached I3C and I2C devices. + * This contains slists of attached I3C and I2C devices. + * + * This is a helper struct that can be used by controller device + * driver to aid in device management. + */ +struct i3c_dev_attached_list { + /** + * Address slots: + * - Aid in dynamic address assignment. + * - Quick way to find out if a target address is + * a I3C or I2C device. + */ + struct i3c_addr_slots addr_slots; + + struct { + /** + * Linked list of attached I3C devices. + */ + sys_slist_t i3c; + + /** + * Linked list of attached I2C devices. + */ + sys_slist_t i2c; + } devices; +}; + +/** + * @brief Structure for describing known devices for a controller. + * + * This contains arrays of known I3C and I2C devices. * * This is a helper struct that can be used by controller device * driver to aid in device management. */ struct i3c_dev_list { /** - * Pointer to array of attached I3C devices. + * Pointer to array of known I3C devices. */ struct i3c_device_desc * const i3c; /** - * Pointer to array of attached I2C devices. + * Pointer to array of known I2C devices. */ struct i3c_i2c_device_desc * const i2c; @@ -916,6 +1008,28 @@ struct i3c_dev_list { const uint8_t num_i2c; }; +/** + * This structure is common to all I3C drivers and is expected to be + * the first element in the object pointed to by the config field + * in the device structure. + */ +struct i3c_driver_config { + /** I3C/I2C device list struct. */ + struct i3c_dev_list dev_list; +}; + +/** + * This structure is common to all I3C drivers and is expected to be the first + * element in the driver's struct driver_data declaration. + */ +struct i3c_driver_data { + /** Controller Configuration */ + struct i3c_config_controller ctrl_config; + + /** Attached I3C/I2C devices and addresses */ + struct i3c_dev_attached_list attached_dev; +}; + /** * @brief Find a I3C target device descriptor by ID. * @@ -934,8 +1048,8 @@ struct i3c_device_desc *i3c_dev_list_find(const struct i3c_dev_list *dev_list, /** * @brief Find a I3C target device descriptor by dynamic address. * - * This finds the I3C target device descriptor in the device list - * matching the dynamic address (@p addr) + * This finds the I3C target device descriptor in the attached + * device list matching the dynamic address (@p addr) * * @param dev_list Pointer to the device list struct. * @param addr Dynamic address to be matched. @@ -943,14 +1057,14 @@ struct i3c_device_desc *i3c_dev_list_find(const struct i3c_dev_list *dev_list, * @return Pointer the the I3C target device descriptor, or * NULL if none is found. */ -struct i3c_device_desc *i3c_dev_list_i3c_addr_find(const struct i3c_dev_list *dev_list, +struct i3c_device_desc *i3c_dev_list_i3c_addr_find(struct i3c_dev_attached_list *dev_list, uint8_t addr); /** * @brief Find a I2C target device descriptor by address. * - * This finds the I2C target device descriptor in the device list - * matching the address (@p addr) + * This finds the I2C target device descriptor in the attached + * device list matching the address (@p addr) * * @param dev_list Pointer to the device list struct. * @param addr Address to be matched. @@ -958,8 +1072,23 @@ struct i3c_device_desc *i3c_dev_list_i3c_addr_find(const struct i3c_dev_list *de * @return Pointer the the I2C target device descriptor, or * NULL if none is found. */ -struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(const struct i3c_dev_list *dev_list, - uint16_t addr); +struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(struct i3c_dev_attached_list *dev_list, + uint16_t addr); + +/** + * @brief Helper function to find the default address an i3c device is attached with + * + * This is a helper function to find the default address the + * device will be loaded with. This could be either it's static + * address, a requested dynamic address, or just a dynamic address + * that is available + * @param[in] target The pointer of the device descriptor + * @param[out] addr Address to be assigned to target device. + * + * @retval 0 if successful. + * @retval -EINVAL if the expected default address is already in use + */ +int i3c_determine_default_addr(struct i3c_device_desc *target, uint8_t *addr); /** * @brief Helper function to find a usable address during ENTDAA. @@ -1100,6 +1229,27 @@ static inline int i3c_recover_bus(const struct device *dev) return api->recover_bus(dev); } +/** + * @brief Attach an I3C device + * + * Called to attach a I3C device to the addresses. This is + * typically called before a SETDASA or ENTDAA to reserve + * the addresses. This will also call the optional api to + * update any registers within the driver if implemented. + * + * @warning + * Use cases involving multiple writers to the i3c/i2c devices must prevent + * concurrent write operations, either by preventing all writers from + * being preempted or by using a mutex to govern writes to the i3c/i2c devices. + * + * @param target Pointer to the target device descriptor + * + * @retval 0 If successful. + * @retval -EINVAL If address is not available or if the device + * has already been attached before + */ +int i3c_attach_i3c_device(struct i3c_device_desc *target); + /** * @brief Reattach I3C device * @@ -1108,8 +1258,14 @@ static inline int i3c_recover_bus(const struct device *dev) * down and has lost its address, or it can happen when a * device had a static address and has been assigned a * dynamic address with SETDASA or a dynamic address has - * been updated with SETNEWDA. - * This method is optional. + * been updated with SETNEWDA. This will also call the + * optional api to update any registers within the driver + * if implemented. + * + * @warning + * Use cases involving multiple writers to the i3c/i2c devices must prevent + * concurrent write operations, either by preventing all writers from + * being preempted or by using a mutex to govern writes to the i3c/i2c devices. * * @param target Pointer to the target device descriptor * @param old_dyn_addr The old dynamic address of target device, 0 if @@ -1118,18 +1274,67 @@ static inline int i3c_recover_bus(const struct device *dev) * @retval 0 If successful. * @retval -EINVAL If address is not available */ -static inline int i3c_reattach_i3c_device(struct i3c_device_desc *target, - uint8_t old_dyn_addr) -{ - const struct i3c_driver_api *api = - (const struct i3c_driver_api *)target->bus->api; +int i3c_reattach_i3c_device(struct i3c_device_desc *target, uint8_t old_dyn_addr); - if (api->reattach_i3c_device == NULL) { - return -ENOSYS; - } +/** + * @brief Detach I3C Device + * + * called to remove an I3C device and to free up the address + * that it used. If it's dynamic address was not set, then it + * assumed that SETDASA failed and will free it's static addr. + * This will also call the optional api to update any registers + * within the driver if implemented. + * + * @warning + * Use cases involving multiple writers to the i3c/i2c devices must prevent + * concurrent write operations, either by preventing all writers from + * being preempted or by using a mutex to govern writes to the i3c/i2c devices. + * + * @param target Pointer to the target device descriptor + * + * @retval 0 If successful. + * @retval -EINVAL If device is already detached + */ +int i3c_detach_i3c_device(struct i3c_device_desc *target); - return api->reattach_i3c_device(target->bus, target, old_dyn_addr); -} +/** + * @brief Attach an I2C device + * + * Called to attach a I2C device to the addresses. This will + * also call the optional api to update any registers within + * the driver if implemented. + * + * @warning + * Use cases involving multiple writers to the i3c/i2c devices must prevent + * concurrent write operations, either by preventing all writers from + * being preempted or by using a mutex to govern writes to the i3c/i2c devices. + * + * @param target Pointer to the target device descriptor + * + * @retval 0 If successful. + * @retval -EINVAL If address is not available or if the device + * has already been attached before + */ +int i3c_attach_i2c_device(struct i3c_i2c_device_desc *target); + +/** + * @brief Detach I2C Device + * + * called to remove an I2C device and to free up the address + * that it used. This will also call the optional api to + * update any registers within the driver if implemented. + * + * @warning + * Use cases involving multiple writers to the i3c/i2c devices must prevent + * concurrent write operations, either by preventing all writers from + * being preempted or by using a mutex to govern writes to the i3c/i2c devices. + * + * @param target Pointer to the target device descriptor + * + * @retval 0 If successful. + * @retval -EINVAL If device is already detached + */ +int i3c_detach_i2c_device(struct i3c_i2c_device_desc *target); /** * @brief Perform Dynamic Address Assignment on the I3C bus. diff --git a/include/zephyr/drivers/i3c/addresses.h b/include/zephyr/drivers/i3c/addresses.h index f259b735bba..95f05f180e1 100644 --- a/include/zephyr/drivers/i3c/addresses.h +++ b/include/zephyr/drivers/i3c/addresses.h @@ -49,14 +49,12 @@ struct i3c_addr_slots { * This clears out the assigned address bits, and set the reserved * address bits according to the I3C specification. * - * @param slots Pointer to address slots struct. - * @param dev_list Pointer to device list struct. + * @param dev Pointer to controller device driver instance. * * @retval 0 if successful. * @retval -EINVAL if duplicate addresses. */ -int i3c_addr_slots_init(struct i3c_addr_slots *slots, - const struct i3c_dev_list *dev_list); +int i3c_addr_slots_init(const struct device *dev); /** * @brief Set the address status of a device.