* [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA
@ 2026-06-08 7:57 Adrian Hunter
2026-06-08 7:57 ` [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() Adrian Hunter
` (6 more replies)
0 siblings, 7 replies; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
Hi
Patches 1-2 fix a use-after-free race in the MIPI I3C HCI driver's IBI
handling and make IBI teardown resilient to DISEC failures.
Patches 3-7 fix address management issues in the I3C core and HCI
driver that arise when Dynamic Address Assignment (DAA) does not complete
cleanly, culminating in a reconciliation step that detects and resolves
inconsistencies between assigned address slots and registered devices.
Patches are based on top of:
[PATCH V3 0/8] i3c: Hot-Join improvements and MIPI HCI Hot-Join support
https://lore.kernel.org/linux-i3c/20260608054312.10604-1-adrian.hunter@intel.com
which, in turn, applies on top of:
[PATCH V5 00/17] i3c: mipi-i3c-hci: DMA abort, recovery and related improvements
https://lore.kernel.org/linux-i3c/20260603090754.16252-1-adrian.hunter@intel.com
Changes in V2:
i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev()
Factor out __i3c_hci_disable_ibi() to facilitate also clearing
ibi_devs[dat_idx] upon IBI free, and update commit message
accordingly.
Demote a message in PIO and DMA IBI handling, and update commit
message accordingly.
i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs
Re-base due to changes in previous patch.
i3c: master: Prevent reuse of dynamic address on device add failure
Fix 'if (IS_ERR(newdev)' error path.
Be defensive and do not change the addr_slot_status if it is not
free, and update commit message accordingly.
Amend commit message to note removal of unnecesary 'if (!master)'
check.
Add Fixes tag.
i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA
None
i3c: master: Make i3c_master_add_i3c_dev_locked() return void
Re-base due to changes in previous patches.
i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked()
None
i3c: master: Reconcile dynamic addresses after DAA
Add bitmap.h include for bitmap_zero() etc.
Re-base due to changes in previous patches.
Adrian Hunter (7):
i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev()
i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs
i3c: master: Prevent reuse of dynamic address on device add failure
i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA
i3c: master: Make i3c_master_add_i3c_dev_locked() return void
i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked()
i3c: master: Reconcile dynamic addresses after DAA
drivers/i3c/master.c | 275 ++++++++++++++++++++++---------
drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 4 +-
drivers/i3c/master/mipi-i3c-hci/cmd_v2.c | 4 +-
drivers/i3c/master/mipi-i3c-hci/core.c | 45 ++++-
drivers/i3c/master/mipi-i3c-hci/dma.c | 7 +-
drivers/i3c/master/mipi-i3c-hci/hci.h | 1 +
drivers/i3c/master/mipi-i3c-hci/ibi.h | 13 +-
drivers/i3c/master/mipi-i3c-hci/pio.c | 7 +-
include/linux/i3c/master.h | 3 +-
9 files changed, 250 insertions(+), 109 deletions(-)
Regards
Adrian
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev()
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
@ 2026-06-08 7:57 ` Adrian Hunter
2026-06-08 17:31 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 2/7] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs Adrian Hunter
` (5 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
i3c_hci_addr_to_dev() walks bus->devs.i3c, which is protected by
bus.lock (rwsem). However, it is invoked from the MIPI I3C HCI IRQ
handler, which cannot take bus.lock. This allows concurrent device
addition/removal in the I3C core to modify the list while it is being
traversed, potentially leading to use-after-free or crashes.
Remove the dependency on the bus device list and introduce a dedicated
lookup table. Add an ibi_devs[] array indexed by DAT entry, maintained
under hci->lock. Update the array when IBIs are enabled or disabled,
so that it always reflects the set of devices allowed to generate IBIs.
Also update when IBIs are freed, to cover the corner case when an IBI is
freed without first being disabled (e.g. oldedev in
i3c_master_add_i3c_dev_locked()).
Move i3c_hci_addr_to_dev() into core.c, reimplement it using the new
array, and add a lockdep assertion to enforce that hci->lock is held
by callers.
Demote a message in PIO and DMA IBI handling, from an error to a debug
message, because there is a race window when the condition can arise
normally.
Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver")
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
Factor out __i3c_hci_disable_ibi() to facilitate also clearing
ibi_devs[dat_idx] upon IBI free, and update commit message
accordingly.
Demote a message in PIO and DMA IBI handling, and update commit
message accordingly.
drivers/i3c/master/mipi-i3c-hci/core.c | 37 ++++++++++++++++++++++++--
drivers/i3c/master/mipi-i3c-hci/dma.c | 7 +++--
drivers/i3c/master/mipi-i3c-hci/hci.h | 1 +
drivers/i3c/master/mipi-i3c-hci/ibi.h | 13 +--------
drivers/i3c/master/mipi-i3c-hci/pio.c | 7 +++--
5 files changed, 47 insertions(+), 18 deletions(-)
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index 53797841b63f..1e1f05aff092 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -22,6 +22,7 @@
#include "ext_caps.h"
#include "cmd.h"
#include "dat.h"
+#include "ibi.h"
/*
* Host Controller Capabilities and Operation Registers
@@ -124,6 +125,7 @@ static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci)
static int i3c_hci_bus_init(struct i3c_master_controller *m)
{
struct i3c_hci *hci = to_i3c_hci(m);
+ struct device *dev = hci->master.dev.parent;
struct i3c_device_info info;
int ret;
@@ -144,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
if (ret)
return ret;
+ hci->ibi_devs = devm_kcalloc(dev, hci->DAT_entries, sizeof(*hci->ibi_devs), GFP_KERNEL);
+ if (!hci->ibi_devs)
+ return -ENOMEM;
+
ret = hci->io->init(hci);
if (ret)
return ret;
@@ -639,14 +645,40 @@ static int i3c_hci_request_ibi(struct i3c_dev_desc *dev,
return hci->io->request_ibi(hci, dev, req);
}
+static void __i3c_hci_disable_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev)
+{
+ struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
+
+ mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
+ scoped_guard(spinlock_irqsave, &hci->lock)
+ hci->ibi_devs[dev_data->dat_idx] = NULL;
+}
+
static void i3c_hci_free_ibi(struct i3c_dev_desc *dev)
{
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct i3c_hci *hci = to_i3c_hci(m);
+ /* Must ensure the IBI has been disabled */
+ __i3c_hci_disable_ibi(hci, dev);
hci->io->free_ibi(hci, dev);
}
+struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr)
+{
+ int dat_idx;
+
+ lockdep_assert_held(&hci->lock);
+
+ for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
+ struct i3c_dev_desc *dev = hci->ibi_devs[dat_idx];
+
+ if (dev && dev->info.dyn_addr == addr)
+ return dev;
+ }
+ return NULL;
+}
+
static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev)
{
struct i3c_master_controller *m = i3c_dev_get_master(dev);
@@ -654,6 +686,8 @@ static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev)
struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
+ scoped_guard(spinlock_irqsave, &hci->lock)
+ hci->ibi_devs[dev_data->dat_idx] = dev;
return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
}
@@ -661,9 +695,8 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev)
{
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct i3c_hci *hci = to_i3c_hci(m);
- struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
- mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
+ __i3c_hci_disable_ibi(hci, dev);
return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
}
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index 87622d6f817e..0672ed1132f8 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -967,8 +967,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
dev = i3c_hci_addr_to_dev(hci, ibi_addr);
if (!dev) {
- dev_err(&hci->master.dev,
- "IBI for unknown device %#x\n", ibi_addr);
+ /*
+ * Either an IBI received just before IBI's were disabled, or
+ * the controller is broken. Assume the former.
+ */
+ dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi_addr);
goto done;
}
diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
index 41d31a53abd3..b3d9803b1968 100644
--- a/drivers/i3c/master/mipi-i3c-hci/hci.h
+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
@@ -65,6 +65,7 @@ struct i3c_hci {
unsigned int DAT_entry_size;
void *DAT_data;
struct dat_words *DAT;
+ struct i3c_dev_desc **ibi_devs;
unsigned int DCT_entries;
unsigned int DCT_entry_size;
u8 version_major;
diff --git a/drivers/i3c/master/mipi-i3c-hci/ibi.h b/drivers/i3c/master/mipi-i3c-hci/ibi.h
index e1f98e264da0..073ca67b7d04 100644
--- a/drivers/i3c/master/mipi-i3c-hci/ibi.h
+++ b/drivers/i3c/master/mipi-i3c-hci/ibi.h
@@ -26,17 +26,6 @@
#define IBI_DATA_LENGTH GENMASK(7, 0)
/* handy helpers */
-static inline struct i3c_dev_desc *
-i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr)
-{
- struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
- struct i3c_dev_desc *dev;
-
- i3c_bus_for_each_i3cdev(bus, dev) {
- if (dev->info.dyn_addr == addr)
- return dev;
- }
- return NULL;
-}
+struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr);
#endif
diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c
index b5ae1cfaa9e0..ff2657ee220b 100644
--- a/drivers/i3c/master/mipi-i3c-hci/pio.c
+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c
@@ -869,8 +869,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
dev = i3c_hci_addr_to_dev(hci, ibi->addr);
if (!dev) {
- dev_err(&hci->master.dev,
- "IBI for unknown device %#x\n", ibi->addr);
+ /*
+ * Either an IBI received just before IBI's were disabled, or
+ * the controller is broken. Assume the former.
+ */
+ dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi->addr);
return true;
}
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V2 2/7] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
2026-06-08 7:57 ` [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() Adrian Hunter
@ 2026-06-08 7:57 ` Adrian Hunter
2026-06-08 17:33 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 3/7] i3c: master: Prevent reuse of dynamic address on device add failure Adrian Hunter
` (4 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
Disabling IBIs currently returns the result of the DISEC CCC, causing
i3c_hci_disable_ibi() to fail if the transfer errors out.
However, the controller has already been programmed to reject IBIs by
setting DAT_0_SIR_REJECT, so the target’s IBIs are effectively disabled
from the host side regardless of the outcome of the DISEC command. At
this point, teardown of the IBI infrastructure can safely proceed even
if DISEC fails.
Note, from then on, the MIPI I3C HCI not only NACKs the target's IBI but
automatically sends another DISEC command.
Make i3c_hci_disable_ibi() resilient by ignoring the return value of
i3c_master_disec_locked() and always returning success.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
Re-base due to changes in previous patch.
drivers/i3c/master/mipi-i3c-hci/core.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index 1e1f05aff092..fffbc1775ef9 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -697,7 +697,13 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev)
struct i3c_hci *hci = to_i3c_hci(m);
__i3c_hci_disable_ibi(hci, dev);
- return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+ /*
+ * The DAT entry is now set to NACK and DISEC this target's IBIs, so
+ * the IBI teardown can proceed even if DISEC below fails, so ignore
+ * errors.
+ */
+ i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+ return 0;
}
static void i3c_hci_recycle_ibi_slot(struct i3c_dev_desc *dev,
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V2 3/7] i3c: master: Prevent reuse of dynamic address on device add failure
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
2026-06-08 7:57 ` [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() Adrian Hunter
2026-06-08 7:57 ` [PATCH V2 2/7] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs Adrian Hunter
@ 2026-06-08 7:57 ` Adrian Hunter
2026-06-08 17:45 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 4/7] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA Adrian Hunter
` (3 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
i3c_master_add_i3c_dev_locked() is called after a device has already
been assigned a dynamic address. If the function fails, the address
remains marked as free and may be reallocated to another device,
leading to address conflicts on the bus.
Ensure the address is not marked as free on failure, by updating the
address slot state to prevent the address from being re-used.
Emit an error message to inform of the failure.
Opportunistically remove the !master check because it is impossible.
Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure")
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
Fix 'if (IS_ERR(newdev)' error path.
Be defensive and do not change the addr_slot_status if it is not
free, and update commit message accordingly.
Amend commit message to note removal of unnecesary 'if (!master)'
check.
Add Fixes tag.
drivers/i3c/master.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index f87bf0099d3c..7b60b0c7f646 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2345,12 +2345,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
bool enable_ibi = false;
int ret;
- if (!master)
- return -EINVAL;
-
newdev = i3c_master_alloc_i3c_dev(master, &info);
- if (IS_ERR(newdev))
- return PTR_ERR(newdev);
+ if (IS_ERR(newdev)) {
+ ret = PTR_ERR(newdev);
+ goto err_prevent_addr_reuse;
+ }
ret = i3c_master_attach_i3c_dev(master, newdev);
if (ret)
@@ -2472,6 +2471,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
err_free_dev:
i3c_master_free_i3c_dev(newdev);
+err_prevent_addr_reuse:
+ /*
+ * Although the device has not been added, the address has been
+ * assigned. Prevent the address from being used again.
+ */
+ if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE)
+ i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV);
+
+ dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V2 4/7] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
` (2 preceding siblings ...)
2026-06-08 7:57 ` [PATCH V2 3/7] i3c: master: Prevent reuse of dynamic address on device add failure Adrian Hunter
@ 2026-06-08 7:57 ` Adrian Hunter
2026-06-08 17:48 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 5/7] i3c: master: Make i3c_master_add_i3c_dev_locked() return void Adrian Hunter
` (2 subsequent siblings)
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
i3c_master_add_i3c_dev_locked() no longer leaves the address marked as
free on failure, so aborting the DAA sequence on its error is unnecessary.
Failure to register a discovered device does not invalidate the entire
Dynamic Address Assignment (DAA) procedure. Align with the behavior of
other I3C master drivers by ignoring errors from
i3c_master_add_i3c_dev_locked() and continuing enumeration.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
None
drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 4 +---
drivers/i3c/master/mipi-i3c-hci/cmd_v2.c | 4 +---
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
index 75d452d7f6af..3b9345718d27 100644
--- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
@@ -358,9 +358,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
* TODO: Extend the subsystem layer to allow for registering
* new device and provide BCR/DCR/PID at the same time.
*/
- ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
- if (ret)
- break;
+ i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
}
if (dat_idx >= 0)
diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
index 39eec26a363c..8d93748e858d 100644
--- a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
@@ -296,9 +296,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci)
* TODO: Extend the subsystem layer to allow for registering
* new device and provide BCR/DCR/PID at the same time.
*/
- ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
- if (ret)
- break;
+ i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
}
hci_free_xfer(xfer, 2);
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V2 5/7] i3c: master: Make i3c_master_add_i3c_dev_locked() return void
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
` (3 preceding siblings ...)
2026-06-08 7:57 ` [PATCH V2 4/7] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA Adrian Hunter
@ 2026-06-08 7:57 ` Adrian Hunter
2026-06-08 17:51 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 6/7] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked() Adrian Hunter
2026-06-08 7:58 ` [PATCH V2 7/7] i3c: master: Reconcile dynamic addresses after DAA Adrian Hunter
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
The return value of i3c_master_add_i3c_dev_locked() is not used by any
caller, and callers are not in a position to recover from failures in
this path.
Change the function to return void. Amend the kernel-doc accordingly,
fix some grammar and remove a stale paragraph.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
Re-base due to changes in previous patches.
drivers/i3c/master.c | 17 ++++-------------
include/linux/i3c/master.h | 3 +--
2 files changed, 5 insertions(+), 15 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 7b60b0c7f646..57857a3351c7 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2324,19 +2324,12 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev)
* @master: master used to send frames on the bus
* @addr: I3C slave dynamic address assigned to the device
*
- * This function is instantiating an I3C device object and adding it to the
- * I3C device list. All device information are automatically retrieved using
- * standard CCC commands.
- *
- * The I3C device object is returned in case the master wants to attach
- * private data to it using i3c_dev_set_master_data().
+ * This function instantiates an I3C device object and adds it to the I3C device
+ * list. All device information is retrieved using standard CCC commands.
*
* This function must be called with the bus lock held in write mode.
- *
- * Return: a 0 in case of success, an negative error code otherwise.
*/
-int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
- u8 addr)
+void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr)
{
struct i3c_device_info info = { .dyn_addr = addr };
struct i3c_dev_desc *newdev, *olddev;
@@ -2460,7 +2453,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
mutex_unlock(&newdev->ibi_lock);
}
- return 0;
+ return;
err_detach_dev:
if (newdev->dev && newdev->dev->desc)
@@ -2480,8 +2473,6 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV);
dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret);
-
- return ret;
}
EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index e2c831fb5354..f73cede87d36 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -615,8 +615,7 @@ int i3c_master_defslvs_locked(struct i3c_master_controller *master);
int i3c_master_get_free_addr(struct i3c_master_controller *master,
u8 start_addr);
-int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
- u8 addr);
+void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr);
int i3c_master_do_daa(struct i3c_master_controller *master);
int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa);
struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V2 6/7] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked()
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
` (4 preceding siblings ...)
2026-06-08 7:57 ` [PATCH V2 5/7] i3c: master: Make i3c_master_add_i3c_dev_locked() return void Adrian Hunter
@ 2026-06-08 7:57 ` Adrian Hunter
2026-06-08 17:52 ` Frank Li
2026-06-08 7:58 ` [PATCH V2 7/7] i3c: master: Reconcile dynamic addresses after DAA Adrian Hunter
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:57 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
Relocate i3c_master_do_daa_ext() and i3c_master_do_daa() so they appear
after i3c_master_add_i3c_dev_locked().
This ordering is required for upcoming changes where the DAA flow will
(indirectly) rely on i3c_master_add_i3c_dev_locked() functionality.
Reordering avoids forward dependency issues and keeps related code paths
logically arranged.
No functional change.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
None
drivers/i3c/master.c | 128 +++++++++++++++++++++----------------------
1 file changed, 64 insertions(+), 64 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 57857a3351c7..5021d25b718a 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1870,70 +1870,6 @@ static void i3c_master_reg_work_fn(struct work_struct *work)
i3c_bus_normaluse_unlock(&master->bus);
}
-/**
- * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
- * @master: controller
- * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA)
- *
- * Perform Dynamic Address Assignment with optional support for System
- * Hibernation (@rstdaa is true).
- *
- * After System Hibernation, Dynamic Addresses can have been reassigned at boot
- * time to different values. A simple strategy is followed to handle that.
- * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA
- * procedure which has provision for reassigning addresses that differ from the
- * previously recorded addresses.
- *
- * Return: a 0 in case of success, an negative error code otherwise.
- */
-int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
-{
- int rstret = 0;
- int ret;
-
- ret = i3c_master_rpm_get(master);
- if (ret)
- return ret;
-
- i3c_bus_maintenance_lock(&master->bus);
-
- if (master->shutting_down) {
- ret = -ENODEV;
- } else {
- if (rstdaa)
- rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
- ret = master->ops->do_daa(master);
- }
-
- i3c_bus_maintenance_unlock(&master->bus);
-
- if (ret)
- goto out;
-
- queue_work(master->wq, &master->reg_work);
-out:
- i3c_master_rpm_put(master);
-
- return rstret ?: ret;
-}
-EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext);
-
-/**
- * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
- * @master: master doing the DAA
- *
- * This function instantiates I3C device objects and adds them to the
- * I3C device list. All device information is automatically retrieved using
- * standard CCC commands.
- *
- * Return: a 0 in case of success, an negative error code otherwise.
- */
-int i3c_master_do_daa(struct i3c_master_controller *master)
-{
- return i3c_master_do_daa_ext(master, false);
-}
-EXPORT_SYMBOL_GPL(i3c_master_do_daa);
-
/**
* i3c_master_dma_map_single() - Map buffer for single DMA transfer
* @dev: device object of a device doing DMA
@@ -2476,6 +2412,70 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr
}
EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
+/**
+ * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
+ * @master: controller
+ * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA)
+ *
+ * Perform Dynamic Address Assignment with optional support for System
+ * Hibernation (@rstdaa is true).
+ *
+ * After System Hibernation, Dynamic Addresses can have been reassigned at boot
+ * time to different values. A simple strategy is followed to handle that.
+ * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA
+ * procedure which has provision for reassigning addresses that differ from the
+ * previously recorded addresses.
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
+{
+ int rstret = 0;
+ int ret;
+
+ ret = i3c_master_rpm_get(master);
+ if (ret)
+ return ret;
+
+ i3c_bus_maintenance_lock(&master->bus);
+
+ if (master->shutting_down) {
+ ret = -ENODEV;
+ } else {
+ if (rstdaa)
+ rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
+ ret = master->ops->do_daa(master);
+ }
+
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ if (ret)
+ goto out;
+
+ queue_work(master->wq, &master->reg_work);
+out:
+ i3c_master_rpm_put(master);
+
+ return rstret ?: ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext);
+
+/**
+ * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
+ * @master: master doing the DAA
+ *
+ * This function instantiates I3C device objects and adds them to the
+ * I3C device list. All device information is automatically retrieved using
+ * standard CCC commands.
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_do_daa(struct i3c_master_controller *master)
+{
+ return i3c_master_do_daa_ext(master, false);
+}
+EXPORT_SYMBOL_GPL(i3c_master_do_daa);
+
#define OF_I3C_REG1_IS_I2C_DEV BIT(31)
static int
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V2 7/7] i3c: master: Reconcile dynamic addresses after DAA
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
` (5 preceding siblings ...)
2026-06-08 7:57 ` [PATCH V2 6/7] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked() Adrian Hunter
@ 2026-06-08 7:58 ` Adrian Hunter
2026-06-08 18:06 ` Frank Li
6 siblings, 1 reply; 15+ messages in thread
From: Adrian Hunter @ 2026-06-08 7:58 UTC (permalink / raw)
To: alexandre.belloni; +Cc: Frank.Li, linux-i3c, linux-kernel
After Dynamic Address Assignment (DAA), there may be cases where
devices have been assigned dynamic addresses on the bus, but are not
successfully registered in the device model. This can happen, for
example, if errors occur during device addition, leaving the bus state
and software state inconsistent.
Introduce a reconciliation step to resolve such inconsistencies.
Scan all address slots marked as I3C devices by the bus, and compare
them against the set of devices currently registered. For any dynamic
address that is marked occupied but has no corresponding i3c_dev_desc,
probe for device presence using a GETSTATUS CCC.
Retry the probe (with exponential backoff delay) to handle transient NACK
conditions. If a device responds, register it via
i3c_master_add_i3c_dev_locked(). Otherwise, free the address
slot so it may be reused in future DAA operations.
Note, i3c_master_add_i3c_dev_locked() may fail (again), in which case the
dynamic address remains marked as occupied. A future DAA will try again.
This also handles a corner case where a device is assigned a dynamic
address but not successfully added, and subsequently loses that address
(e.g. due to power management). If DAA is run again, the device may
receive a new dynamic address while the old one remains marked as
occupied. Repeated occurrences of this scenario could eventually
exhaust the dynamic address space. The reconciliation step ensures that
stale addresses are detected and freed, preventing address leakage.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes in V2:
Add bitmap.h include for bitmap_zero() etc.
Re-base due to changes in previous patches.
drivers/i3c/master.c | 115 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 113 insertions(+), 2 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 5021d25b718a..6656c8591fab 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -6,7 +6,9 @@
*/
#include <linux/atomic.h>
+#include <linux/bitmap.h>
#include <linux/bug.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -1613,6 +1615,56 @@ static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
return 0;
}
+static int i3c_master_getstatus_locked(struct i3c_master_controller *master,
+ u8 addr, u16 *status)
+{
+ struct i3c_ccc_getstatus *getstatus;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ getstatus = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*getstatus));
+ if (!getstatus)
+ return -ENOMEM;
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETSTATUS, &dest, 1);
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ goto out;
+
+ if (dest.payload.len != sizeof(*getstatus)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (status)
+ *status = be16_to_cpu(getstatus->status);
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+#define I3C_DEV_PROBE_INITIAL_DELAY_US 20
+#define I3C_DEV_PROBE_DELAY_FACTOR 2
+#define I3C_DEV_PROBE_CNT 5
+
+static bool i3c_master_i3c_dev_present(struct i3c_master_controller *master, unsigned int addr)
+{
+ int delay = I3C_DEV_PROBE_INITIAL_DELAY_US;
+
+ for (int i = 0; i < I3C_DEV_PROBE_CNT; i++) {
+ if (i) {
+ fsleep(delay);
+ delay *= I3C_DEV_PROBE_DELAY_FACTOR;
+ }
+ if (!i3c_master_getstatus_locked(master, addr, NULL))
+ return true;
+ }
+
+ return false;
+}
+
static void i3c_master_put_i3c_addrs(struct i3c_dev_desc *dev)
{
struct i3c_master_controller *master = i3c_dev_get_master(dev);
@@ -2256,22 +2308,25 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev)
}
/**
- * i3c_master_add_i3c_dev_locked() - add an I3C slave to the bus
+ * __i3c_master_add_i3c_dev_locked() - add an I3C slave to the bus
* @master: master used to send frames on the bus
* @addr: I3C slave dynamic address assigned to the device
+ * @probe: probe to see if the device is really present at @addr
*
* This function instantiates an I3C device object and adds it to the I3C device
* list. All device information is retrieved using standard CCC commands.
*
* This function must be called with the bus lock held in write mode.
*/
-void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr)
+static void __i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
+ u8 addr, bool probe)
{
struct i3c_device_info info = { .dyn_addr = addr };
struct i3c_dev_desc *newdev, *olddev;
u8 old_dyn_addr = addr, expected_dyn_addr;
struct i3c_ibi_setup ibireq = { };
bool enable_ibi = false;
+ bool no_dev = false;
int ret;
newdev = i3c_master_alloc_i3c_dev(master, &info);
@@ -2284,6 +2339,18 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr
if (ret)
goto err_free_dev;
+ /*
+ * When a dynamic address is first assigned, there is no need to check
+ * whether it is still assigned, however, if adding the device fails,
+ * it will be attempted again later, at which point the address may
+ * have been lost (e.g. due to power management), so for that case,
+ * probe to see if the device is still present at the assigned address.
+ */
+ if (probe && !i3c_master_i3c_dev_present(master, addr)) {
+ no_dev = true;
+ goto err_detach_dev;
+ }
+
ret = i3c_master_retrieve_dev_info(newdev);
if (ret)
goto err_detach_dev;
@@ -2401,6 +2468,8 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr
i3c_master_free_i3c_dev(newdev);
err_prevent_addr_reuse:
+ if (no_dev)
+ return;
/*
* Although the device has not been added, the address has been
* assigned. Prevent the address from being used again.
@@ -2410,8 +2479,45 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr
dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret);
}
+
+/**
+ * i3c_master_add_i3c_dev_locked() - add an I3C slave to the bus
+ * @master: master used to send frames on the bus
+ * @addr: I3C slave dynamic address assigned to the device
+ *
+ * This function instantiates an I3C device object and adds it to the
+ * I3C device list. All device information is automatically retrieved using
+ * standard CCC commands.
+ *
+ * This function must be called with the bus lock held in write mode.
+ */
+void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr)
+{
+ __i3c_master_add_i3c_dev_locked(master, addr, false);
+}
EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
+static void i3c_master_reconcile_dyn_addrs(struct i3c_master_controller *master)
+{
+ DECLARE_BITMAP(dev_dyn_addrs, I2C_MAX_ADDR + 1);
+ enum i3c_addr_slot_status status;
+ struct i3c_dev_desc *desc;
+
+ /* Mark all devices' dynamic addresses in the bitmap */
+ bitmap_zero(dev_dyn_addrs, I2C_MAX_ADDR + 1);
+ i3c_bus_for_each_i3cdev(&master->bus, desc)
+ __set_bit(desc->info.dyn_addr, dev_dyn_addrs);
+ /* Reconcile the bitmap with the bus address slot status */
+ for (unsigned int addr = 0; addr <= I2C_MAX_ADDR; addr++) {
+ status = i3c_bus_get_addr_slot_status(&master->bus, addr);
+ if (status != I3C_ADDR_SLOT_I3C_DEV || test_bit(addr, dev_dyn_addrs))
+ continue;
+ i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_FREE);
+ /* Try to add the device, but probe to see if it is really present */
+ __i3c_master_add_i3c_dev_locked(master, addr, true);
+ }
+}
+
/**
* i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
* @master: controller
@@ -2445,6 +2551,11 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
if (rstdaa)
rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
ret = master->ops->do_daa(master);
+ /*
+ * Handle cases where a dynamic address was assigned but the
+ * device was not successfully added.
+ */
+ i3c_master_reconcile_dyn_addrs(master);
}
i3c_bus_maintenance_unlock(&master->bus);
--
2.51.0
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev()
2026-06-08 7:57 ` [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() Adrian Hunter
@ 2026-06-08 17:31 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 17:31 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:57:54AM +0300, Adrian Hunter wrote:
> i3c_hci_addr_to_dev() walks bus->devs.i3c, which is protected by
> bus.lock (rwsem). However, it is invoked from the MIPI I3C HCI IRQ
> handler, which cannot take bus.lock. This allows concurrent device
> addition/removal in the I3C core to modify the list while it is being
> traversed, potentially leading to use-after-free or crashes.
>
> Remove the dependency on the bus device list and introduce a dedicated
> lookup table. Add an ibi_devs[] array indexed by DAT entry, maintained
> under hci->lock. Update the array when IBIs are enabled or disabled,
> so that it always reflects the set of devices allowed to generate IBIs.
> Also update when IBIs are freed, to cover the corner case when an IBI is
> freed without first being disabled (e.g. oldedev in
> i3c_master_add_i3c_dev_locked()).
>
> Move i3c_hci_addr_to_dev() into core.c, reimplement it using the new
> array, and add a lockdep assertion to enforce that hci->lock is held
> by callers.
>
> Demote a message in PIO and DMA IBI handling, from an error to a debug
> message, because there is a race window when the condition can arise
> normally.
>
> Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver")
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
>
> Changes in V2:
>
> Factor out __i3c_hci_disable_ibi() to facilitate also clearing
> ibi_devs[dat_idx] upon IBI free, and update commit message
> accordingly.
> Demote a message in PIO and DMA IBI handling, and update commit
> message accordingly.
>
>
> drivers/i3c/master/mipi-i3c-hci/core.c | 37 ++++++++++++++++++++++++--
> drivers/i3c/master/mipi-i3c-hci/dma.c | 7 +++--
> drivers/i3c/master/mipi-i3c-hci/hci.h | 1 +
> drivers/i3c/master/mipi-i3c-hci/ibi.h | 13 +--------
> drivers/i3c/master/mipi-i3c-hci/pio.c | 7 +++--
> 5 files changed, 47 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
> index 53797841b63f..1e1f05aff092 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/core.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/core.c
> @@ -22,6 +22,7 @@
> #include "ext_caps.h"
> #include "cmd.h"
> #include "dat.h"
> +#include "ibi.h"
>
> /*
> * Host Controller Capabilities and Operation Registers
> @@ -124,6 +125,7 @@ static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci)
> static int i3c_hci_bus_init(struct i3c_master_controller *m)
> {
> struct i3c_hci *hci = to_i3c_hci(m);
> + struct device *dev = hci->master.dev.parent;
> struct i3c_device_info info;
> int ret;
>
> @@ -144,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
> if (ret)
> return ret;
>
> + hci->ibi_devs = devm_kcalloc(dev, hci->DAT_entries, sizeof(*hci->ibi_devs), GFP_KERNEL);
> + if (!hci->ibi_devs)
> + return -ENOMEM;
> +
> ret = hci->io->init(hci);
> if (ret)
> return ret;
> @@ -639,14 +645,40 @@ static int i3c_hci_request_ibi(struct i3c_dev_desc *dev,
> return hci->io->request_ibi(hci, dev, req);
> }
>
> +static void __i3c_hci_disable_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev)
> +{
> + struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
> +
> + mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
> + scoped_guard(spinlock_irqsave, &hci->lock)
> + hci->ibi_devs[dev_data->dat_idx] = NULL;
> +}
> +
> static void i3c_hci_free_ibi(struct i3c_dev_desc *dev)
> {
> struct i3c_master_controller *m = i3c_dev_get_master(dev);
> struct i3c_hci *hci = to_i3c_hci(m);
>
> + /* Must ensure the IBI has been disabled */
> + __i3c_hci_disable_ibi(hci, dev);
> hci->io->free_ibi(hci, dev);
> }
>
> +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr)
> +{
> + int dat_idx;
> +
> + lockdep_assert_held(&hci->lock);
> +
> + for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
> + struct i3c_dev_desc *dev = hci->ibi_devs[dat_idx];
> +
> + if (dev && dev->info.dyn_addr == addr)
> + return dev;
> + }
> + return NULL;
> +}
> +
> static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev)
> {
> struct i3c_master_controller *m = i3c_dev_get_master(dev);
> @@ -654,6 +686,8 @@ static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev)
> struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
>
> mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
> + scoped_guard(spinlock_irqsave, &hci->lock)
> + hci->ibi_devs[dev_data->dat_idx] = dev;
> return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
> }
>
> @@ -661,9 +695,8 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev)
> {
> struct i3c_master_controller *m = i3c_dev_get_master(dev);
> struct i3c_hci *hci = to_i3c_hci(m);
> - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
>
> - mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
> + __i3c_hci_disable_ibi(hci, dev);
> return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
> }
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
> index 87622d6f817e..0672ed1132f8 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/dma.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
> @@ -967,8 +967,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
>
> dev = i3c_hci_addr_to_dev(hci, ibi_addr);
> if (!dev) {
> - dev_err(&hci->master.dev,
> - "IBI for unknown device %#x\n", ibi_addr);
> + /*
> + * Either an IBI received just before IBI's were disabled, or
> + * the controller is broken. Assume the former.
> + */
> + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi_addr);
> goto done;
> }
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
> index 41d31a53abd3..b3d9803b1968 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/hci.h
> +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
> @@ -65,6 +65,7 @@ struct i3c_hci {
> unsigned int DAT_entry_size;
> void *DAT_data;
> struct dat_words *DAT;
> + struct i3c_dev_desc **ibi_devs;
> unsigned int DCT_entries;
> unsigned int DCT_entry_size;
> u8 version_major;
> diff --git a/drivers/i3c/master/mipi-i3c-hci/ibi.h b/drivers/i3c/master/mipi-i3c-hci/ibi.h
> index e1f98e264da0..073ca67b7d04 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/ibi.h
> +++ b/drivers/i3c/master/mipi-i3c-hci/ibi.h
> @@ -26,17 +26,6 @@
> #define IBI_DATA_LENGTH GENMASK(7, 0)
>
> /* handy helpers */
> -static inline struct i3c_dev_desc *
> -i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr)
> -{
> - struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
> - struct i3c_dev_desc *dev;
> -
> - i3c_bus_for_each_i3cdev(bus, dev) {
> - if (dev->info.dyn_addr == addr)
> - return dev;
> - }
> - return NULL;
> -}
> +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr);
>
> #endif
> diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c
> index b5ae1cfaa9e0..ff2657ee220b 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/pio.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c
> @@ -869,8 +869,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
>
> dev = i3c_hci_addr_to_dev(hci, ibi->addr);
> if (!dev) {
> - dev_err(&hci->master.dev,
> - "IBI for unknown device %#x\n", ibi->addr);
> + /*
> + * Either an IBI received just before IBI's were disabled, or
> + * the controller is broken. Assume the former.
> + */
> + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi->addr);
> return true;
> }
>
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V2 2/7] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs
2026-06-08 7:57 ` [PATCH V2 2/7] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs Adrian Hunter
@ 2026-06-08 17:33 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 17:33 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:57:55AM +0300, Adrian Hunter wrote:
> Disabling IBIs currently returns the result of the DISEC CCC, causing
> i3c_hci_disable_ibi() to fail if the transfer errors out.
>
> However, the controller has already been programmed to reject IBIs by
> setting DAT_0_SIR_REJECT, so the target’s IBIs are effectively disabled
> from the host side regardless of the outcome of the DISEC command. At
> this point, teardown of the IBI infrastructure can safely proceed even
> if DISEC fails.
>
> Note, from then on, the MIPI I3C HCI not only NACKs the target's IBI but
> automatically sends another DISEC command.
>
> Make i3c_hci_disable_ibi() resilient by ignoring the return value of
> i3c_master_disec_locked() and always returning success.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
>
> Changes in V2:
>
> Re-base due to changes in previous patch.
>
>
> drivers/i3c/master/mipi-i3c-hci/core.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
> index 1e1f05aff092..fffbc1775ef9 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/core.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/core.c
> @@ -697,7 +697,13 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev)
> struct i3c_hci *hci = to_i3c_hci(m);
>
> __i3c_hci_disable_ibi(hci, dev);
> - return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
> + /*
> + * The DAT entry is now set to NACK and DISEC this target's IBIs, so
> + * the IBI teardown can proceed even if DISEC below fails, so ignore
> + * errors.
> + */
> + i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
> + return 0;
> }
>
> static void i3c_hci_recycle_ibi_slot(struct i3c_dev_desc *dev,
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V2 3/7] i3c: master: Prevent reuse of dynamic address on device add failure
2026-06-08 7:57 ` [PATCH V2 3/7] i3c: master: Prevent reuse of dynamic address on device add failure Adrian Hunter
@ 2026-06-08 17:45 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 17:45 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:57:56AM +0300, Adrian Hunter wrote:
> i3c_master_add_i3c_dev_locked() is called after a device has already
> been assigned a dynamic address. If the function fails, the address
> remains marked as free and may be reallocated to another device,
> leading to address conflicts on the bus.
There are other error patch, which also free dynamic address. And if add
dev failure, should it send CCC command to clean device dymatic address?
Frank
>
> Ensure the address is not marked as free on failure, by updating the
> address slot state to prevent the address from being re-used.
>
> Emit an error message to inform of the failure.
>
> Opportunistically remove the !master check because it is impossible.
>
> Fixes: 3a379bbcea0af ("i3c: Add core I3C infrastructure")
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>
>
> Changes in V2:
>
> Fix 'if (IS_ERR(newdev)' error path.
> Be defensive and do not change the addr_slot_status if it is not
> free, and update commit message accordingly.
> Amend commit message to note removal of unnecesary 'if (!master)'
> check.
> Add Fixes tag.
>
>
> drivers/i3c/master.c | 19 ++++++++++++++-----
> 1 file changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index f87bf0099d3c..7b60b0c7f646 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -2345,12 +2345,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
> bool enable_ibi = false;
> int ret;
>
> - if (!master)
> - return -EINVAL;
> -
> newdev = i3c_master_alloc_i3c_dev(master, &info);
> - if (IS_ERR(newdev))
> - return PTR_ERR(newdev);
> + if (IS_ERR(newdev)) {
> + ret = PTR_ERR(newdev);
> + goto err_prevent_addr_reuse;
> + }
>
> ret = i3c_master_attach_i3c_dev(master, newdev);
> if (ret)
> @@ -2472,6 +2471,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
> err_free_dev:
> i3c_master_free_i3c_dev(newdev);
>
> +err_prevent_addr_reuse:
> + /*
> + * Although the device has not been added, the address has been
> + * assigned. Prevent the address from being used again.
> + */
> + if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE)
> + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV);
> +
> + dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret);
> +
> return ret;
> }
> EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V2 4/7] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA
2026-06-08 7:57 ` [PATCH V2 4/7] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA Adrian Hunter
@ 2026-06-08 17:48 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 17:48 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:57:57AM +0300, Adrian Hunter wrote:
> i3c_master_add_i3c_dev_locked() no longer leaves the address marked as
> free on failure, so aborting the DAA sequence on its error is unnecessary.
>
> Failure to register a discovered device does not invalidate the entire
> Dynamic Address Assignment (DAA) procedure. Align with the behavior of
> other I3C master drivers by ignoring errors from
> i3c_master_add_i3c_dev_locked() and continuing enumeration.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
>
> Changes in V2:
>
> None
>
>
> drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 4 +---
> drivers/i3c/master/mipi-i3c-hci/cmd_v2.c | 4 +---
> 2 files changed, 2 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
> index 75d452d7f6af..3b9345718d27 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
> @@ -358,9 +358,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
> * TODO: Extend the subsystem layer to allow for registering
> * new device and provide BCR/DCR/PID at the same time.
> */
> - ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
> - if (ret)
> - break;
> + i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
> }
>
> if (dat_idx >= 0)
> diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
> index 39eec26a363c..8d93748e858d 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c
> @@ -296,9 +296,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci)
> * TODO: Extend the subsystem layer to allow for registering
> * new device and provide BCR/DCR/PID at the same time.
> */
> - ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
> - if (ret)
> - break;
> + i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
> }
>
> hci_free_xfer(xfer, 2);
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V2 5/7] i3c: master: Make i3c_master_add_i3c_dev_locked() return void
2026-06-08 7:57 ` [PATCH V2 5/7] i3c: master: Make i3c_master_add_i3c_dev_locked() return void Adrian Hunter
@ 2026-06-08 17:51 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 17:51 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:57:58AM +0300, Adrian Hunter wrote:
> The return value of i3c_master_add_i3c_dev_locked() is not used by any
> caller, and callers are not in a position to recover from failures in
> this path.
>
> Change the function to return void. Amend the kernel-doc accordingly,
> fix some grammar and remove a stale paragraph.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
>
> Changes in V2:
>
> Re-base due to changes in previous patches.
>
>
> drivers/i3c/master.c | 17 ++++-------------
> include/linux/i3c/master.h | 3 +--
> 2 files changed, 5 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 7b60b0c7f646..57857a3351c7 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -2324,19 +2324,12 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev)
> * @master: master used to send frames on the bus
> * @addr: I3C slave dynamic address assigned to the device
> *
> - * This function is instantiating an I3C device object and adding it to the
> - * I3C device list. All device information are automatically retrieved using
> - * standard CCC commands.
> - *
> - * The I3C device object is returned in case the master wants to attach
> - * private data to it using i3c_dev_set_master_data().
> + * This function instantiates an I3C device object and adds it to the I3C device
> + * list. All device information is retrieved using standard CCC commands.
> *
> * This function must be called with the bus lock held in write mode.
> - *
> - * Return: a 0 in case of success, an negative error code otherwise.
> */
> -int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
> - u8 addr)
> +void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr)
> {
> struct i3c_device_info info = { .dyn_addr = addr };
> struct i3c_dev_desc *newdev, *olddev;
> @@ -2460,7 +2453,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
> mutex_unlock(&newdev->ibi_lock);
> }
>
> - return 0;
> + return;
>
> err_detach_dev:
> if (newdev->dev && newdev->dev->desc)
> @@ -2480,8 +2473,6 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
> i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV);
>
> dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret);
> -
> - return ret;
> }
> EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
>
> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
> index e2c831fb5354..f73cede87d36 100644
> --- a/include/linux/i3c/master.h
> +++ b/include/linux/i3c/master.h
> @@ -615,8 +615,7 @@ int i3c_master_defslvs_locked(struct i3c_master_controller *master);
> int i3c_master_get_free_addr(struct i3c_master_controller *master,
> u8 start_addr);
>
> -int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
> - u8 addr);
> +void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr);
> int i3c_master_do_daa(struct i3c_master_controller *master);
> int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa);
> struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V2 6/7] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked()
2026-06-08 7:57 ` [PATCH V2 6/7] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked() Adrian Hunter
@ 2026-06-08 17:52 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 17:52 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:57:59AM +0300, Adrian Hunter wrote:
> Relocate i3c_master_do_daa_ext() and i3c_master_do_daa() so they appear
> after i3c_master_add_i3c_dev_locked().
>
> This ordering is required for upcoming changes where the DAA flow will
> (indirectly) rely on i3c_master_add_i3c_dev_locked() functionality.
> Reordering avoids forward dependency issues and keeps related code paths
> logically arranged.
>
> No functional change.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
>
> Changes in V2:
>
> None
>
>
> drivers/i3c/master.c | 128 +++++++++++++++++++++----------------------
> 1 file changed, 64 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 57857a3351c7..5021d25b718a 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -1870,70 +1870,6 @@ static void i3c_master_reg_work_fn(struct work_struct *work)
> i3c_bus_normaluse_unlock(&master->bus);
> }
>
> -/**
> - * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
> - * @master: controller
> - * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA)
> - *
> - * Perform Dynamic Address Assignment with optional support for System
> - * Hibernation (@rstdaa is true).
> - *
> - * After System Hibernation, Dynamic Addresses can have been reassigned at boot
> - * time to different values. A simple strategy is followed to handle that.
> - * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA
> - * procedure which has provision for reassigning addresses that differ from the
> - * previously recorded addresses.
> - *
> - * Return: a 0 in case of success, an negative error code otherwise.
> - */
> -int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
> -{
> - int rstret = 0;
> - int ret;
> -
> - ret = i3c_master_rpm_get(master);
> - if (ret)
> - return ret;
> -
> - i3c_bus_maintenance_lock(&master->bus);
> -
> - if (master->shutting_down) {
> - ret = -ENODEV;
> - } else {
> - if (rstdaa)
> - rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
> - ret = master->ops->do_daa(master);
> - }
> -
> - i3c_bus_maintenance_unlock(&master->bus);
> -
> - if (ret)
> - goto out;
> -
> - queue_work(master->wq, &master->reg_work);
> -out:
> - i3c_master_rpm_put(master);
> -
> - return rstret ?: ret;
> -}
> -EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext);
> -
> -/**
> - * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
> - * @master: master doing the DAA
> - *
> - * This function instantiates I3C device objects and adds them to the
> - * I3C device list. All device information is automatically retrieved using
> - * standard CCC commands.
> - *
> - * Return: a 0 in case of success, an negative error code otherwise.
> - */
> -int i3c_master_do_daa(struct i3c_master_controller *master)
> -{
> - return i3c_master_do_daa_ext(master, false);
> -}
> -EXPORT_SYMBOL_GPL(i3c_master_do_daa);
> -
> /**
> * i3c_master_dma_map_single() - Map buffer for single DMA transfer
> * @dev: device object of a device doing DMA
> @@ -2476,6 +2412,70 @@ void i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, u8 addr
> }
> EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
>
> +/**
> + * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
> + * @master: controller
> + * @rstdaa: whether to first perform Reset of Dynamic Addresses (RSTDAA)
> + *
> + * Perform Dynamic Address Assignment with optional support for System
> + * Hibernation (@rstdaa is true).
> + *
> + * After System Hibernation, Dynamic Addresses can have been reassigned at boot
> + * time to different values. A simple strategy is followed to handle that.
> + * Perform a Reset of Dynamic Addresses (RSTDAA) followed by the normal DAA
> + * procedure which has provision for reassigning addresses that differ from the
> + * previously recorded addresses.
> + *
> + * Return: a 0 in case of success, an negative error code otherwise.
> + */
> +int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
> +{
> + int rstret = 0;
> + int ret;
> +
> + ret = i3c_master_rpm_get(master);
> + if (ret)
> + return ret;
> +
> + i3c_bus_maintenance_lock(&master->bus);
> +
> + if (master->shutting_down) {
> + ret = -ENODEV;
> + } else {
> + if (rstdaa)
> + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
> + ret = master->ops->do_daa(master);
> + }
> +
> + i3c_bus_maintenance_unlock(&master->bus);
> +
> + if (ret)
> + goto out;
> +
> + queue_work(master->wq, &master->reg_work);
> +out:
> + i3c_master_rpm_put(master);
> +
> + return rstret ?: ret;
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_do_daa_ext);
> +
> +/**
> + * i3c_master_do_daa() - do a DAA (Dynamic Address Assignment)
> + * @master: master doing the DAA
> + *
> + * This function instantiates I3C device objects and adds them to the
> + * I3C device list. All device information is automatically retrieved using
> + * standard CCC commands.
> + *
> + * Return: a 0 in case of success, an negative error code otherwise.
> + */
> +int i3c_master_do_daa(struct i3c_master_controller *master)
> +{
> + return i3c_master_do_daa_ext(master, false);
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_do_daa);
> +
> #define OF_I3C_REG1_IS_I2C_DEV BIT(31)
>
> static int
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V2 7/7] i3c: master: Reconcile dynamic addresses after DAA
2026-06-08 7:58 ` [PATCH V2 7/7] i3c: master: Reconcile dynamic addresses after DAA Adrian Hunter
@ 2026-06-08 18:06 ` Frank Li
0 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-06-08 18:06 UTC (permalink / raw)
To: Adrian Hunter; +Cc: alexandre.belloni, Frank.Li, linux-i3c, linux-kernel
On Mon, Jun 08, 2026 at 10:58:00AM +0300, Adrian Hunter wrote:
> After Dynamic Address Assignment (DAA), there may be cases where
> devices have been assigned dynamic addresses on the bus, but are not
> successfully registered in the device model. This can happen, for
> example, if errors occur during device addition, leaving the bus state
> and software state inconsistent.
>
> Introduce a reconciliation step to resolve such inconsistencies.
>
> Scan all address slots marked as I3C devices by the bus, and compare
> them against the set of devices currently registered. For any dynamic
> address that is marked occupied but has no corresponding i3c_dev_desc,
> probe for device presence using a GETSTATUS CCC.
>
> Retry the probe (with exponential backoff delay) to handle transient NACK
> conditions. If a device responds, register it via
> i3c_master_add_i3c_dev_locked(). Otherwise, free the address
> slot so it may be reused in future DAA operations.
>
> Note, i3c_master_add_i3c_dev_locked() may fail (again), in which case the
> dynamic address remains marked as occupied. A future DAA will try again.
>
> This also handles a corner case where a device is assigned a dynamic
> address but not successfully added, and subsequently loses that address
> (e.g. due to power management). If DAA is run again, the device may
> receive a new dynamic address while the old one remains marked as
> occupied. Repeated occurrences of this scenario could eventually
> exhaust the dynamic address space. The reconciliation step ensures that
> stale addresses are detected and freed, preventing address leakage.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>
>
> Changes in V2:
>
> Add bitmap.h include for bitmap_zero() etc.
> Re-base due to changes in previous patches.
>
>
> drivers/i3c/master.c | 115 ++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 113 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 5021d25b718a..6656c8591fab 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -6,7 +6,9 @@
> */
>
> #include <linux/atomic.h>
> +#include <linux/bitmap.h>
> #include <linux/bug.h>
> +#include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/dma-mapping.h>
> #include <linux/err.h>
> @@ -1613,6 +1615,56 @@ static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
> return 0;
> }
>
> +static int i3c_master_getstatus_locked(struct i3c_master_controller *master,
> + u8 addr, u16 *status)
> +{
> + struct i3c_ccc_getstatus *getstatus;
> + struct i3c_ccc_cmd_dest dest;
> + struct i3c_ccc_cmd cmd;
> + int ret;
> +
> + getstatus = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*getstatus));
> + if (!getstatus)
> + return -ENOMEM;
> +
> + i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETSTATUS, &dest, 1);
> + ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
> + if (ret)
> + goto out;
> +
> + if (dest.payload.len != sizeof(*getstatus)) {
> + ret = -EIO;
> + goto out;
> + }
> +
> + if (status)
> + *status = be16_to_cpu(getstatus->status);
> +out:
> + i3c_ccc_cmd_dest_cleanup(&dest);
> +
> + return ret;
> +}
> +
> +#define I3C_DEV_PROBE_INITIAL_DELAY_US 20
> +#define I3C_DEV_PROBE_DELAY_FACTOR 2
> +#define I3C_DEV_PROBE_CNT 5
what's these value coming from? from i3c spec?
> +
> +static bool i3c_master_i3c_dev_present(struct i3c_master_controller *master, unsigned int addr)
> +{
> + int delay = I3C_DEV_PROBE_INITIAL_DELAY_US;
> +
> + for (int i = 0; i < I3C_DEV_PROBE_CNT; i++) {
> + if (i) {
> + fsleep(delay);
> + delay *= I3C_DEV_PROBE_DELAY_FACTOR;
> + }
> + if (!i3c_master_getstatus_locked(master, addr, NULL))
> + return true;
> + }
> +
> + return false;
> +}
> +
...
>
> +static void i3c_master_reconcile_dyn_addrs(struct i3c_master_controller *master)
> +{
> + DECLARE_BITMAP(dev_dyn_addrs, I2C_MAX_ADDR + 1);
> + enum i3c_addr_slot_status status;
> + struct i3c_dev_desc *desc;
> +
> + /* Mark all devices' dynamic addresses in the bitmap */
> + bitmap_zero(dev_dyn_addrs, I2C_MAX_ADDR + 1);
> + i3c_bus_for_each_i3cdev(&master->bus, desc)
> + __set_bit(desc->info.dyn_addr, dev_dyn_addrs);
> + /* Reconcile the bitmap with the bus address slot status */
> + for (unsigned int addr = 0; addr <= I2C_MAX_ADDR; addr++) {
> + status = i3c_bus_get_addr_slot_status(&master->bus, addr);
> + if (status != I3C_ADDR_SLOT_I3C_DEV || test_bit(addr, dev_dyn_addrs))
> + continue;
> + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_FREE);
> + /* Try to add the device, but probe to see if it is really present */
> + __i3c_master_add_i3c_dev_locked(master, addr, true);
If dev add success for addr, but you free addr at previous line, any
issue?
Frank
> + }
> +}
> +
> /**
> * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version)
> * @master: controller
> @@ -2445,6 +2551,11 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa)
> if (rstdaa)
> rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
> ret = master->ops->do_daa(master);
> + /*
> + * Handle cases where a dynamic address was assigned but the
> + * device was not successfully added.
> + */
> + i3c_master_reconcile_dyn_addrs(master);
> }
>
> i3c_bus_maintenance_unlock(&master->bus);
> --
> 2.51.0
>
--
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-06-08 18:06 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-08 7:57 [PATCH V2 0/7] i3c: Fix IBI race, address handling, and reconcile DAA Adrian Hunter
2026-06-08 7:57 ` [PATCH V2 1/7] i3c: mipi-i3c-hci: Fix race in i3c_hci_addr_to_dev() Adrian Hunter
2026-06-08 17:31 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 2/7] i3c: mipi-i3c-hci: Ignore DISEC failures when disabling IBIs Adrian Hunter
2026-06-08 17:33 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 3/7] i3c: master: Prevent reuse of dynamic address on device add failure Adrian Hunter
2026-06-08 17:45 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 4/7] i3c: mipi-i3c-hci: Tolerate i3c_master_add_i3c_dev_locked() failures in DAA Adrian Hunter
2026-06-08 17:48 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 5/7] i3c: master: Make i3c_master_add_i3c_dev_locked() return void Adrian Hunter
2026-06-08 17:51 ` Frank Li
2026-06-08 7:57 ` [PATCH V2 6/7] i3c: master: Move DAA API functions after i3c_master_add_i3c_dev_locked() Adrian Hunter
2026-06-08 17:52 ` Frank Li
2026-06-08 7:58 ` [PATCH V2 7/7] i3c: master: Reconcile dynamic addresses after DAA Adrian Hunter
2026-06-08 18:06 ` Frank Li
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox