i3c: master: svc: wait for Manual ACK/NACK Done before next step
authorFrank Li <Frank.Li@nxp.com>
Wed, 2 Oct 2024 14:50:37 +0000 (10:50 -0400)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Thu, 31 Oct 2024 22:53:39 +0000 (23:53 +0100)
Wait for the controller to complete emitting ACK/NACK, otherwise the next
command may be omitted by the hardware.

Add a "command done" check in svc_i3c_master_nack(ack)_ibi() and change the
return type to int to flag possible timeouts.

Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20241002-svc-i3c-hj-v6-5-7e6e1d3569ae@nxp.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/i3c/master/svc-i3c-master.c

index ed3f1bf..7ef7e50 100644 (file)
@@ -388,10 +388,11 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
        return 0;
 }
 
-static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
+static int svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
                                   bool mandatory_byte)
 {
        unsigned int ibi_ack_nack;
+       u32 reg;
 
        ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK;
        if (mandatory_byte)
@@ -400,18 +401,30 @@ static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
                ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE;
 
        writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL);
+
+       return readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
+                                        SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000);
+
 }
 
-static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
+static int svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
 {
+       int ret;
+       u32 reg;
+
        writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK |
               SVC_I3C_MCTRL_IBIRESP_NACK,
               master->regs + SVC_I3C_MCTRL);
+
+       ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
+                                       SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000);
+       return ret;
 }
 
 static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 mstatus)
 {
        u32 ibitype;
+       int ret = 0;
 
        ibitype = SVC_I3C_MSTATUS_IBITYPE(mstatus);
 
@@ -421,10 +434,10 @@ static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 msta
        switch (ibitype) {
        case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
        case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
-               svc_i3c_master_nack_ibi(master);
+               ret = svc_i3c_master_nack_ibi(master);
        }
 
-       return 0;
+       return ret;
 }
 
 static void svc_i3c_master_ibi_work(struct work_struct *work)
@@ -935,7 +948,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
                        if (ret)
                                break;
                } else if (SVC_I3C_MSTATUS_IBIWON(reg)) {
-                       svc_i3c_master_handle_ibi_won(master, reg);
+                       ret = svc_i3c_master_handle_ibi_won(master, reg);
+                       if (ret)
+                               break;
                        continue;
                } else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
                        if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
@@ -1209,7 +1224,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
                 * start.
                 */
                if (SVC_I3C_MSTATUS_IBIWON(reg)) {
-                       svc_i3c_master_handle_ibi_won(master, reg);
+                       ret = svc_i3c_master_handle_ibi_won(master, reg);
+                       if (ret)
+                               goto emit_stop;
                        continue;
                }