Skip to content

Commit b818a76

Browse files
xodus7kartben
authored andcommitted
drivers: i3c: i3c_dw: dynamically attach I2C devices on transfer
The maximum number of attached devices is limited by the maximum number of entries in the device address table. For I3C devices these have to by allocated permanently when devices are attached but for I2C devices we can dynamically allocate an entry in the table on a I2C transfer and free it after the transfer is completed. This allows the maximum number of I2C + I3C devices on a bus to be larger than the address table size as long as the number of I3C devices is maxdevs - 1. Signed-off-by: Corey Wharton <[email protected]>
1 parent e330a69 commit b818a76

File tree

1 file changed

+51
-85
lines changed

1 file changed

+51
-85
lines changed

drivers/i3c/i3c_dw.c

Lines changed: 51 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -672,33 +672,6 @@ static int get_i3c_addr_pos(const struct device *dev, uint8_t addr, bool sa)
672672
return dw_i3c_device_data->id;
673673
}
674674

675-
/**
676-
* @brief Get the position of an I2C device with the specified address.
677-
*
678-
* This function retrieves the position (ID) of an I2C device with the specified
679-
* address on the I3C bus associated with the provided I3C device structure. This
680-
* utilizes the controller private data for where the id reg is stored.
681-
*
682-
* @param dev Pointer to the I3C device structure.
683-
* @param addr I2C address of the device whose position is to be retrieved.
684-
*
685-
* @return The position (ID) of the device on success, or a negative error code
686-
* if the device with the given address is not found.
687-
*/
688-
static int get_i2c_addr_pos(const struct device *dev, uint16_t addr)
689-
{
690-
struct dw_i3c_i2c_dev_data *dw_i3c_device_data;
691-
struct i3c_i2c_device_desc *desc = i3c_dev_list_i2c_addr_find(dev, addr);
692-
693-
if (desc == NULL) {
694-
return -ENODEV;
695-
}
696-
697-
dw_i3c_device_data = desc->controller_priv;
698-
699-
return dw_i3c_device_data->id;
700-
}
701-
702675
/**
703676
* @brief Transfer messages in I3C mode.
704677
*
@@ -891,6 +864,41 @@ static int dw_i3c_xfers(const struct device *dev, struct i3c_device_desc *target
891864
return ret;
892865
}
893866

867+
static int dw_i3c_i2c_attach_device(const struct device *dev, struct i3c_i2c_device_desc *desc)
868+
{
869+
const struct dw_i3c_config *config = dev->config;
870+
struct dw_i3c_data *data = dev->data;
871+
uint8_t pos;
872+
873+
pos = get_free_pos(data->free_pos);
874+
if (pos < 0) {
875+
return -ENOSPC;
876+
}
877+
878+
data->dw_i3c_i2c_priv_data[pos].id = pos;
879+
desc->controller_priv = &(data->dw_i3c_i2c_priv_data[pos]);
880+
data->free_pos &= ~BIT(pos);
881+
882+
sys_write32(DEV_ADDR_TABLE_LEGACY_I2C_DEV | DEV_ADDR_TABLE_STATIC_ADDR(desc->addr),
883+
config->regs + DEV_ADDR_TABLE_LOC(data->datstartaddr, pos));
884+
885+
return 0;
886+
}
887+
888+
static void dw_i3c_i2c_detach_device(const struct device *dev, struct i3c_i2c_device_desc *desc)
889+
{
890+
const struct dw_i3c_config *config = dev->config;
891+
struct dw_i3c_data *data = dev->data;
892+
struct dw_i3c_i2c_dev_data *dw_i2c_device_data = desc->controller_priv;
893+
894+
__ASSERT_NO_MSG(dw_i2c_device_data != NULL);
895+
896+
sys_write32(0,
897+
config->regs + DEV_ADDR_TABLE_LOC(data->datstartaddr, dw_i2c_device_data->id));
898+
data->free_pos |= BIT(dw_i2c_device_data->id);
899+
desc->controller_priv = NULL;
900+
}
901+
894902
/**
895903
* @brief Transfer messages in I2C mode.
896904
*
@@ -921,12 +929,6 @@ static int dw_i3c_i2c_transfer(const struct device *dev, struct i3c_i2c_device_d
921929
return -ENOTSUP;
922930
}
923931

924-
pos = get_i2c_addr_pos(dev, target->addr);
925-
if (pos < 0) {
926-
LOG_ERR("%s: Invalid slave device", dev->name);
927-
return -EINVAL;
928-
}
929-
930932
for (i = 0; i < num_msgs; i++) {
931933
if (msgs[i].flags & I2C_MSG_READ) {
932934
nrxwords += DIV_ROUND_UP(msgs[i].len, 4);
@@ -947,6 +949,17 @@ static int dw_i3c_i2c_transfer(const struct device *dev, struct i3c_i2c_device_d
947949

948950
pm_device_busy_set(dev);
949951

952+
/* In order limit the number of retaining registers occupied by connected devices,
953+
* I2C devices are only configured during transfers. This allows the number of devices
954+
* to be larger than the number of retaining registers on mixed buses.
955+
*/
956+
ret = dw_i3c_i2c_attach_device(dev, target);
957+
if (ret != 0) {
958+
LOG_ERR("%s: Failed to attach I2C device (%d)", dev->name, ret);
959+
goto error_attach;
960+
}
961+
pos = ((struct dw_i3c_i2c_dev_data *)target->controller_priv)->id;
962+
950963
memset(xfer, 0, sizeof(struct dw_i3c_xfer));
951964

952965
xfer->ncmds = num_msgs;
@@ -1002,6 +1015,8 @@ static int dw_i3c_i2c_transfer(const struct device *dev, struct i3c_i2c_device_d
10021015
ret = xfer->ret;
10031016

10041017
error:
1018+
dw_i3c_i2c_detach_device(dev, target);
1019+
error_attach:
10051020
pm_device_busy_clear(dev);
10061021
k_mutex_unlock(&data->mt);
10071022

@@ -1032,25 +1047,22 @@ static struct i3c_i2c_device_desc *dw_i3c_i2c_device_find(const struct device *d
10321047
* @see i2c_transfer
10331048
*
10341049
* @param dev Pointer to device driver instance.
1035-
* @param target Pointer to target device descriptor.
10361050
* @param msgs Pointer to I2C messages.
10371051
* @param num_msgs Number of messages to transfers.
1052+
* @param addr Address of the I2C target device.
10381053
*
10391054
* @return @see i2c_transfer
10401055
*/
10411056
static int dw_i3c_i2c_api_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
10421057
uint16_t addr)
10431058
{
10441059
struct i3c_i2c_device_desc *i2c_dev = dw_i3c_i2c_device_find(dev, addr);
1045-
int ret;
10461060

10471061
if (i2c_dev == NULL) {
1048-
ret = -ENODEV;
1049-
} else {
1050-
ret = dw_i3c_i2c_transfer(dev, i2c_dev, msgs, num_msgs);
1062+
return -ENODEV;
10511063
}
10521064

1053-
return ret;
1065+
return dw_i3c_i2c_transfer(dev, i2c_dev, msgs, num_msgs);
10541066
}
10551067

10561068
#ifdef CONFIG_I3C_USE_IBI
@@ -1571,50 +1583,6 @@ static int dw_i3c_detach_device(const struct device *dev, struct i3c_device_desc
15711583
return 0;
15721584
}
15731585

1574-
static int dw_i3c_i2c_attach_device(const struct device *dev, struct i3c_i2c_device_desc *desc)
1575-
{
1576-
const struct dw_i3c_config *config = dev->config;
1577-
struct dw_i3c_data *data = dev->data;
1578-
uint8_t pos;
1579-
1580-
pos = get_free_pos(data->free_pos);
1581-
if (pos < 0) {
1582-
return -ENOSPC;
1583-
}
1584-
1585-
data->dw_i3c_i2c_priv_data[pos].id = pos;
1586-
desc->controller_priv = &(data->dw_i3c_i2c_priv_data[pos]);
1587-
data->free_pos &= ~BIT(pos);
1588-
1589-
sys_write32(DEV_ADDR_TABLE_LEGACY_I2C_DEV | DEV_ADDR_TABLE_STATIC_ADDR(desc->addr),
1590-
config->regs + DEV_ADDR_TABLE_LOC(data->datstartaddr, pos));
1591-
1592-
return 0;
1593-
}
1594-
1595-
static int dw_i3c_i2c_detach_device(const struct device *dev, struct i3c_i2c_device_desc *desc)
1596-
{
1597-
const struct dw_i3c_config *config = dev->config;
1598-
struct dw_i3c_data *data = dev->data;
1599-
struct dw_i3c_i2c_dev_data *dw_i2c_device_data = desc->controller_priv;
1600-
1601-
if (dw_i2c_device_data == NULL) {
1602-
LOG_ERR("%s: device not attached", dev->name);
1603-
return -EINVAL;
1604-
}
1605-
1606-
k_mutex_lock(&data->mt, K_FOREVER);
1607-
1608-
sys_write32(0,
1609-
config->regs + DEV_ADDR_TABLE_LOC(data->datstartaddr, dw_i2c_device_data->id));
1610-
data->free_pos |= BIT(dw_i2c_device_data->id);
1611-
desc->controller_priv = NULL;
1612-
1613-
k_mutex_unlock(&data->mt);
1614-
1615-
return 0;
1616-
}
1617-
16181586
static int set_controller_info(const struct device *dev)
16191587
{
16201588
const struct dw_i3c_config *config = dev->config;
@@ -2389,8 +2357,6 @@ static DEVICE_API(i3c, dw_i3c_api) = {
23892357
.attach_i3c_device = dw_i3c_attach_device,
23902358
.reattach_i3c_device = dw_i3c_reattach_device,
23912359
.detach_i3c_device = dw_i3c_detach_device,
2392-
.attach_i2c_device = dw_i3c_i2c_attach_device,
2393-
.detach_i2c_device = dw_i3c_i2c_detach_device,
23942360

23952361
.do_daa = dw_i3c_do_daa,
23962362
.do_ccc = dw_i3c_do_ccc,

0 commit comments

Comments
 (0)