From: Stanislav Aleksandrov <lightofmysoul@gmail.com>
To: Bjorn Andersson <andersson@kernel.org>,
Abel Vesa <abelvesa@kernel.org>, Stephen Boyd <sboyd@kernel.org>,
Brian Masney <bmasney+clk@redhat.com>,
Jerome Brunet <jbrunet+clk@baylibre.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Taniya Das <taniya.das@oss.qualcomm.com>
Cc: Qiang Yu <qiang.yu@oss.qualcomm.com>,
Konrad Dybcio <konradybcio@kernel.org>,
linux-arm-msm@vger.kernel.org, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Stanislav Aleksandrov <lightofmysoul@gmail.com>
Subject: [PATCH 4/5] clk: qcom: common: Register clk_ref clocks from qcom_cc_desc
Date: Mon, 09 Nov 2026 00:29:13 +0000 [thread overview]
Message-ID: <20261109-halliday-clk-v1-4-20c3ba8be0bd@gmail.com> (raw)
In-Reply-To: <20261109-halliday-clk-v1-0-20c3ba8be0bd@gmail.com>
The clk_ref clocks model a clkref_en gate together with the supplies of
the QREF block that feeds it. So far they can only be registered as a
standalone clock provider with qcom_clk_ref_probe(), as on the TCSR of
X1E80100 and Glymur.
On Halliday the clkref_en registers are part of GCC, and the QREF
supplies are shared with the UFS PHY: when UFS runtime suspend turns
them off, the USB HS PHY loses its reference clock. The GCC driver
needs to provide its clkrefs as clk_ref clocks next to its other
clocks, so that their users keep the supplies on.
Let a qcom_cc_desc carry clk_ref descriptions, indexed by clock ID like
its other clocks. qcom_clk_ref_register() now registers a single clock,
so qcom_cc_really_probe() allocates and registers only the populated
entries, and qcom_cc_clk_hw_get() returns them for their indices. As
with the other clocks, entries listed in protected-clocks are skipped.
A clock ID used by both a clk_ref and a regmap clock fails the probe.
Signed-off-by: Stanislav Aleksandrov <lightofmysoul@gmail.com>
---
drivers/clk/qcom/clk-ref.c | 91 +++++++++++++++++++++-------------------------
drivers/clk/qcom/common.c | 60 ++++++++++++++++++++++++++++++
drivers/clk/qcom/common.h | 9 +++++
3 files changed, 110 insertions(+), 50 deletions(-)
diff --git a/drivers/clk/qcom/clk-ref.c b/drivers/clk/qcom/clk-ref.c
index 3efdf6749770..266acdfa5488 100644
--- a/drivers/clk/qcom/clk-ref.c
+++ b/drivers/clk/qcom/clk-ref.c
@@ -13,6 +13,8 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include "common.h"
+
#define QCOM_CLK_REF_EN_MASK BIT(0)
struct qcom_clk_ref_provider {
@@ -96,63 +98,46 @@ static const struct clk_ops qcom_clk_ref_ops = {
.is_enabled = qcom_clk_ref_is_enabled,
};
-static int qcom_clk_ref_register(struct device *dev, struct regmap *regmap,
- struct qcom_clk_ref *clk_refs,
- const struct qcom_clk_ref_desc * const *descs,
- size_t num_clk_refs)
+int qcom_clk_ref_register(struct device *dev, struct regmap *regmap,
+ struct qcom_clk_ref *clk_ref,
+ const struct qcom_clk_ref_desc *desc)
{
- const struct qcom_clk_ref_desc *desc;
struct clk_init_data init_data = {};
- struct qcom_clk_ref *clk_ref;
- size_t clk_idx;
unsigned int i;
int ret;
- for (clk_idx = 0; clk_idx < num_clk_refs; clk_idx++) {
- clk_ref = &clk_refs[clk_idx];
- desc = descs[clk_idx];
+ if (WARN_ON(!desc->name))
+ return -EINVAL;
- /* Skip unpopulated indices; the array is indexed by clock ID. */
- if (!desc)
- continue;
+ clk_ref->regmap = regmap;
+ clk_ref->desc = *desc;
- if (WARN_ON(!desc->name))
- continue;
+ if (clk_ref->desc.num_regulators) {
+ clk_ref->regulators = devm_kcalloc(dev, clk_ref->desc.num_regulators,
+ sizeof(*clk_ref->regulators),
+ GFP_KERNEL);
+ if (!clk_ref->regulators)
+ return -ENOMEM;
- clk_ref->regmap = regmap;
- clk_ref->desc = *desc;
-
- if (clk_ref->desc.num_regulators) {
- clk_ref->regulators = devm_kcalloc(dev, clk_ref->desc.num_regulators,
- sizeof(*clk_ref->regulators),
- GFP_KERNEL);
- if (!clk_ref->regulators)
- return -ENOMEM;
-
- for (i = 0; i < clk_ref->desc.num_regulators; i++)
- clk_ref->regulators[i].supply =
- clk_ref->desc.regulator_names[i];
-
- ret = devm_regulator_bulk_get(dev, clk_ref->desc.num_regulators,
- clk_ref->regulators);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to get regulators for %s\n",
- clk_ref->desc.name);
- }
-
- init_data.name = clk_ref->desc.name;
- init_data.parent_data = &qcom_clk_ref_parent_data;
- init_data.num_parents = 1;
- init_data.ops = &qcom_clk_ref_ops;
- clk_ref->hw.init = &init_data;
-
- ret = devm_clk_hw_register(dev, &clk_ref->hw);
+ for (i = 0; i < clk_ref->desc.num_regulators; i++)
+ clk_ref->regulators[i].supply =
+ clk_ref->desc.regulator_names[i];
+
+ ret = devm_regulator_bulk_get(dev, clk_ref->desc.num_regulators,
+ clk_ref->regulators);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret,
+ "Failed to get regulators for %s\n",
+ clk_ref->desc.name);
}
- return 0;
+ init_data.name = clk_ref->desc.name;
+ init_data.parent_data = &qcom_clk_ref_parent_data;
+ init_data.num_parents = 1;
+ init_data.ops = &qcom_clk_ref_ops;
+ clk_ref->hw.init = &init_data;
+
+ return devm_clk_hw_register(dev, &clk_ref->hw);
}
static struct clk_hw *qcom_clk_ref_provider_get(struct of_phandle_args *clkspec, void *data)
@@ -178,6 +163,7 @@ int qcom_clk_ref_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct regmap *regmap;
void __iomem *base;
+ size_t i;
int ret;
base = devm_platform_ioremap_resource(pdev, 0);
@@ -195,10 +181,15 @@ int qcom_clk_ref_probe(struct platform_device *pdev,
provider->num_refs = num_clk_refs;
- ret = qcom_clk_ref_register(dev, regmap, provider->refs, descs,
- provider->num_refs);
- if (ret)
- return ret;
+ for (i = 0; i < num_clk_refs; i++) {
+ /* Skip unpopulated indices; the array is indexed by clock ID. */
+ if (!descs[i])
+ continue;
+
+ ret = qcom_clk_ref_register(dev, regmap, &provider->refs[i], descs[i]);
+ if (ret)
+ return ret;
+ }
return devm_of_clk_add_hw_provider(dev, qcom_clk_ref_provider_get, provider);
}
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 08fd62ac780d..c767fa3d0207 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -8,6 +8,7 @@
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
+#include <linux/clk/qcom.h>
#include <linux/interconnect-clk.h>
#include <linux/pm_runtime.h>
#include <linux/reset-controller.h>
@@ -25,6 +26,8 @@ struct qcom_cc {
struct qcom_reset_controller reset;
struct clk_regmap **rclks;
size_t num_rclks;
+ struct qcom_clk_ref **clk_refs;
+ size_t num_clk_refs;
struct dev_pm_domain_list *pd_list;
};
@@ -247,6 +250,9 @@ static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
struct qcom_cc *cc = data;
unsigned int idx = clkspec->args[0];
+ if (idx < cc->num_clk_refs && cc->clk_refs[idx])
+ return &cc->clk_refs[idx]->hw;
+
if (idx >= cc->num_rclks) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
@@ -255,6 +261,54 @@ static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
}
+static bool qcom_cc_is_protected(struct device *dev, unsigned int idx)
+{
+ u32 i;
+
+ of_property_for_each_u32(dev->of_node, "protected-clocks", i) {
+ if (i == idx)
+ return true;
+ }
+
+ return false;
+}
+
+static int qcom_cc_register_clk_refs(struct device *dev, struct qcom_cc *cc,
+ const struct qcom_cc_desc *desc,
+ struct regmap *regmap)
+{
+ struct qcom_clk_ref *clk_ref;
+ size_t i;
+ int ret;
+
+ cc->clk_refs = devm_kcalloc(dev, desc->num_clk_refs, sizeof(*cc->clk_refs),
+ GFP_KERNEL);
+ if (!cc->clk_refs)
+ return -ENOMEM;
+
+ cc->num_clk_refs = desc->num_clk_refs;
+
+ for (i = 0; i < desc->num_clk_refs; i++) {
+ if (!desc->clk_refs[i] || qcom_cc_is_protected(dev, i))
+ continue;
+
+ if (WARN_ON(i < desc->num_clks && desc->clks[i]))
+ return -EINVAL;
+
+ clk_ref = devm_kzalloc(dev, sizeof(*clk_ref), GFP_KERNEL);
+ if (!clk_ref)
+ return -ENOMEM;
+
+ ret = qcom_clk_ref_register(dev, regmap, clk_ref, desc->clk_refs[i]);
+ if (ret)
+ return ret;
+
+ cc->clk_refs[i] = clk_ref;
+ }
+
+ return 0;
+}
+
static int qcom_cc_icc_register(struct device *dev,
const struct qcom_cc_desc *desc)
{
@@ -359,6 +413,12 @@ int qcom_cc_really_probe(struct device *dev,
qcom_cc_clk_regs_configure(dev, desc->driver_data, regmap);
}
+ if (desc->num_clk_refs) {
+ ret = qcom_cc_register_clk_refs(dev, cc, desc, regmap);
+ if (ret)
+ goto put_rpm;
+ }
+
reset = &cc->reset;
reset->rcdev.of_node = dev->of_node;
reset->rcdev.ops = &qcom_reset_ops;
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 6f2406f8839e..438fe5be56e1 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -19,6 +19,9 @@ struct clk_hw;
#define PLL_VOTE_FSM_ENA BIT(20)
#define PLL_VOTE_FSM_RESET BIT(21)
+struct qcom_clk_ref;
+struct qcom_clk_ref_desc;
+
struct qcom_icc_hws_data {
int master_id;
int slave_id;
@@ -45,6 +48,8 @@ struct qcom_cc_desc {
size_t num_gdscs;
struct clk_hw **clk_hws;
size_t num_clk_hws;
+ const struct qcom_clk_ref_desc * const *clk_refs;
+ size_t num_clk_refs;
const struct qcom_icc_hws_data *icc_hws;
size_t num_icc_hws;
unsigned int icc_first_node_id;
@@ -89,4 +94,8 @@ extern int qcom_cc_probe(struct platform_device *pdev,
extern int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
const struct qcom_cc_desc *desc);
+int qcom_clk_ref_register(struct device *dev, struct regmap *regmap,
+ struct qcom_clk_ref *clk_ref,
+ const struct qcom_clk_ref_desc *desc);
+
#endif
--
2.55.0
next prev parent reply other threads:[~2026-11-09 0:29 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 0:29 [PATCH 0/5] Add RPMh and global clock controller support for Qualcomm Halliday (SXR2250P) Stanislav Aleksandrov
2026-11-09 0:29 ` [PATCH 1/5] dt-bindings: clock: qcom: document the Halliday RPMh Clock Controller Stanislav Aleksandrov
2026-11-09 0:29 ` [PATCH 2/5] clk: qcom: rpmh: Add support for RPMh clocks on Halliday Stanislav Aleksandrov
2026-11-09 0:29 ` [PATCH 3/5] dt-bindings: clock: qcom: document the Halliday Global Clock Controller Stanislav Aleksandrov
2026-11-09 0:29 ` Stanislav Aleksandrov [this message]
2026-11-09 0:29 ` [PATCH 5/5] clk: qcom: Add support for Global clock controller on Halliday Stanislav Aleksandrov
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=20261109-halliday-clk-v1-4-20c3ba8be0bd@gmail.com \
--to=lightofmysoul@gmail.com \
--cc=abelvesa@kernel.org \
--cc=andersson@kernel.org \
--cc=bmasney+clk@redhat.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=jbrunet+clk@baylibre.com \
--cc=konradybcio@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=qiang.yu@oss.qualcomm.com \
--cc=robh@kernel.org \
--cc=sboyd@kernel.org \
--cc=taniya.das@oss.qualcomm.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®