During ENTDAA, a Transfer Error Status Interrupt may be raised while the Response Status Interrupt is being handled. It remains latched in the status register while waiting for GETPID, so clear the Transfer Error Status Interrupt after ENTDAA. Also, `polltimeout()` returns a negative value on timeout; update the comparison to account for this.
Signed-off-by: Dineshkumar Parameswaran <[email protected]> --- drivers/i3c/master/dw-i3c-master.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 25933adf010..4cdd908067e 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -539,7 +539,7 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master, dw_i3c_master_enqueue_xfer(master, xfer); - if (dw_i3c_status_poll_timeout(master) > POLL_SUCCESS) + if (dw_i3c_status_poll_timeout(master) < POLL_SUCCESS) dw_i3c_master_dequeue_xfer(master, xfer); else dw_i3c_master_irq_handler(master); @@ -584,7 +584,7 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) dw_i3c_master_enqueue_xfer(master, xfer); - if (dw_i3c_status_poll_timeout(master) > POLL_SUCCESS) + if (dw_i3c_status_poll_timeout(master) < POLL_SUCCESS) dw_i3c_master_dequeue_xfer(master, xfer); else dw_i3c_master_irq_handler(master); @@ -661,7 +661,7 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) dw_i3c_master_enqueue_xfer(master, xfer); - if (dw_i3c_status_poll_timeout(master) > POLL_SUCCESS) + if (dw_i3c_status_poll_timeout(master) < POLL_SUCCESS) dw_i3c_master_dequeue_xfer(master, xfer); else dw_i3c_master_irq_handler(master); @@ -669,6 +669,17 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0); newdevs &= ~olddevs; + /* Sometimes, when the Response Queue is being processed, + * the interrupt status register may get updated with a transfer error + * (INTR_TRANSFER_ERR_STAT) and this error stays set. Because of + * this, when the system keeps checking for the next interrupt, it + * detects that an interrupt is already present—even before the + * intended, actual interrupt occurs. So, clearing the Transfer + * error here. + */ + if (readl(master->regs + INTR_STATUS) & INTR_TRANSFER_ERR_STAT) + writel(INTR_TRANSFER_ERR_STAT, master->regs + INTR_STATUS); + for (pos = 0; pos < master->maxdevs; pos++) { if (newdevs & BIT(pos)) { if (i3c_master_add_i3c_dev_locked(m, master->addrs[pos])) @@ -743,7 +754,7 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev, dw_i3c_master_enqueue_xfer(master, xfer); - if (dw_i3c_status_poll_timeout(master) > POLL_SUCCESS) + if (dw_i3c_status_poll_timeout(master) < POLL_SUCCESS) dw_i3c_master_dequeue_xfer(master, xfer); else dw_i3c_master_irq_handler(master); @@ -888,7 +899,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, dw_i3c_master_enqueue_xfer(master, xfer); - if (dw_i3c_status_poll_timeout(master) > POLL_SUCCESS) + if (dw_i3c_status_poll_timeout(master) < POLL_SUCCESS) dw_i3c_master_dequeue_xfer(master, xfer); else dw_i3c_master_irq_handler(master); -- 2.34.1
