[PATCH RFC 2/3] clk: qcom: common: Restore the clock controller after hibernation
From: Abel Vesa
Date: Wed Oct 28 2026 - 05:54:55 EST
Nothing puts a clock controller back into the state the clock framework
expects after a hibernation image is restored. The hardware has been
through a power cycle and through the kernel that loaded the image,
which may have programmed other rates and parents, or not probed the
controller at all, depending on whether its driver is built in or
available from the initramfs. The rates and parents the consumers asked
for before the image was created are not programmed again either, since
the framework sees no change.
On the Glymur CRD, the display clock controller driver is a module that
the kernel loading the image does not probe, so the probe time PLL
configuration is missing and the DPU runs at the 333MHz the boot
firmware left the PLL at instead of the 717MHz the framework believes,
underflows, and the panel shows a solid blue screen.
Add a restore callback that redoes the probe time configuration and
restores the cached state of the clocks, for the drivers to use.
Signed-off-by: Abel Vesa <abel.vesa@xxxxxxxxxxxxxxxx>
---
drivers/clk/qcom/common.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++-
drivers/clk/qcom/common.h | 2 ++
2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 08fd62ac780d..ff243fc78188 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/clk-provider.h>
#include <linux/interconnect-clk.h>
#include <linux/pm_runtime.h>
@@ -26,6 +27,8 @@ struct qcom_cc {
struct clk_regmap **rclks;
size_t num_rclks;
struct dev_pm_domain_list *pd_list;
+ const struct qcom_cc_desc *desc;
+ struct regmap *regmap;
};
const
@@ -321,6 +324,10 @@ static void qcom_cc_clk_regs_configure(struct device *dev, const struct qcom_cc_
data->clk_regs_configure(dev, regmap);
}
+static void qcom_cc_release(struct device *dev, void *res)
+{
+}
+
int qcom_cc_really_probe(struct device *dev,
const struct qcom_cc_desc *desc, struct regmap *regmap)
{
@@ -333,9 +340,13 @@ int qcom_cc_really_probe(struct device *dev,
size_t num_clk_hws = desc->num_clk_hws;
struct clk_hw **clk_hws = desc->clk_hws;
- cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
+ cc = devres_alloc(qcom_cc_release, sizeof(*cc), GFP_KERNEL);
if (!cc)
return -ENOMEM;
+ devres_add(dev, cc);
+
+ cc->desc = desc;
+ cc->regmap = regmap;
ret = devm_pm_domain_attach_list(dev, NULL, &cc->pd_list);
if (ret < 0 && ret != -EEXIST)
@@ -434,6 +445,45 @@ int qcom_cc_really_probe(struct device *dev,
}
EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
+/* Redo the probe time setup and the cached clock state after a power cycle */
+static int qcom_cc_restore(struct device *dev)
+{
+ struct qcom_cc *cc = devres_find(dev, qcom_cc_release, NULL, NULL);
+ const struct qcom_cc_desc *desc;
+ int ret = 0;
+
+ if (!cc)
+ return 0;
+
+ desc = cc->desc;
+
+ if (desc->use_rpm) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+ }
+
+ if (desc->driver_data) {
+ ret = qcom_cc_clk_pll_configure(desc->driver_data, cc->regmap);
+ if (ret)
+ goto put_rpm;
+
+ qcom_cc_clk_regs_configure(dev, desc->driver_data, cc->regmap);
+ }
+
+ clk_restore_context_dev(dev);
+
+put_rpm:
+ if (desc->use_rpm)
+ pm_runtime_put_sync(dev);
+
+ return ret;
+}
+
+EXPORT_GPL_DEV_PM_OPS(qcom_cc_pm_ops) = {
+ .restore = pm_sleep_ptr(qcom_cc_restore),
+};
+
int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
{
struct regmap *regmap;
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 6f2406f8839e..227669609a50 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -4,6 +4,7 @@
#ifndef __QCOM_CLK_COMMON_H__
#define __QCOM_CLK_COMMON_H__
+struct dev_pm_ops;
struct platform_device;
struct regmap_config;
struct clk_regmap;
@@ -86,6 +87,7 @@ extern int qcom_cc_really_probe(struct device *dev,
struct regmap *regmap);
extern int qcom_cc_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc);
+extern const struct dev_pm_ops qcom_cc_pm_ops;
extern int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
const struct qcom_cc_desc *desc);
--
2.54.0