]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
spi: spi-qcom-qspi: Fix incomplete error handling in runtime PM
authorViken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Wed, 29 Apr 2026 17:01:37 +0000 (22:31 +0530)
committerMark Brown <broonie@kernel.org>
Mon, 4 May 2026 13:15:10 +0000 (22:15 +0900)
The runtime PM functions had incomplete error handling that could leave the
system in an inconsistent state. If any operation failed midway through
suspend or resume, some resources would be left in the wrong state while
others were already changed, leading to potential clock/power imbalances.

Reorder the suspend/resume sequences to avoid brownout risk by ensuring the
performance state is set appropriately before clocks are enabled and clocks
are disabled before dropping the performance state.

Fix by adding proper error checking for all operations and using goto-based
cleanup to ensure all successfully acquired resources are properly released
on any error.

Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Link: https://patch.msgid.link/20260429-spi-nor-v5-2-993016c9711e@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-qcom-qspi.c

index 7e39038160e00cbbcf410cd2aeaa19ece03c1c55..edfbf0b5d1faade10a26bf4b8e67c1a73a19c182 100644 (file)
@@ -818,20 +818,34 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev)
        struct qcom_qspi *ctrl = spi_controller_get_devdata(host);
        int ret;
 
-       /* Drop the performance state vote */
-       dev_pm_opp_set_rate(dev, 0);
        clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks);
 
        ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
        if (ret) {
                dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
                        __func__, ret);
-               return ret;
+               goto err_enable_clk;
        }
 
-       pinctrl_pm_select_sleep_state(dev);
+       ret = pinctrl_pm_select_sleep_state(dev);
+       if (ret)
+               goto err_enable_icc;
+
+       /* Drop the performance state vote */
+       ret = dev_pm_opp_set_rate(dev, 0);
+       if (ret)
+               goto err_select_default_state;
 
        return 0;
+
+err_select_default_state:
+       pinctrl_pm_select_default_state(dev);
+err_enable_icc:
+       icc_enable(ctrl->icc_path_cpu_to_qspi);
+err_enable_clk:
+       if (clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks))
+               dev_err_ratelimited(ctrl->dev, "Failed to re-enable clocks\n");
+       return ret;
 }
 
 static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
@@ -840,20 +854,34 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
        struct qcom_qspi *ctrl = spi_controller_get_devdata(host);
        int ret;
 
-       pinctrl_pm_select_default_state(dev);
+       ret = dev_pm_opp_set_rate(dev, ctrl->last_speed * 4);
+       if (ret)
+               return ret;
+
+       ret = pinctrl_pm_select_default_state(dev);
+       if (ret)
+               goto err_opp_set_rate_zero;
 
        ret = icc_enable(ctrl->icc_path_cpu_to_qspi);
        if (ret) {
                dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n",
                        __func__, ret);
-               return ret;
+               goto err_select_sleep_state;
        }
 
        ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
        if (ret)
-               return ret;
+               goto err_disable_icc;
 
-       return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4);
+       return 0;
+
+err_disable_icc:
+       icc_disable(ctrl->icc_path_cpu_to_qspi);
+err_select_sleep_state:
+       pinctrl_pm_select_sleep_state(dev);
+err_opp_set_rate_zero:
+       dev_pm_opp_set_rate(dev, 0);
+       return ret;
 }
 
 static int __maybe_unused qcom_qspi_suspend(struct device *dev)