From b68492166a92e44abcd2d544cd8c25376a91d9dd Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Fri, 9 Dec 2022 00:31:14 -0800 Subject: [PATCH] drivers: i3c: cdns: add attach/detach api implementation The cadence i3c ip requires it's retaining registers to be updated when a device is detached or attached. Signed-off-by: Ryan McClelland --- drivers/i3c/i3c_cdns.c | 286 +++++++++++++++++++---------------------- 1 file changed, 129 insertions(+), 157 deletions(-) diff --git a/drivers/i3c/i3c_cdns.c b/drivers/i3c/i3c_cdns.c index cefea4fbf9..b88bf927a8 100644 --- a/drivers/i3c/i3c_cdns.c +++ b/drivers/i3c/i3c_cdns.c @@ -444,20 +444,18 @@ struct cdns_i3c_xfer { /* Driver config */ struct cdns_i3c_config { + struct i3c_driver_config common; /** base address of the controller */ uintptr_t base; /** input frequency to the I3C Cadence */ uint32_t input_frequency; /** Interrupt configuration function. */ void (*irq_config_func)(const struct device *dev); - /** I3C/I2C device list struct. */ - struct i3c_dev_list device_list; }; /* Driver instance data */ struct cdns_i3c_data { - struct i3c_config_controller ctrl_config; - struct i3c_addr_slots addr_slots; + struct i3c_driver_data common; struct cdns_i3c_hw_config hw_cfg; struct k_mutex bus_lock; struct cdns_i3c_i2c_dev_data cdns_i3c_i2c_priv_data[I3C_MAX_DEVS]; @@ -642,7 +640,7 @@ static void cdns_i3c_set_prescalers(const struct device *dev) { struct cdns_i3c_data *data = dev->data; const struct cdns_i3c_config *config = dev->config; - struct i3c_config_controller *ctrl_config = &data->ctrl_config; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; /* These formulas are from section 6.2.1 of the Cadence I3C Master User Guide. */ uint32_t prescl_i3c = DIV_ROUND_UP(config->input_frequency, @@ -715,124 +713,25 @@ static uint32_t prepare_rr0_dev_address(uint16_t addr) /** * @brief Program Retaining Registers with device lists * - * This will reprogram all retaining registers with I3C devices, I2C devices, - * and the controller itself. + * This will program the retaining register with the controller itself * * @param dev Pointer to controller device driver instance. */ -static void cdns_i3c_program_retaining_regs(const struct device *dev) +static void cdns_i3c_program_controller_retaining_reg(const struct device *dev) { const struct cdns_i3c_config *config = dev->config; struct cdns_i3c_data *data = dev->data; - - /* Clear all retaining regs */ - sys_write32(DEVS_CTRL_DEV_CLR_ALL, config->base + DEVS_CTRL); - - uint32_t dev_id_rr0; - uint32_t dev_id_rr1; - uint32_t dev_id_rr2; - - /* program I2C devices */ - for (int i = 0; i < config->device_list.num_i2c; i++) { - struct i3c_i2c_device_desc *i2c_device = &(config->device_list.i2c[i]); - struct cdns_i3c_i2c_dev_data *cdns_i2c_device_data = i2c_device->controller_priv; - - if (cdns_i2c_device_data == NULL) { - LOG_ERR("%s: device not attached", dev->name); - } - - /* Mark the address as I2C device */ - i3c_addr_slots_mark_i2c(&data->addr_slots, i2c_device->addr); - - dev_id_rr0 = prepare_rr0_dev_address(i2c_device->addr); - dev_id_rr2 = DEV_ID_RR2_LVR(i2c_device->lvr); - - sys_write32(dev_id_rr0, config->base + DEV_ID_RR0(cdns_i2c_device_data->id)); - sys_write32(0, config->base + DEV_ID_RR1(cdns_i2c_device_data->id)); - sys_write32(dev_id_rr2, config->base + DEV_ID_RR2(cdns_i2c_device_data->id)); - - sys_write32(sys_read32(config->base + DEVS_CTRL) | - DEVS_CTRL_DEV_ACTIVE(cdns_i2c_device_data->id), - config->base + DEVS_CTRL); - } - - /* program I3C devices */ - for (int i = 0; i < config->device_list.num_i3c; i++) { - struct i3c_device_desc *i3c_device = &(config->device_list.i3c[i]); - struct cdns_i3c_i2c_dev_data *cdns_i3c_device_data = i3c_device->controller_priv; - - if (cdns_i3c_device_data == NULL) { - LOG_ERR("%s: %s: device not attached", dev->name, i3c_device->dev->name); - continue; - } - - /* - * Use the desired dynamic address as the new dynamic address - * if the slot is free. - */ - uint8_t dynamic_addr; - - if (i3c_device->init_dynamic_addr != 0U) { - /* initial dynamic address is requested */ - if (i3c_device->static_addr != 0) { - if (i3c_addr_slots_is_free(&data->addr_slots, - i3c_device->init_dynamic_addr)) { - /* Set DA during ENTDAA */ - dynamic_addr = i3c_device->init_dynamic_addr; - } else { - /* address is not free, get the next one */ - dynamic_addr = - i3c_addr_slots_next_free_find(&data->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 - */ - dynamic_addr = i3c_device->static_addr; - } - } else { - /* no init dynamic address is requested */ - if (i3c_device->static_addr != 0) { - /* static exists, set DA with same SA during SETDASA*/ - dynamic_addr = i3c_device->static_addr; - } else { - /* pick a DA to use */ - dynamic_addr = i3c_addr_slots_next_free_find(&data->addr_slots); - } - } - /* Mark the address as I3C device */ - i3c_addr_slots_mark_i3c(&data->addr_slots, dynamic_addr); - - dev_id_rr0 = DEV_ID_RR0_IS_I3C | prepare_rr0_dev_address(dynamic_addr); - dev_id_rr1 = DEV_ID_RR1_PID_MSB((i3c_device->pid & 0xFFFFFFFF0000) >> 16); - dev_id_rr2 = DEV_ID_RR2_PID_LSB(i3c_device->pid & 0xFFFF); - - sys_write32(dev_id_rr0, config->base + DEV_ID_RR0(cdns_i3c_device_data->id)); - sys_write32(dev_id_rr1, config->base + DEV_ID_RR1(cdns_i3c_device_data->id)); - sys_write32(dev_id_rr2, config->base + DEV_ID_RR2(cdns_i3c_device_data->id)); - - /** Mark Devices as active, devices that will be found and marked active during DAA, - * it will be given the exact DA programmed in it's RR if the PID matches and marked - * as active duing ENTDAA, otherwise they get set as active here - */ - if (i3c_device->static_addr != 0) { - sys_write32(sys_read32(config->base + DEVS_CTRL) | - DEVS_CTRL_DEV_ACTIVE(cdns_i3c_device_data->id), - config->base + DEVS_CTRL); - } - } - /* Set controller retaining register */ uint8_t controller_da = I3C_CONTROLLER_ADDR; - if (!i3c_addr_slots_is_free(&data->addr_slots, controller_da)) { - controller_da = i3c_addr_slots_next_free_find(&data->addr_slots); + if (!i3c_addr_slots_is_free(&data->common.attached_dev.addr_slots, controller_da)) { + controller_da = + i3c_addr_slots_next_free_find(&data->common.attached_dev.addr_slots); LOG_DBG("%s: 0x%02x DA selected for controller", dev->name, controller_da); } sys_write32(prepare_rr0_dev_address(controller_da), config->base + DEV_ID_RR0(0)); /* Mark the address as I3C device */ - i3c_addr_slots_mark_i3c(&data->addr_slots, controller_da); + i3c_addr_slots_mark_i3c(&data->common.attached_dev.addr_slots, controller_da); } #ifdef CONFIG_I3C_USE_IBI @@ -918,7 +817,7 @@ static int cdns_i3c_target_ibi_raise_hj(const struct device *dev) { const struct cdns_i3c_config *config = dev->config; struct cdns_i3c_data *data = dev->data; - struct i3c_config_controller *ctrl_config = &data->ctrl_config; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; /* HJ requests should not be done by primary controllers */ if (!ctrl_config->is_secondary) { @@ -1199,7 +1098,7 @@ static int cdns_i3c_do_daa(const struct device *dev) { struct cdns_i3c_data *data = dev->data; const struct cdns_i3c_config *config = dev->config; - struct i3c_config_controller *ctrl_config = &data->ctrl_config; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; /* DAA should not be done by secondary controllers */ if (ctrl_config->is_secondary) { @@ -1251,7 +1150,8 @@ static int cdns_i3c_do_daa(const struct device *dev) LOG_INF("%s: PID 0x%012llx is not in registered device " "list, given DA 0x%02x", dev->name, pid, dyn_addr); - i3c_addr_slots_mark_i3c(&data->addr_slots, dyn_addr); + i3c_addr_slots_mark_i3c( + &data->common.attached_dev.addr_slots, dyn_addr); } else { target->dynamic_addr = dyn_addr; target->bcr = bcr; @@ -1295,7 +1195,7 @@ static int cdns_i3c_do_daa(const struct device *dev) static int cdns_i3c_i2c_api_configure(const struct device *dev, uint32_t config) { struct cdns_i3c_data *data = dev->data; - struct i3c_config_controller *ctrl_config = &data->ctrl_config; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; switch (I2C_SPEED_GET(config)) { case I2C_SPEED_STANDARD: @@ -1344,8 +1244,8 @@ static int cdns_i3c_configure(const struct device *dev, enum i3c_config_type typ return -EINVAL; } - data->ctrl_config.scl.i3c = ctrl_cfg->scl.i3c; - data->ctrl_config.scl.i2c = ctrl_cfg->scl.i2c; + data->common.ctrl_config.scl.i3c = ctrl_cfg->scl.i3c; + data->common.ctrl_config.scl.i2c = ctrl_cfg->scl.i2c; cdns_i3c_set_prescalers(dev); return 0; @@ -1558,10 +1458,11 @@ static int cdns_i3c_master_get_rr_slot(const struct device *dev, uint8_t dyn_add return -EINVAL; } -static int cdns_i3c_attach_device(const struct device *dev, struct i3c_device_desc *desc) +static int cdns_i3c_attach_device(const struct device *dev, struct i3c_device_desc *desc, + uint8_t addr) { + const struct cdns_i3c_config *config = dev->config; struct cdns_i3c_data *data = dev->data; - int slot = cdns_i3c_master_get_rr_slot(dev, desc->dynamic_addr); if (slot < 0) { @@ -1575,6 +1476,24 @@ static int cdns_i3c_attach_device(const struct device *dev, struct i3c_device_de desc->controller_priv = &(data->cdns_i3c_i2c_priv_data[slot]); data->free_rr_slots &= ~BIT(slot); + uint32_t dev_id_rr0 = DEV_ID_RR0_IS_I3C | prepare_rr0_dev_address(addr); + uint32_t dev_id_rr1 = DEV_ID_RR1_PID_MSB((desc->pid & 0xFFFFFFFF0000) >> 16); + uint32_t dev_id_rr2 = DEV_ID_RR2_PID_LSB(desc->pid & 0xFFFF); + + sys_write32(dev_id_rr0, config->base + DEV_ID_RR0(slot)); + sys_write32(dev_id_rr1, config->base + DEV_ID_RR1(slot)); + sys_write32(dev_id_rr2, config->base + DEV_ID_RR2(slot)); + + /** Mark Devices as active, devices that will be found and marked active during DAA, + * it will be given the exact DA programmed in it's RR if the PID matches and marked + * as active duing ENTDAA, otherwise they get set as active here. If dynamic address + * is set, then it assumed that it was already initialized by the primary controller. + */ + if ((desc->static_addr != 0) || (desc->dynamic_addr != 0)) { + sys_write32(sys_read32(config->base + DEVS_CTRL) | DEVS_CTRL_DEV_ACTIVE(slot), + config->base + DEVS_CTRL); + } + k_mutex_unlock(&data->bus_lock); return 0; @@ -1592,22 +1511,40 @@ static int cdns_i3c_reattach_device(const struct device *dev, struct i3c_device_ return -EINVAL; } - if (!i3c_addr_slots_is_free(&data->addr_slots, desc->dynamic_addr)) { - LOG_ERR("%s: %s: dynamic address 0x%02x is not free", dev->name, desc->dev->name, - desc->dynamic_addr); - return -EADDRNOTAVAIL; + k_mutex_lock(&data->bus_lock, K_FOREVER); + + uint32_t dev_id_rr0 = DEV_ID_RR0_IS_I3C | prepare_rr0_dev_address(desc->dynamic_addr); + uint32_t dev_id_rr1 = DEV_ID_RR1_PID_MSB((desc->pid & 0xFFFFFFFF0000) >> 16); + uint32_t dev_id_rr2 = DEV_ID_RR2_PID_LSB(desc->pid & 0xFFFF) | DEV_ID_RR2_BCR(desc->bcr) | + DEV_ID_RR2_DCR(desc->dcr); + + sys_write32(dev_id_rr0, config->base + DEV_ID_RR0(cdns_i3c_device_data->id)); + sys_write32(dev_id_rr1, config->base + DEV_ID_RR1(cdns_i3c_device_data->id)); + sys_write32(dev_id_rr2, config->base + DEV_ID_RR2(cdns_i3c_device_data->id)); + + k_mutex_unlock(&data->bus_lock); + + return 0; +} + +static int cdns_i3c_detach_device(const struct device *dev, struct i3c_device_desc *desc) +{ + const struct cdns_i3c_config *config = dev->config; + struct cdns_i3c_data *data = dev->data; + struct cdns_i3c_i2c_dev_data *cdns_i3c_device_data = desc->controller_priv; + + if (cdns_i3c_device_data == NULL) { + LOG_ERR("%s: %s: device not attached", dev->name, desc->dev->name); + return -EINVAL; } k_mutex_lock(&data->bus_lock, K_FOREVER); - uint32_t rr0 = DEV_ID_RR0_IS_I3C | prepare_rr0_dev_address(desc->dynamic_addr); - - if (old_dyn_addr) { - /* mark the old address as free */ - i3c_addr_slots_mark_free(&data->addr_slots, old_dyn_addr); - } - sys_write32(rr0, config->base + DEV_ID_RR0(cdns_i3c_device_data->id)); - i3c_addr_slots_mark_i3c(&data->addr_slots, desc->dynamic_addr); + sys_write32(sys_read32(config->base + DEVS_CTRL) | + DEVS_CTRL_DEV_CLR(cdns_i3c_device_data->id), + config->base + DEVS_CTRL); + data->free_rr_slots |= BIT(cdns_i3c_device_data->id); + desc->controller_priv = NULL; k_mutex_unlock(&data->bus_lock); @@ -1616,6 +1553,7 @@ static int cdns_i3c_reattach_device(const struct device *dev, struct i3c_device_ static int cdns_i3c_i2c_attach_device(const struct device *dev, struct i3c_i2c_device_desc *desc) { + const struct cdns_i3c_config *config = dev->config; struct cdns_i3c_data *data = dev->data; int slot = cdns_i3c_master_get_rr_slot(dev, 0); @@ -1627,10 +1565,44 @@ static int cdns_i3c_i2c_attach_device(const struct device *dev, struct i3c_i2c_d k_mutex_lock(&data->bus_lock, K_FOREVER); + uint32_t dev_id_rr0 = prepare_rr0_dev_address(desc->addr); + uint32_t dev_id_rr2 = DEV_ID_RR2_LVR(desc->lvr); + + sys_write32(dev_id_rr0, config->base + DEV_ID_RR0(slot)); + sys_write32(0, config->base + DEV_ID_RR1(slot)); + sys_write32(dev_id_rr2, config->base + DEV_ID_RR2(slot)); + data->cdns_i3c_i2c_priv_data[slot].id = slot; desc->controller_priv = &(data->cdns_i3c_i2c_priv_data[slot]); data->free_rr_slots &= ~BIT(slot); + sys_write32(sys_read32(config->base + DEVS_CTRL) | DEVS_CTRL_DEV_ACTIVE(slot), + config->base + DEVS_CTRL); + + k_mutex_unlock(&data->bus_lock); + + return 0; +} + +static int cdns_i3c_i2c_detach_device(const struct device *dev, struct i3c_i2c_device_desc *desc) +{ + const struct cdns_i3c_config *config = dev->config; + struct cdns_i3c_data *data = dev->data; + struct cdns_i3c_i2c_dev_data *cdns_i2c_device_data = desc->controller_priv; + + if (cdns_i2c_device_data == NULL) { + LOG_ERR("%s: device not attached", dev->name); + return -EINVAL; + } + + k_mutex_lock(&data->bus_lock, K_FOREVER); + + sys_write32(sys_read32(config->base + DEVS_CTRL) | + DEVS_CTRL_DEV_CLR(cdns_i2c_device_data->id), + config->base + DEVS_CTRL); + data->free_rr_slots |= BIT(cdns_i2c_device_data->id); + desc->controller_priv = NULL; + k_mutex_unlock(&data->bus_lock); return 0; @@ -1752,6 +1724,7 @@ static int cdns_i3c_transfer(const struct device *dev, struct i3c_device_desc *t static void cdns_i3c_handle_ibi(const struct device *dev, uint32_t ibir) { const struct cdns_i3c_config *config = dev->config; + struct cdns_i3c_data *data = dev->data; uint8_t ibi_data[CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE]; @@ -1767,7 +1740,8 @@ static void cdns_i3c_handle_ibi(const struct device *dev, uint32_t ibir) uint32_t dev_id_rr0 = sys_read32(config->base + DEV_ID_RR0(slave_id + 1)); uint8_t dyn_addr = DEV_ID_RR0_GET_DEV_ADDR(dev_id_rr0); - struct i3c_device_desc *desc = i3c_dev_list_i3c_addr_find(&config->device_list, dyn_addr); + struct i3c_device_desc *desc = + i3c_dev_list_i3c_addr_find(&data->common.attached_dev, dyn_addr); /* * Check for NAK or error conditions. @@ -2079,7 +2053,7 @@ static int cdns_i3c_config_get(const struct device *dev, enum i3c_config_type ty 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; @@ -2195,7 +2169,7 @@ static struct i3c_device_desc *cdns_i3c_device_find(const struct device *dev, { const struct cdns_i3c_config *config = dev->config; - return i3c_dev_list_find(&config->device_list, id); + return i3c_dev_list_find(&config->common.dev_list, id); } /** @@ -2213,9 +2187,9 @@ static struct i3c_device_desc *cdns_i3c_device_find(const struct device *dev, */ static struct i3c_i2c_device_desc *cdns_i3c_i2c_device_find(const struct device *dev, uint16_t addr) { - const struct cdns_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); } /** @@ -2314,13 +2288,10 @@ static int cdns_i3c_bus_init(const struct device *dev) { struct cdns_i3c_data *data = dev->data; const struct cdns_i3c_config *config = dev->config; - struct i3c_config_controller *ctrl_config = &data->ctrl_config; + struct i3c_config_controller *ctrl_config = &data->common.ctrl_config; - int ret = i3c_addr_slots_init(&data->addr_slots, &config->device_list); - - if (ret != 0) { - return ret; - } + /* Clear all retaining regs */ + sys_write32(DEVS_CTRL_DEV_CLR_ALL, config->base + DEVS_CTRL); uint32_t conf0 = sys_read32(config->base + CONF_STATUS0); @@ -2346,7 +2317,7 @@ static int cdns_i3c_bus_init(const struct device *dev) /* determine prescaler timings for i3c and i2c scl */ cdns_i3c_set_prescalers(dev); - enum i3c_bus_mode mode = i3c_bus_mode(&config->device_list); + enum i3c_bus_mode mode = i3c_bus_mode(&config->common.dev_list); LOG_DBG("%s: i3c bus mode %d", dev->name, mode); int cdns_mode; @@ -2418,22 +2389,19 @@ static int cdns_i3c_bus_init(const struct device *dev) sys_write32(MST_INT_IBIR_THR | MST_INT_RX_UNF | MST_INT_HALTED | MST_INT_TX_OVF, config->base + MST_IER); - /* attach i3c devices */ - for (int i = 0; i < config->device_list.num_i3c; i++) { - cdns_i3c_attach_device(dev, &config->device_list.i3c[i]); - } - /* attach i2c devices */ - for (int i = 0; i < config->device_list.num_i2c; i++) { - cdns_i3c_i2c_attach_device(dev, &config->device_list.i2c[i]); + int ret = i3c_addr_slots_init(dev); + + if (ret != 0) { + return ret; } /* Program retaining regs. */ - cdns_i3c_program_retaining_regs(dev); + cdns_i3c_program_controller_retaining_reg(dev); /* only primary controllers are responsible for initializing the bus */ if (!ctrl_config->is_secondary) { /* Perform bus initialization */ - ret = i3c_bus_init(dev, &config->device_list); + ret = i3c_bus_init(dev, &config->common.dev_list); /* Bus Initialization Complete, allow HJ ACKs */ sys_write32(CTRL_HJ_ACK | sys_read32(config->base + CTRL), config->base + CTRL); } @@ -2448,7 +2416,11 @@ static struct i3c_driver_api api = { .configure = cdns_i3c_configure, .config_get = cdns_i3c_config_get, + .attach_i3c_device = cdns_i3c_attach_device, .reattach_i3c_device = cdns_i3c_reattach_device, + .detach_i3c_device = cdns_i3c_detach_device, + .attach_i2c_device = cdns_i3c_i2c_attach_device, + .detach_i2c_device = cdns_i3c_i2c_detach_device, .do_daa = cdns_i3c_do_daa, .do_ccc = cdns_i3c_do_ccc, @@ -2477,14 +2449,14 @@ static struct i3c_driver_api api = { .base = DT_INST_REG_ADDR(n), \ .input_frequency = DT_INST_PROP(n, input_clock_frequency), \ .irq_config_func = cdns_i3c_config_func_##n, \ - .device_list.i3c = cdns_i3c_device_array_##n, \ - .device_list.num_i3c = ARRAY_SIZE(cdns_i3c_device_array_##n), \ - .device_list.i2c = cdns_i3c_i2c_device_array_##n, \ - .device_list.num_i2c = ARRAY_SIZE(cdns_i3c_i2c_device_array_##n), \ + .common.dev_list.i3c = cdns_i3c_device_array_##n, \ + .common.dev_list.num_i3c = ARRAY_SIZE(cdns_i3c_device_array_##n), \ + .common.dev_list.i2c = cdns_i3c_i2c_device_array_##n, \ + .common.dev_list.num_i2c = ARRAY_SIZE(cdns_i3c_i2c_device_array_##n), \ }; \ static struct cdns_i3c_data i3c_data_##n = { \ - .ctrl_config.scl.i3c = DT_INST_PROP_OR(n, i3c_scl_hz, 0), \ - .ctrl_config.scl.i2c = DT_INST_PROP_OR(n, i2c_scl_hz, 0), \ + .common.ctrl_config.scl.i3c = DT_INST_PROP_OR(n, i3c_scl_hz, 0), \ + .common.ctrl_config.scl.i2c = DT_INST_PROP_OR(n, i2c_scl_hz, 0), \ }; \ DEVICE_DT_INST_DEFINE(n, cdns_i3c_bus_init, NULL, &i3c_data_##n, &i3c_config_##n, \ POST_KERNEL, CONFIG_I3C_CONTROLLER_INIT_PRIORITY, &api); \