Messages in this thread | | | Date | Tue, 25 Jul 2023 08:52:05 +0200 | Subject | Re: [PATCH v3] spmi: mtk-pmif: Serialize PMIF status check and command submission | From | AngeloGioacchino Del Regno <> |
| |
Il 24/07/23 17:47, Nícolas F. R. A. Prado ha scritto: > Before writing the read or write command to the SPMI arbiter through the > PMIF interface, the current status of the channel is checked to ensure > it is idle. However, since the status only changes from idle when the > command is written, it is possible for two concurrent calls to determine > that the channel is idle and simultaneously send their commands. At this > point the PMIF interface hangs, with the status register no longer being > updated, and thus causing all subsequent operations to time out. > > This was observed on the mt8195-cherry-tomato-r2 machine, particularly > after commit 46600ab142f8 ("regulator: Set PROBE_PREFER_ASYNCHRONOUS for > drivers between 5.10 and 5.15") was applied, since then the two MT6315 > devices present on the SPMI bus would probe assynchronously and > sometimes (during probe or at a later point) read the bus > simultaneously, breaking the PMIF interface and consequently slowing > down the whole system. > > To fix the issue at its root cause, introduce locking around the channel > status check and the command write, so that both become an atomic > operation, preventing race conditions between two (or more) SPMI bus > read/write operations. A spinlock is used since this is a fast bus, as > indicated by the usage of the atomic variant of readl_poll, and > '.fast_io = true' being used in the mt6315 driver, so spinlocks are > already used for the regmap access. > > Fixes: b45b3ccef8c0 ("spmi: mediatek: Add support for MT6873/8192") > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Perfect! :-)
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> > --- > > Changes in v3: > - Switched to raw spinlock to avoid overhead in PREEMPT_RT > - Moved spin_unlock to after marking the channel ready on the error > paths > > Changes in v2: > - Added missing spin_unlocks to error paths > - Moved memcpy outside spinlock region in the write_cmd function > - Reworded commit message to make clear that issue can happen at any > point in runtime, not only during boot > > drivers/spmi/spmi-mtk-pmif.c | 20 ++++++++++++++++---- > 1 file changed, 16 insertions(+), 4 deletions(-) > > diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c > index b3c991e1ea40..54c35f5535cb 100644 > --- a/drivers/spmi/spmi-mtk-pmif.c > +++ b/drivers/spmi/spmi-mtk-pmif.c > @@ -50,6 +50,7 @@ struct pmif { > struct clk_bulk_data clks[PMIF_MAX_CLKS]; > size_t nclks; > const struct pmif_data *data; > + raw_spinlock_t lock; > }; > > static const char * const pmif_clock_names[] = { > @@ -314,6 +315,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > struct ch_reg *inf_reg; > int ret; > u32 data, cmd; > + unsigned long flags; > > /* Check for argument validation. */ > if (sid & ~0xf) { > @@ -334,6 +336,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > else > return -EINVAL; > > + raw_spin_lock_irqsave(&arb->lock, flags); > /* Wait for Software Interface FSM state to be IDLE. */ > inf_reg = &arb->chan; > ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], > @@ -343,6 +346,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > /* set channel ready if the data has transferred */ > if (pmif_is_fsm_vldclr(arb)) > pmif_writel(arb, 1, inf_reg->ch_rdy); > + raw_spin_unlock_irqrestore(&arb->lock, flags); > dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n"); > return ret; > } > @@ -350,6 +354,7 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > /* Send the command. */ > cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr; > pmif_writel(arb, cmd, inf_reg->ch_send); > + raw_spin_unlock_irqrestore(&arb->lock, flags); > > /* > * Wait for Software Interface FSM state to be WFVLDCLR, > @@ -376,7 +381,8 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > struct pmif *arb = spmi_controller_get_drvdata(ctrl); > struct ch_reg *inf_reg; > int ret; > - u32 data, cmd; > + u32 data, wdata, cmd; > + unsigned long flags; > > if (len > 4) { > dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len); > @@ -394,6 +400,10 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > else > return -EINVAL; > > + /* Set the write data. */ > + memcpy(&wdata, buf, len); > + > + raw_spin_lock_irqsave(&arb->lock, flags); > /* Wait for Software Interface FSM state to be IDLE. */ > inf_reg = &arb->chan; > ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], > @@ -403,17 +413,17 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, > /* set channel ready if the data has transferred */ > if (pmif_is_fsm_vldclr(arb)) > pmif_writel(arb, 1, inf_reg->ch_rdy); > + raw_spin_unlock_irqrestore(&arb->lock, flags); > dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n"); > return ret; > } > > - /* Set the write data. */ > - memcpy(&data, buf, len); > - pmif_writel(arb, data, inf_reg->wdata); > + pmif_writel(arb, wdata, inf_reg->wdata); > > /* Send the command. */ > cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr; > pmif_writel(arb, cmd, inf_reg->ch_send); > + raw_spin_unlock_irqrestore(&arb->lock, flags); > > return 0; > } > @@ -488,6 +498,8 @@ static int mtk_spmi_probe(struct platform_device *pdev) > arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset; > arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset; > > + raw_spin_lock_init(&arb->lock); > + > platform_set_drvdata(pdev, ctrl); > > err = spmi_controller_add(ctrl);
| |