]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
i3c: master: svc: fix invalidate IBI type and miss call client IBI handler
authorFrank Li <Frank.Li@nxp.com>
Mon, 6 May 2024 16:40:09 +0000 (12:40 -0400)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Wed, 22 May 2024 22:29:19 +0000 (00:29 +0200)
In an In-Band Interrupt (IBI) handle, the code logic is as follows:

1: writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI | SVC_I3C_MCTRL_IBIRESP_AUTO,
  master->regs + SVC_I3C_MCTRL);

2: ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
                                    SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
...
3: ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
   ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);

SVC_I3C_MSTATUS_IBIWON may be set before step 1. Thus, step 2 will return
immediately, and the I3C controller has not sent out the 9th SCL yet.
Consequently, ibitype and ibiaddr are 0, resulting in an unknown IBI type
occurrence and missing call I3C client driver's IBI handler.

A typical case is that SVC_I3C_MSTATUS_IBIWON is set when an IBI occurs
during the controller send start frame in svc_i3c_master_xfer().

Clear SVC_I3C_MSTATUS_IBIWON before issue SVC_I3C_MCTRL_REQUEST_AUTO_IBI
to fix this issue.

Cc: stable@vger.kernel.org
Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/r/20240506164009.21375-3-Frank.Li@nxp.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/i3c/master/svc-i3c-master.c

index a2298ab460a3732c8e93797151a21fd3675a0a38..bb299ce02cccbdb46541f2fcb7ab749843e5b98e 100644 (file)
@@ -415,6 +415,19 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
        int ret;
 
        mutex_lock(&master->lock);
+       /*
+        * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
+        * readl_relaxed_poll_timeout() to return immediately. Consequently,
+        * ibitype will be 0 since it was last updated only after the 8th SCL
+        * cycle, leading to missed client IBI handlers.
+        *
+        * A typical scenario is when IBIWON occurs and bus arbitration is lost
+        * at svc_i3c_master_priv_xfers().
+        *
+        * Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
+        */
+       writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
        /* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
        writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
               SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -429,9 +442,6 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
                goto reenable_ibis;
        }
 
-       /* Clear the interrupt status */
-       writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
-
        status = readl(master->regs + SVC_I3C_MSTATUS);
        ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
        ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);