From: sashiko-bot@kernel.org
To: "Abel Vesa" <abel.vesa@oss.qualcomm.com>
Cc: linux-clk@vger.kernel.org
Subject: Re: [PATCH RFC 2/3] clk: qcom: common: Restore the clock controller after hibernation
Date: Wed, 28 Oct 2026 10:44:18 +0000 [thread overview]
Message-ID: <sashiko-outbox-187761@kernel.org> (raw)
In-Reply-To: <20261028-b4-clk-restore-after-hibernation-v1-2-86cf62747a7f@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] clk: qcom: common: dangling stack pointer in cc->desc after probe
- [High] clk: qcom: common: qcom_cc_restore() corrupts clk_rcg2_shared_ops state
--
commit a92f46011ef8a0320b3784fea1ac76fde1a0f05f
Author: Abel Vesa <abel.vesa@oss.qualcomm.com>
clk: qcom: common: Restore the clock controller after hibernation
This commit adds a hibernation restore callback to qcom_cc_pm_ops that
redoes probe-time PLL and register configuration and calls
clk_restore_context_dev() to restore cached clock state after hibernation.
> diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
> index 08fd62ac780d3..ff243fc781887 100644
> --- a/drivers/clk/qcom/common.c
> +++ b/drivers/clk/qcom/common.c
[ ... ]
> @@ -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;
[Severity: High]
Can storing the desc pointer in cc->desc leave a dangling stack pointer after
probe returns?
Several Qualcomm clock controller drivers allocate a local struct qcom_cc_desc
on the stack in their probe functions to customize fields per SoC variant and
pass &desc to qcom_cc_really_probe():
drivers/clk/qcom/camcc-sa8775p.c:cam_cc_sa8775p_probe() {
struct qcom_cc_desc desc = cam_cc_sa8775p_desc;
...
desc.driver_data = &cam_cc_qcs8300_driver_data;
}
ret = qcom_cc_really_probe(&pdev->dev, &desc, regmap);
}
drivers/clk/qcom/videocc-sm8350.c:video_cc_sm8350_probe() {
struct qcom_cc_desc desc = video_cc_sm8350_desc;
...
desc.driver_data = &video_cc_sc8280xp_driver_data;
...
ret = qcom_cc_really_probe(&pdev->dev, &desc, regmap);
}
drivers/clk/qcom/gcc-ipq5018.c:gcc_ipq5018_probe() {
struct qcom_cc_desc ipq5018_desc = gcc_ipq5018_desc;
...
return qcom_cc_really_probe(&pdev->dev, &ipq5018_desc, regmap);
}
When qcom_cc_restore() later dereferences cc->desc (checking desc->use_rpm and
desc->driver_data), the probe stack frame has already been reclaimed. Would it
be safer to store desc->use_rpm and desc->driver_data directly in struct
qcom_cc instead of retaining the desc pointer?
>
> 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);
[Severity: High]
Does calling clk_restore_context_dev() here corrupt the hardware state of
clocks using clk_rcg2_shared_ops (as well as clk_rcg2_shared_floor_ops and
clk_rcg2_shared_no_init_park_ops)?
In clk_core_restore_state(), the clock framework restores a clock by calling
set_parent(), set_rate(), prepare(), and enable() in sequence:
drivers/clk/clk.c:clk_core_restore_state() {
if (parent && core->num_parents > 1 && core->ops->set_parent) {
index = clk_fetch_parent_index(core, parent);
if (index >= 0)
core->ops->set_parent(core->hw, index);
}
if (core->rate && core->ops->set_rate)
core->ops->set_rate(core->hw, core->rate,
parent ? parent->rate : 0);
if (core->prepare_count && core->ops->prepare)
core->ops->prepare(core->hw);
if (core->enable_count && core->ops->enable) {
flags = clk_enable_lock();
core->ops->enable(core->hw);
clk_enable_unlock(flags);
}
}
Because clk_rcg2_shared_ops does not implement .is_enabled,
clk_hw_is_enabled(hw) evaluates to (core->enable_count > 0).
When a shared RCG is enabled across hibernation (core->enable_count > 0),
clk_rcg2_shared_set_parent() and __clk_rcg2_shared_set_rate() see
clk_hw_is_enabled(hw) == true and write the new configuration directly to
hardware CFG_REG without updating rcg->parked_cfg:
drivers/clk/qcom/clk-rcg2.c:__clk_rcg2_shared_set_rate() {
...
if (!clk_hw_is_enabled(hw))
return __clk_rcg2_configure(rcg, f, &rcg->parked_cfg);
return clk_rcg2_shared_force_enable_clear(hw, f);
}
Immediately afterwards, clk_core_restore_state() invokes core->ops->enable(),
which calls clk_rcg2_shared_enable():
drivers/clk/qcom/clk-rcg2.c:clk_rcg2_shared_enable() {
...
/* Write back the stored configuration corresponding to current rate */
ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, rcg->parked_cfg);
if (ret)
return ret;
ret = update_config(rcg);
...
}
Since runtime rate and parent changes while the clock was enabled never
updated rcg->parked_cfg, won't clk_rcg2_shared_enable() overwrite CFG_REG with
a stale rcg->parked_cfg and clobber the rate and parent just programmed by
set_parent() and set_rate()?
Conversely, when core->enable_count == 0, clk_core_restore_state() does not
call .init (clk_rcg2_shared_init()) or .disable, and set_parent()/set_rate()
only update rcg->parked_cfg in memory without writing to hardware registers,
leaving any RCG enabled by boot firmware unparked in hardware.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-b4-clk-restore-after-hibernation-v1-0-86cf62747a7f@oss.qualcomm.com?part=2
next prev parent reply other threads:[~2026-10-28 10:44 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 9:52 [PATCH RFC 0/3] clk: Restore the clocks of a provider after a hibernation Abel Vesa
2026-10-28 9:52 ` [PATCH RFC 1/3] clk: Add a way to restore the clocks of a provider after a power cycle Abel Vesa
2026-10-28 10:13 ` sashiko-bot
2026-10-28 9:52 ` [PATCH RFC 2/3] clk: qcom: common: Restore the clock controller after hibernation Abel Vesa
2026-10-28 10:44 ` sashiko-bot [this message]
2026-10-28 9:52 ` [PATCH RFC 3/3] clk: qcom: dispcc-glymur: Restore the clocks " Abel Vesa
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-187761@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=abel.vesa@oss.qualcomm.com \
--cc=linux-clk@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.