[PATCH v2 3/3] i3c: master: amd: Add hot-join support

From: Shubham Patil

Date: Mon Aug 24 2026 - 00:49:51 EST


Add hot-join support for the AMD AXI I3C master controller.
By default, the hot-join acknowledgment is disabled. Users can use the
sysfs entry to enable it. A hot-join event is handed to the i3c core
with i3c_master_queue_hotjoin(), which runs DAA from the core workqueue
so the new device is enumerated.

Signed-off-by: Shubham Patil <shubhamsanjay.patil@xxxxxxx>
---
Changes in V2:
- Use the core's i3c_master_queue_hotjoin() instead of a private work
item, and only queue when hj_capable is set; the core owns the work
and cancels it on unregister.
- Update the commit description accordingly.
---
drivers/i3c/master/amd-i3c-master.c | 67 +++++++++++++++++++++++++----
1 file changed, 58 insertions(+), 9 deletions(-)

diff --git a/drivers/i3c/master/amd-i3c-master.c b/drivers/i3c/master/amd-i3c-master.c
index 92a9f43f8934..26ef2ba9f651 100644
--- a/drivers/i3c/master/amd-i3c-master.c
+++ b/drivers/i3c/master/amd-i3c-master.c
@@ -55,9 +55,11 @@
#define XI3C_CR_EN_MASK BIT(0) /* Core Enable */
#define XI3C_CR_RESUME_MASK BIT(2) /* Core Resume */
#define XI3C_CR_IBI_MASK BIT(3) /* IBI ACK enable */
+#define XI3C_CR_HJ_MASK BIT(4) /* Hot-Join ACK enable */
#define XI3C_SR_RESP_NOT_EMPTY_MASK BIT(4) /* Resp Fifo not empty status mask */
#define XI3C_RD_FIFO_NOT_EMPTY_MASK BIT(15) /* Read Fifo not empty status mask */
#define XI3C_INTR_IBI_MASK BIT(7) /* IBI event (INTR status/enable) */
+#define XI3C_INTR_HJ_MASK BIT(8) /* Hot-Join event */

#define XI3C_BCR_MASK GENMASK(23, 16)
#define XI3C_DCR_MASK GENMASK(31, 24)
@@ -191,11 +193,14 @@ struct xi3c_xfer {
* @xfer_resp_valid is set. Guarded by @lock.
* @xfer_resp_valid: True once the in-flight transfer's own response word has
* been taken from the shared response FIFO. Guarded by @lock.
- * @irq: Controller interrupt line, used for IBI events. Only valid when
- * @ibi_capable is set.
+ * @irq: Controller interrupt line, used for IBI/Hot-Join events. Only valid
+ * when @ibi_capable is set.
* @ibi_capable: True when the IP was synthesized with In-Band Interrupt
- * support ("xlnx,in-band-interrupt-capable"); also the
- * condition for the controller interrupt being present.
+ * support ("xlnx,in-band-interrupt-capable"). Since Hot-Join
+ * requests are ACKed by the IBI machinery, this is also the
+ * condition for the controller interrupt being present at all.
+ * @hj_capable: True when the IP was synthesized with Hot-Join support
+ * ("xlnx,hot-join-capable"); implies @ibi_capable.
* @ops: Controller ops handed to the framework, assembled at probe time from
* the base ops plus the callbacks the design actually supports.
* @ibi: In-Band Interrupt slot tracking.
@@ -224,6 +229,7 @@ struct xi3c_master {
bool xfer_resp_valid;
int irq;
bool ibi_capable;
+ bool hj_capable;
struct i3c_master_controller_ops ops;
struct {
spinlock_t lock; /* protects slots[] against the IBI handler */
@@ -1243,13 +1249,14 @@ static void xi3c_master_bus_cleanup(struct i3c_master_controller *m)
struct xi3c_master *master = to_xi3c_master(m);

/*
- * Disarm all interrupt sources and the IBI ACK so the controller can't
- * assert once disabled; reset the refcount that tracks them.
+ * Disarm the interrupts and the IBI/Hot-Join ACKs so nothing is left
+ * armed for a later bus_init(); reset the refcount that tracks them.
*/
scoped_guard(spinlock_irqsave, &master->reg_lock) {
iowrite32(0, master->membase + XI3C_INTR_RE_OFFSET);
iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) &
- ~XI3C_CR_IBI_MASK, master->membase + XI3C_CR_OFFSET);
+ ~(XI3C_CR_IBI_MASK | XI3C_CR_HJ_MASK),
+ master->membase + XI3C_CR_OFFSET);
master->ibi.enabled_count = 0;
}

@@ -1550,6 +1557,32 @@ static void xi3c_master_process_ibi(struct xi3c_master *master, u32 resp, u8 da)
xi3c_master_drain_ibi_fifo(master, len);
}

+static int xi3c_master_enable_hotjoin(struct i3c_master_controller *m)
+{
+ struct xi3c_master *master = to_xi3c_master(m);
+
+ guard(spinlock_irqsave)(&master->reg_lock);
+ iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) | XI3C_CR_HJ_MASK,
+ master->membase + XI3C_CR_OFFSET);
+ iowrite32(ioread32(master->membase + XI3C_INTR_RE_OFFSET) |
+ XI3C_INTR_HJ_MASK, master->membase + XI3C_INTR_RE_OFFSET);
+
+ return 0;
+}
+
+static int xi3c_master_disable_hotjoin(struct i3c_master_controller *m)
+{
+ struct xi3c_master *master = to_xi3c_master(m);
+
+ guard(spinlock_irqsave)(&master->reg_lock);
+ iowrite32(ioread32(master->membase + XI3C_INTR_RE_OFFSET) &
+ ~XI3C_INTR_HJ_MASK, master->membase + XI3C_INTR_RE_OFFSET);
+ iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) & ~XI3C_CR_HJ_MASK,
+ master->membase + XI3C_CR_OFFSET);
+
+ return 0;
+}
+
static irqreturn_t xi3c_master_irq_handler(int irq, void *dev_id)
{
struct xi3c_master *master = dev_id;
@@ -1570,6 +1603,9 @@ static irqreturn_t xi3c_master_irq_handler(int irq, void *dev_id)
xi3c_master_handle_ibi(master);
}

+ if (master->hj_capable && (status & XI3C_INTR_HJ_MASK))
+ i3c_master_queue_hotjoin(&master->base);
+
return IRQ_HANDLED;
}

@@ -1590,6 +1626,11 @@ static void xi3c_master_init_ibi_ops(struct xi3c_master *master)
master->ops.enable_ibi = xi3c_master_enable_ibi;
master->ops.disable_ibi = xi3c_master_disable_ibi;
master->ops.recycle_ibi_slot = xi3c_master_recycle_ibi_slot;
+
+ if (master->hj_capable) {
+ master->ops.enable_hotjoin = xi3c_master_enable_hotjoin;
+ master->ops.disable_hotjoin = xi3c_master_disable_hotjoin;
+ }
}

static int xi3c_master_probe(struct platform_device *pdev)
@@ -1625,10 +1666,18 @@ static int xi3c_master_probe(struct platform_device *pdev)
master->ibi_capable =
device_property_read_bool(master->dev,
"xlnx,in-band-interrupt-capable");
+ master->hj_capable =
+ device_property_read_bool(master->dev,
+ "xlnx,hot-join-capable");
+
+ /* Hot-Join requests are ACKed by the IBI machinery. */
+ if (master->hj_capable && !master->ibi_capable)
+ return dev_err_probe(master->dev, -EINVAL,
+ "hot-join-capable requires in-band-interrupt-capable\n");

/*
- * The interrupt only carries IBI events, so it is only described for
- * designs synthesized with that feature.
+ * The interrupt only carries IBI and Hot-Join events, so it is only
+ * described for designs synthesized with those features.
*/
if (master->ibi_capable) {
xi3c_master_init_ibi_ops(master);
--
2.34.1