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.

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(+)

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");
+               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;
+
 
        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 */
+       ret = pm_runtime_get_sync(&msm_host->pdev->dev);
+       if (ret < 0)
+               pm_runtime_put_noidle(&msm_host->pdev->dev);
+
        dsi_ctrl_disable(msm_host);
 
        pinctrl_pm_select_sleep_state(&msm_host->pdev->dev);

-- 
2.51.0


Reply via email to