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

From: Frank Li

Date: Tue May 12 2026 - 12:43:43 EST


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@xxxxxxxxx>
> ---

Reviewed-by: Frank Li <Frank.Li@xxxxxxx>

> 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
>