[PATCH v3 2/7] spi: qcom-qspi: Fix incomplete error handling in runtime PM

Viken Dadhaniya
2026-04-20T06:13:17Z

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>
---
drivers/spi/spi-qcom-qspi.c | 43 +++++++++++++++++++++++++++++++++++--------
1 file changed, 35 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c
index 7e39038160e0..8496c4a9f642 100644
--- a/drivers/spi/spi-qcom-qspi.c
+++ b/drivers/spi/spi-qcom-qspi.c
@@ -818,20 +818,33 @@ 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:
+ clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
+ return ret;
}

static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
@@ -840,20 +853,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)

--
2.34.1