Re: [PATCH 1/2] i2c: qcom-cci: Reset the controller when it resumes
From: Loic Poulain
Date: Wed Oct 21 2026 - 03:20:29 EST
On Tue, Oct 20, 2026 at 9:40 PM Birk Skyum <birk.skyum@xxxxx> wrote:
>
> A CCI master can stop starting transfers after a target on its bus was
> powered down and up again while the controller kept its clocks running.
> The next transfer's queue then stays on its first command, neither
> CCI_IRQ_STATUS_0 nor the queue's report status changes, and
> cci_run_queue() times out after 100 ms ("master 1 queue 0 timeout").
> Only the controller reset in that error path, or the controller's power
> domain collapsing while it is runtime suspended, brings it back.
>
> On the Lenovo Yoga Slim 7x (X1E80100) the OV02C10 front camera is
> powered down whenever it stops streaming, and the unpowered sensor
> holds SCL and SDA low: both read low at the TLMM pins with the internal
> pull-ups enabled. The queue registers at the timeout show the stuck
> first command (CUR_CMD 0x08010339, a write of 03 01 08) with the bus
> timing registers intact and no error or halt interrupt before it.
>
> cci_resume_runtime() only programs the interrupt mask and the bus timing
> again, so a controller that was runtime suspended without its power
> domain collapsing keeps the stuck state. With the camera's TITAN_TOP
> GDSC held on and the controller suspended between camera starts, 3 of
> 40 starts failed this way; with a reset on resume, 0 of 40.
>
> Reset the controller on every runtime resume, as the timeout path does.
>
> Signed-off-by: Birk Skyum <birk.skyum@xxxxx>
> ---
> drivers/i2c/busses/i2c-qcom-cci.c | 15 ++++++++++++++-
> 1 file changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
> index 76a5ded..51ce174 100644
> --- a/drivers/i2c/busses/i2c-qcom-cci.c
> +++ b/drivers/i2c/busses/i2c-qcom-cci.c
> @@ -651,7 +651,20 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
> if (ret)
> return ret;
>
> - return cci_init(cci);
> + /*
> + * A master can stop starting transfers after a target was powered
> + * down and up again while the controller ran, and keeps that state
> + * over a runtime suspend unless the power domain collapsed. Reset
> + * the controller, with the interrupt mask set first so that the
> + * reset-done interrupt also arrives after a power collapse.
> + */
> + ret = cci_init(cci);
> + if (!ret)
> + ret = cci_reset(cci);
If we don't know the state, it's probably better to just
unconditionally cci_reset().
> + if (ret)
> + cci_disable_clocks(cci);
> +
> + return ret;
> }
>
> static const struct dev_pm_ops qcom_cci_pm = {
> --
> 2.53.0
>
>