[PATCH 8/8] i3c: mipi-i3c-hci: Add Hot-Join support

Frank Li Frank.li at nxp.com
Tue May 12 09:34:34 PDT 2026


On Tue, May 12, 2026 at 03:17:32PM +0300, Adrian Hunter wrote:
> Wire the MIPI I3C HCI driver into the I3C core Hot-Join framework to
> allow targets to dynamically join the bus after initial DAA.
>
> HCI hardware ACKs or NACKs Hot-Join requests based on
> HC_CONTROL.HOT_JOIN_CTRL.  This was previously left in the
> NACK-and-DISEC state, effectively preventing Hot-Join.  Implement
> the ->enable_hotjoin() and ->disable_hotjoin() master operations
> so the core and user space can control this policy at runtime.
>
> Also issue broadcast ENEC HJ when enabling Hot-Join.  This is required
> because the controller may have previously DISEC'ed the Hot-Join
> event, causing targets that were NACKed once to never retry.
>
> Acknowledged Hot-Join requests are delivered as IBIs on the reserved
> address 0x02.  Update both the DMA and PIO IBI paths to recognise this
> address and forward the event to i3c_master_queue_hotjoin().
>
> To make Hot-Join usable by default, enable it once after the initial
> DAA.  This is gated by rpm_ibi_allowed, since otherwise keeping Hot-Join
> enabled prevents runtime suspend.  A new hj_init_done flag ensures this
> one-time enablement is not repeated on subsequent DAAs.
>
> Signed-off-by: Adrian Hunter <adrian.hunter at intel.com>
> ---

Reviewed-by: Frank Li <Frank.Li at nxp.com>

>  drivers/i3c/master/mipi-i3c-hci/core.c | 50 ++++++++++++++++++++++++--
>  drivers/i3c/master/mipi-i3c-hci/dma.c  |  5 +++
>  drivers/i3c/master/mipi-i3c-hci/hci.h  |  1 +
>  drivers/i3c/master/mipi-i3c-hci/pio.c  |  5 +++
>  4 files changed, 58 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
> index 2866d599612a..f8b399d16598 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/core.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/core.c
> @@ -392,11 +392,52 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
>  	return ret;
>  }
>
> +static int i3c_hci_enable_hotjoin(struct i3c_master_controller *m)
> +{
> +	struct i3c_hci *hci = to_i3c_hci(m);
> +	int ret;
> +
> +	reg_clear(HC_CONTROL, HC_CONTROL_HOT_JOIN_CTRL);
> +
> +	/*
> +	 * Broadcast Hot_join enable, so that an I3C device that has previously
> +	 * had its Hot-Join request NACK'ed knows to try again.
> +	 */
> +	ret = i3c_master_enec_disec_locked(m, I3C_BROADCAST_ADDR, true, I3C_CCC_EVENT_HJ, true);
> +	if (ret) {
> +		reg_set(HC_CONTROL, HC_CONTROL_HOT_JOIN_CTRL);
> +		dev_err(&hci->master.dev, "Hot-Join ENEC CCC failed\n");
> +	}
> +
> +	return ret;
> +}
> +
> +static int i3c_hci_disable_hotjoin(struct i3c_master_controller *m)
> +{
> +	struct i3c_hci *hci = to_i3c_hci(m);
> +
> +	reg_set(HC_CONTROL, HC_CONTROL_HOT_JOIN_CTRL);
> +	return 0;
> +}
> +
>  static int i3c_hci_daa(struct i3c_master_controller *m)
>  {
>  	struct i3c_hci *hci = to_i3c_hci(m);
> +	int ret;
>
> -	return hci->cmd->perform_daa(hci);
> +	ret = hci->cmd->perform_daa(hci);
> +
> +	if (!hci->hj_init_done) {
> +		hci->hj_init_done = true;
> +		/*
> +		 * Enable Hot-Join by default after initial DAA if it does not
> +		 * prevent runtime suspend.
> +		 */
> +		if (m->rpm_ibi_allowed && !ret)
> +			m->hotjoin = !i3c_hci_enable_hotjoin(m);
> +	}
> +
> +	return ret;
>  }
>
>  static int i3c_hci_i3c_xfers(struct i3c_dev_desc *dev,
> @@ -652,6 +693,8 @@ static const struct i3c_master_controller_ops i3c_hci_ops = {
>  	.enable_ibi		= i3c_hci_enable_ibi,
>  	.disable_ibi		= i3c_hci_disable_ibi,
>  	.recycle_ibi_slot	= i3c_hci_recycle_ibi_slot,
> +	.enable_hotjoin		= i3c_hci_enable_hotjoin,
> +	.disable_hotjoin	= i3c_hci_disable_hotjoin,
>  };
>
>  static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
> @@ -833,8 +876,9 @@ static int i3c_hci_do_reset_and_restore(struct i3c_hci *hci)
>  	scoped_guard(spinlock_irqsave, &hci->lock)
>  		hci->irq_inactive = false;
>
> -	/* Enable bus with Hot-Join disabled */
> -	reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL);
> +	/* Enable bus, restoring hot-join state */
> +	reg_set(HC_CONTROL,
> +		HC_CONTROL_BUS_ENABLE | (hci->master.hotjoin ? 0 : HC_CONTROL_HOT_JOIN_CTRL));
>
>  	return 0;
>  }
> diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
> index e5deeba0aa4e..34129ac039dc 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/dma.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
> @@ -971,6 +971,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
>  	}
>
>  	/* determine who this is for */
> +	if (ibi_addr == I3C_HOT_JOIN_ADDR) {
> +		i3c_master_queue_hotjoin(&hci->master);
> +		goto done;
> +	}
> +
>  	dev = i3c_hci_addr_to_dev(hci, ibi_addr);
>  	if (!dev) {
>  		dev_err(&hci->master.dev,
> diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
> index 243d7a67f6f6..591eea040b01 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/hci.h
> +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
> @@ -57,6 +57,7 @@ struct i3c_hci {
>  	bool irq_inactive;
>  	bool enqueue_blocked;
>  	bool recovery_needed;
> +	bool hj_init_done;
>  	wait_queue_head_t enqueue_wait_queue;
>  	u32 caps;
>  	unsigned int quirks;
> diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c
> index 6b8cc5f2b4d2..b5ae1cfaa9e0 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/pio.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c
> @@ -862,6 +862,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
>  	ibi->seg_len = FIELD_GET(IBI_DATA_LENGTH, ibi_status);
>  	ibi->seg_cnt = ibi->seg_len;
>
> +	if (ibi->addr == I3C_HOT_JOIN_ADDR) {
> +		i3c_master_queue_hotjoin(&hci->master);
> +		return true;
> +	}
> +
>  	dev = i3c_hci_addr_to_dev(hci, ibi->addr);
>  	if (!dev) {
>  		dev_err(&hci->master.dev,
> --
> 2.51.0
>



More information about the linux-i3c mailing list