drivers: i3c: i3c attach/detach api
There are some needs to attach and reattach i3c/i2c devices at runtime Some I2C devices can have special registers where the address can be changed at runtime. Also some I3C devices can be powered off at runtime freeing up the address space they take up. These new APIs allow for these to be changed at runtime. This also moves some config/data in to a common i3c config/data structure which would allow the api to operate on to be common for all I3C drivers. Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
This commit is contained in:
parent
2f02d6ca81
commit
62f22f8d3b
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
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;
|
||||
|
|
|
@ -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), \
|
||||
|
|
|
@ -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,9 +1072,24 @@ 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,
|
||||
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.
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue