[PATCH 3/3] i3c: master: amd: Add hot-join support
From: Shubham Patil
Date: Fri Aug 14 2026 - 07:55:39 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 serviced by re-running DAA
from a work item so the i3c core enumerates the new device.
Signed-off-by: Shubham Patil <shubhamsanjay.patil@xxxxxxx>
---
drivers/i3c/master/amd-i3c-master.c | 77 ++++++++++++++++++++++++++---
1 file changed, 71 insertions(+), 6 deletions(-)
diff --git a/drivers/i3c/master/amd-i3c-master.c b/drivers/i3c/master/amd-i3c-master.c
index d47b04326ea4..62221b138bfe 100644
--- a/drivers/i3c/master/amd-i3c-master.c
+++ b/drivers/i3c/master/amd-i3c-master.c
@@ -25,6 +25,7 @@
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/unaligned.h>
+#include <linux/workqueue.h>
#define XI3C_VERSION_OFFSET 0x00 /* Version Register */
#define XI3C_RESET_OFFSET 0x04 /* Soft Reset Register */
@@ -55,9 +56,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 +194,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,ibi-capable"); also the condition for the
- * controller interrupt being present.
+ * support ("xlnx,ibi-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,hj-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.
@@ -205,6 +211,7 @@ struct xi3c_xfer {
* controller-wide IBI ACK/interrupt is armed on the
* first enable and disarmed on the last disable. Guarded
* by @reg_lock.
+ * @hj_work: Deferred re-DAA triggered from the Hot-Join interrupt.
*/
struct xi3c_master {
struct i3c_master_controller base;
@@ -221,12 +228,14 @@ 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 */
struct i3c_dev_desc *slots[XI3C_MAX_DEVS];
unsigned int enabled_count;
} ibi;
+ struct work_struct hj_work;
};
/**
@@ -1474,6 +1483,40 @@ static void xi3c_master_handle_ibi(struct xi3c_master *master)
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 void xi3c_master_hj_work(struct work_struct *work)
+{
+ struct xi3c_master *master = container_of(work, struct xi3c_master,
+ hj_work);
+
+ i3c_master_do_daa(&master->base);
+}
+
static irqreturn_t xi3c_master_irq_handler(int irq, void *dev_id)
{
struct xi3c_master *master = dev_id;
@@ -1494,6 +1537,9 @@ static irqreturn_t xi3c_master_irq_handler(int irq, void *dev_id)
xi3c_master_handle_ibi(master);
}
+ if (status & XI3C_INTR_HJ_MASK)
+ queue_work(master->base.wq, &master->hj_work);
+
return IRQ_HANDLED;
}
@@ -1514,6 +1560,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)
@@ -1545,13 +1596,21 @@ static int xi3c_master_probe(struct platform_device *pdev)
spin_lock_init(&master->ibi.lock);
spin_lock_init(&master->reg_lock);
+ INIT_WORK(&master->hj_work, xi3c_master_hj_work);
master->ibi_capable = device_property_read_bool(master->dev,
"xlnx,ibi-capable");
+ master->hj_capable = device_property_read_bool(master->dev,
+ "xlnx,hj-capable");
+
+ /* Hot-Join requests are ACKed by the IBI machinery. */
+ if (master->hj_capable && !master->ibi_capable)
+ return dev_err_probe(master->dev, -EINVAL,
+ "xlnx,hj-capable requires xlnx,ibi-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);
@@ -1588,6 +1647,12 @@ static void xi3c_master_remove(struct platform_device *pdev)
if (master->ibi_capable)
disable_irq(master->irq);
+ if (master->hj_capable) {
+ /* Stop new Hot-Join IRQs before flushing, so the work can't be re-queued. */
+ xi3c_master_disable_hotjoin(&master->base);
+ cancel_work_sync(&master->hj_work);
+ }
+
i3c_master_unregister(&master->base);
}
--
2.34.1