[PATCH 4/5] clk: qcom: common: Register clk_ref clocks from qcom_cc_desc
From: Stanislav Aleksandrov
Date: Sun Nov 08 2026 - 19:31:06 EST
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@xxxxxxxxx>
---
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