On 13/12/2025 05:01, Dmitry Baryshkov wrote:
On Sat, Dec 13, 2025 at 12:08:17AM +0100, David Heidelberg via B4 Relay wrote:
From: Petr Hodina <[email protected]>
This patch fixes system hangs that occur when RCG2 and DSI code paths
perform register accesses while the associated clocks or power domains
are disabled.
In general this should not be happening. Do you have a description of
the corresponding code path?
For the Qualcomm RCG2 clock driver, updating M/N/D registers while the
clock is gated can cause the hardware to lock up. Avoid toggling the
update bit when the clock is disabled and instead write the configuration
directly.
Signed-off-by: Petr Hodina <[email protected]>
Signed-off-by: David Heidelberg <[email protected]>
---
drivers/clk/qcom/clk-rcg2.c | 18 ++++++++++++++++++
drivers/gpu/drm/msm/dsi/dsi_host.c | 13 +++++++++++++
2 files changed, 31 insertions(+)
This needs to be split into two patches.
Thank you for the feedback! Petr promised to look into it and sent v2
until end of the week.
David
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index e18cb8807d735..a18d2b9319670 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -1182,6 +1182,24 @@ static int clk_pixel_set_rate(struct clk_hw *hw,
unsigned long rate,
f.m = frac->num;
f.n = frac->den;
+ /*
+ * If clock is disabled, update the M, N and D registers and
+ * don't hit the update bit.
+ */
+ if (!clk_hw_is_enabled(hw)) {
+ int ret;
+
+ ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg),
&cfg);
+ if (ret)
+ return ret;
+
+ ret = __clk_rcg2_configure(rcg, &f, &cfg);
+ if (ret)
+ return ret;
+
+ return regmap_write(rcg->clkr.regmap,
RCG_CFG_OFFSET(rcg), cfg);
+ }
+
return clk_rcg2_configure(rcg, &f);
}
return -EINVAL;
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c
b/drivers/gpu/drm/msm/dsi/dsi_host.c
index e0de545d40775..374ed966e960b 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -762,6 +762,12 @@ dsi_get_cmd_fmt(const enum mipi_dsi_pixel_format mipi_fmt)
static void dsi_ctrl_disable(struct msm_dsi_host *msm_host)
{
+ /* Check if we're already powered off before writing registers */
+ if (!msm_host->power_on) {
+ pr_info("DSI CTRL: Skipping register write - host already powered
off\n");
It definitely should be dev_something. Probably dev_warn().
+ return;
+ }
+
dsi_write(msm_host, REG_DSI_CTRL, 0);
}
@@ -2489,6 +2495,8 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host)
{
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
+ int ret;
+
Extra empty line
mutex_lock(&msm_host->dev_mutex);
if (!msm_host->power_on) {
@@ -2496,6 +2504,11 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host)
goto unlock_ret;
}
+ /* Ensure clocks are enabled before register access */
And this looks like yet another fix, prompting for a separate commmit.
+ ret = pm_runtime_get_sync(&msm_host->pdev->dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(&msm_host->pdev->dev);
pm_runtime_resume_and_get()
Also, where is a corresponding put() ? We are leaking the runtime PM
counter otherwise.
+
dsi_ctrl_disable(msm_host);
pinctrl_pm_select_sleep_state(&msm_host->pdev->dev);
--
2.51.0
--
David Heidelberg