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

Shubham Patil shubhamsanjay.patil at amd.com
Fri Aug 14 04:51:55 PDT 2026


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 at amd.com>
---
 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




More information about the linux-i3c mailing list