[PATCH 2/8] i3c: mipi-i3c-hci: Support address-indexed DAT slots
Billy Tsai
billy_tsai at aspeedtech.com
Tue Sep 1 04:35:29 PDT 2026
HCI v1's DEV_INDEX allocation is sequential: the driver picks any free
DAT slot, with no relationship between the slot number and the
device's I3C address. ASPEED's I3C controllers have long needed to
support more devices than the standard 5-bit DEV_INDEX field
(addresses 0-31) allows, so they use a layout where the DAT slot
number is itself the device's address, requiring DEV_INDEX to widen
to 7 bits (addresses 0-127).
Add HCI_QUIRK_DAT_INDEX_IS_ADDR for controllers with this layout:
allocate address-indexed DAT slots and emit the wider DEV_INDEX field.
Moving an entry on a dynamic address change can fail if its slot is
already taken, so set_dynamic_addr() now returns an error that the
reattach and DAA paths propagate. Controllers without the quirk keep
the existing behavior.
Signed-off-by: Billy Tsai <billy_tsai at aspeedtech.com>
Assisted-by: Claude:claude-fable-5
---
drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 35 ++++++++++++------
drivers/i3c/master/mipi-i3c-hci/core.c | 19 +++++++---
drivers/i3c/master/mipi-i3c-hci/dat.h | 5 ++-
drivers/i3c/master/mipi-i3c-hci/dat_v1.c | 63 +++++++++++++++++++++++++++++---
drivers/i3c/master/mipi-i3c-hci/hci.h | 1 +
5 files changed, 98 insertions(+), 25 deletions(-)
diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
index 3b9345718d273..9a11affb14bf6 100644
--- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
@@ -141,6 +141,14 @@ static enum hci_cmd_mode get_i2c_mode(struct i3c_hci *hci)
return MODE_I2C_Fm;
}
+static u32 cmd_v1_dev_index(struct i3c_hci *hci, unsigned int dat_idx)
+{
+ u32 mask = hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR ?
+ W0_MASK(22, 16) : W0_MASK(20, 16);
+
+ return field_prep(mask, dat_idx);
+}
+
static void fill_data_bytes(struct hci_xfer *xfer, u8 *data,
unsigned int data_len)
{
@@ -195,7 +203,7 @@ static int hci_cmd_v1_prep_ccc(struct i3c_hci *hci,
CMD_0_ATTR_I |
CMD_I0_TID(xfer->cmd_tid) |
CMD_I0_CMD(ccc_cmd) | CMD_I0_CP |
- CMD_I0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_I0_DTT(data_len) |
CMD_I0_MODE(mode);
fill_data_bytes(xfer, data, data_len);
@@ -205,7 +213,7 @@ static int hci_cmd_v1_prep_ccc(struct i3c_hci *hci,
CMD_0_ATTR_R |
CMD_R0_TID(xfer->cmd_tid) |
CMD_R0_CMD(ccc_cmd) | CMD_R0_CP |
- CMD_R0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_R0_MODE(mode) |
(rnw ? CMD_R0_RNW : 0);
xfer->cmd_desc[1] =
@@ -233,7 +241,7 @@ static void hci_cmd_v1_prep_i3c_xfer(struct i3c_hci *hci,
xfer->cmd_desc[0] =
CMD_0_ATTR_I |
CMD_I0_TID(xfer->cmd_tid) |
- CMD_I0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_I0_DTT(data_len) |
CMD_I0_MODE(mode);
fill_data_bytes(xfer, data, data_len);
@@ -242,7 +250,7 @@ static void hci_cmd_v1_prep_i3c_xfer(struct i3c_hci *hci,
xfer->cmd_desc[0] =
CMD_0_ATTR_R |
CMD_R0_TID(xfer->cmd_tid) |
- CMD_R0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_R0_MODE(mode) |
(rnw ? CMD_R0_RNW : 0);
xfer->cmd_desc[1] =
@@ -268,7 +276,7 @@ static void hci_cmd_v1_prep_i2c_xfer(struct i3c_hci *hci,
xfer->cmd_desc[0] =
CMD_0_ATTR_I |
CMD_I0_TID(xfer->cmd_tid) |
- CMD_I0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_I0_DTT(data_len) |
CMD_I0_MODE(mode);
fill_data_bytes(xfer, data, data_len);
@@ -277,7 +285,7 @@ static void hci_cmd_v1_prep_i2c_xfer(struct i3c_hci *hci,
xfer->cmd_desc[0] =
CMD_0_ATTR_R |
CMD_R0_TID(xfer->cmd_tid) |
- CMD_R0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_R0_MODE(mode) |
(rnw ? CMD_R0_RNW : 0);
xfer->cmd_desc[1] =
@@ -306,19 +314,22 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
* Yes, there is room for improvements.
*/
for (;;) {
- ret = mipi_i3c_hci_dat_v1.alloc_entry(hci);
- if (ret < 0)
- break;
- dat_idx = ret;
ret = i3c_master_get_free_addr(&hci->master, next_addr);
if (ret < 0)
break;
next_addr = ret;
+ ret = mipi_i3c_hci_dat_v1.alloc_entry(hci, next_addr);
+ if (ret < 0)
+ break;
+ dat_idx = ret;
dev_dbg(&hci->master.dev,
"next_addr = 0x%02x, DAA using DAT %d",
next_addr, dat_idx);
- mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx, next_addr);
+ ret = mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx,
+ next_addr);
+ if (ret < 0)
+ break;
mipi_i3c_hci_dct_index_reset(hci);
xfer->cmd_tid = hci_get_tid();
@@ -326,7 +337,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
CMD_0_ATTR_A |
CMD_A0_TID(xfer->cmd_tid) |
CMD_A0_CMD(I3C_CCC_ENTDAA) |
- CMD_A0_DEV_INDEX(dat_idx) |
+ cmd_v1_dev_index(hci, dat_idx) |
CMD_A0_DEV_COUNT(1) |
CMD_A0_ROC | CMD_A0_TOC;
xfer->cmd_desc[1] = 0;
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index dadf049bd4b54..a624e3c40484e 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -574,7 +574,8 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev)
if (!dev_data)
return -ENOMEM;
if (hci->cmd == &mipi_i3c_hci_cmd_v1) {
- ret = mipi_i3c_hci_dat_v1.alloc_entry(hci);
+ ret = mipi_i3c_hci_dat_v1.alloc_entry(hci,
+ dev->info.dyn_addr ?: dev->info.static_addr);
if (ret < 0) {
kfree(dev_data);
return ret;
@@ -593,9 +594,17 @@ static int i3c_hci_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)
struct i3c_hci *hci = to_i3c_hci(m);
struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
- if (hci->cmd == &mipi_i3c_hci_cmd_v1)
- mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dev_data->dat_idx,
- dev->info.dyn_addr);
+ if (hci->cmd == &mipi_i3c_hci_cmd_v1) {
+ int ret;
+
+ ret = mipi_i3c_hci_dat_v1.set_dynamic_addr(hci,
+ dev_data->dat_idx,
+ dev->info.dyn_addr);
+ if (ret)
+ return ret;
+ if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR)
+ dev_data->dat_idx = dev->info.dyn_addr;
+ }
return 0;
}
@@ -623,7 +632,7 @@ static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *dev)
dev_data = kzalloc_obj(*dev_data);
if (!dev_data)
return -ENOMEM;
- ret = mipi_i3c_hci_dat_v1.alloc_entry(hci);
+ ret = mipi_i3c_hci_dat_v1.alloc_entry(hci, dev->addr);
if (ret < 0) {
kfree(dev_data);
return ret;
diff --git a/drivers/i3c/master/mipi-i3c-hci/dat.h b/drivers/i3c/master/mipi-i3c-hci/dat.h
index 6881f19da77f2..d4c7b03724d37 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dat.h
+++ b/drivers/i3c/master/mipi-i3c-hci/dat.h
@@ -14,12 +14,13 @@
#define DAT_0_I2C_DEVICE W0_BIT_(31)
#define DAT_0_SIR_REJECT W0_BIT_(13)
#define DAT_0_IBI_PAYLOAD W0_BIT_(12)
+#define HCI_DAT_ANY_ADDRESS ((unsigned int)-1)
struct hci_dat_ops {
int (*init)(struct i3c_hci *hci);
- int (*alloc_entry)(struct i3c_hci *hci);
+ int (*alloc_entry)(struct i3c_hci *hci, unsigned int address);
void (*free_entry)(struct i3c_hci *hci, unsigned int dat_idx);
- void (*set_dynamic_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 addr);
+ int (*set_dynamic_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 addr);
void (*set_static_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 addr);
void (*set_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 w1);
void (*clear_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 w1);
diff --git a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
index 852966aa20d98..19b0f72e4ac82 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
@@ -89,7 +89,7 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
return 0;
}
-static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
+static int hci_dat_v1_alloc_entry(struct i3c_hci *hci, unsigned int address)
{
unsigned int dat_idx;
int ret;
@@ -99,9 +99,17 @@ static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
if (ret)
return ret;
}
- dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
- if (dat_idx >= hci->DAT_entries)
- return -ENOENT;
+ if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) {
+ if (address >= hci->DAT_entries)
+ return -EINVAL;
+ if (test_bit_acquire(address, hci->DAT_data))
+ return -ENOENT;
+ dat_idx = address;
+ } else {
+ dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
+ if (dat_idx >= hci->DAT_entries)
+ return -ENOENT;
+ }
__set_bit(dat_idx, hci->DAT_data);
/* default flags */
@@ -118,9 +126,43 @@ static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
__clear_bit(dat_idx, hci->DAT_data);
}
-static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
- unsigned int dat_idx, u8 address)
+static int hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
+ unsigned int dat_idx, u8 address)
{
+ if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) {
+ u32 dat_w0, dat_w1;
+
+ /*
+ * The controller derives the device address from the DAT slot
+ * index, so DAT_0_DYNAMIC_ADDRESS is never written: an address
+ * change moves the whole entry to the slot named by the new
+ * address instead.
+ */
+ if (dat_idx == address)
+ return 0;
+ if (address >= hci->DAT_entries) {
+ dev_err(&hci->master.dev, "invalid DAT slot %u\n", address);
+ return -EINVAL;
+ }
+ if (test_bit(address, hci->DAT_data)) {
+ dev_err(&hci->master.dev, "DAT slot %u already in use\n",
+ address);
+ return -EBUSY;
+ }
+
+ dat_w0 = dat_w0_read(dat_idx);
+ dat_w1 = dat_w1_read(dat_idx);
+
+ __set_bit(address, hci->DAT_data);
+ dat_w0_write(address, dat_w0);
+ dat_w1_write(address, dat_w1);
+
+ dat_w0_write(dat_idx, 0);
+ dat_w1_write(dat_idx, 0);
+ __clear_bit(dat_idx, hci->DAT_data);
+ return 0;
+ }
+
u32 dat_w0;
dat_w0 = dat_w0_read(dat_idx);
@@ -128,6 +170,8 @@ static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
(parity8(address) ? 0 : DAT_0_DYNADDR_PARITY);
dat_w0_write(dat_idx, dat_w0);
+
+ return 0;
}
static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,
@@ -169,6 +213,13 @@ static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,
static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
{
+ if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) {
+ if (dev_addr < hci->DAT_entries &&
+ test_bit(dev_addr, hci->DAT_data))
+ return dev_addr;
+ return -ENODEV;
+ }
+
unsigned int dat_idx;
u32 dat_w0;
diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
index ee73f6e6756a1..f50fc1e22a855 100644
--- a/drivers/i3c/master/mipi-i3c-hci/hci.h
+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
@@ -177,6 +177,7 @@ struct i3c_hci_dev_data {
#define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by parent device */
#define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET BIT(8) /* Do PIO queue SW resets after DMA abort */
#define HCI_QUIRK_DMA_REQUIRES_HC_ABORT BIT(9) /* Use HC_CONTROL ABORT to abort DMA */
+#define HCI_QUIRK_DAT_INDEX_IS_ADDR BIT(10) /* DAT entries are indexed by device address */
/* global functions */
void mipi_i3c_hci_resume(struct i3c_hci *hci);
--
2.34.1
More information about the linux-arm-kernel
mailing list