diff options
Diffstat (limited to 'drivers/i3c/master/svc-i3c-master.c')
-rw-r--r-- | drivers/i3c/master/svc-i3c-master.c | 41 |
1 files changed, 27 insertions, 14 deletions
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index ece563353895..9641e66a4e5f 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -417,6 +417,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master, SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000); if (ret) { dev_err(master->dev, "Timeout when polling for COMPLETE\n"); + i3c_generic_ibi_recycle_slot(data->ibi_pool, slot); return ret; } @@ -517,9 +518,24 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) */ 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, + /* + * Write REQUEST_START_ADDR request to emit broadcast address for arbitration, + * instend of using AUTO_IBI. + * + * Using AutoIBI request may cause controller to remain in AutoIBI state when + * there is a glitch on SDA line (high->low->high). + * 1. SDA high->low, raising an interrupt to execute IBI isr. + * 2. SDA low->high. + * 3. IBI isr writes an AutoIBI request. + * 4. The controller will not start AutoIBI process because SDA is not low. + * 5. IBIWON polling times out. + * 6. Controller reamins in AutoIBI state and doesn't accept EmitStop request. + */ + writel(SVC_I3C_MCTRL_REQUEST_START_ADDR | + SVC_I3C_MCTRL_TYPE_I3C | + SVC_I3C_MCTRL_IBIRESP_MANUAL | + SVC_I3C_MCTRL_DIR(SVC_I3C_MCTRL_DIR_WRITE) | + SVC_I3C_MCTRL_ADDR(I3C_BROADCAST_ADDR), master->regs + SVC_I3C_MCTRL); /* Wait for IBIWON, should take approximately 100us */ @@ -539,10 +555,15 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) switch (ibitype) { case SVC_I3C_MSTATUS_IBITYPE_IBI: dev = svc_i3c_master_dev_from_addr(master, ibiaddr); - if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI)) + if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI)) { svc_i3c_master_nack_ibi(master); - else + } else { + if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD) + svc_i3c_master_ack_ibi(master, true); + else + svc_i3c_master_ack_ibi(master, false); svc_i3c_master_handle_ibi(master, dev); + } break; case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) @@ -665,7 +686,6 @@ static int svc_i3c_master_set_speed(struct i3c_master_controller *m, } rpm_out: - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); return ret; @@ -780,7 +800,6 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m) goto rpm_out; rpm_out: - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); return ret; @@ -802,7 +821,6 @@ static void svc_i3c_master_bus_cleanup(struct i3c_master_controller *m) /* Disable master */ writel(0, master->regs + SVC_I3C_MCONFIG); - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); } @@ -1208,7 +1226,6 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m) dev_err(master->dev, "Cannot handle such a list of devices"); rpm_out: - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); return ret; @@ -1517,7 +1534,6 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master, } spin_unlock_irqrestore(&master->xferqueue.lock, flags); - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); } @@ -1714,7 +1730,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, mutex_lock(&master->lock); svc_i3c_master_enqueue_xfer(master, xfer); - if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) + if (!wait_for_completion_timeout(&xfer->comp, m->i2c.timeout)) svc_i3c_master_dequeue_xfer(master, xfer); mutex_unlock(&master->lock); @@ -1807,7 +1823,6 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev) ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); return ret; @@ -1840,7 +1855,6 @@ static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m) if (!master->enabled_events) svc_i3c_master_disable_interrupts(master); - pm_runtime_mark_last_busy(master->dev); pm_runtime_put_autosuspend(master->dev); return 0; @@ -1960,7 +1974,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev) if (ret) goto rpm_disable; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; |