summaryrefslogtreecommitdiffstats
path: root/drivers/clk/qcom/clk-ref.c
diff options
context:
space:
mode:
authorDapeng Mi <dapeng1.mi@linux.intel.com>2026-09-17 09:52:33 +0800
committerIngo Molnar <mingo@kernel.org>2026-09-22 11:37:46 +0200
commit0102c8c7fdfc4bd700971daff2b94470f4eafae4 (patch)
tree531306c2299f9a269f0a986e29f17ba972180104 /drivers/clk/qcom/clk-ref.c
downloadlinux-stable-0102c8c7fdfc4bd700971daff2b94470f4eafae4.tar.gz
linux-stable-0102c8c7fdfc4bd700971daff2b94470f4eafae4.zip
perf/x86/intel: Rename DMR offcore_rsp attribute to offmodule_rspgrafted
DMR introduces Offmodule Response events in place of the legacy Offcore Response events, but it still exposes the inherited offcore_rsp PMU attribute for programming the corresponding MSR data. Rename the DMR PMU attribute to offmodule_rsp so the sysfs interface matches the underlying event name and avoids user & tooling confusion. Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://patch.msgid.link/20260917015234.981153-12-dapeng1.mi@linux.intel.com
Diffstat (limited to 'drivers/clk/qcom/clk-ref.c')
-rw-r--r--drivers/clk/qcom/clk-ref.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/clk/qcom/clk-ref.c b/drivers/clk/qcom/clk-ref.c
new file mode 100644
index 000000000..3efdf6749
--- /dev/null
+++ b/drivers/clk/qcom/clk-ref.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk/qcom.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define QCOM_CLK_REF_EN_MASK BIT(0)
+
+struct qcom_clk_ref_provider {
+ size_t num_refs;
+ struct qcom_clk_ref refs[] __counted_by(num_refs);
+};
+
+static inline struct qcom_clk_ref *to_qcom_clk_ref(struct clk_hw *hw)
+{
+ return container_of(hw, struct qcom_clk_ref, hw);
+}
+
+static const struct clk_parent_data qcom_clk_ref_parent_data = {
+ .index = 0,
+};
+
+static int qcom_clk_ref_prepare(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+ int ret;
+
+ if (!rclk->desc.num_regulators)
+ return 0;
+
+ ret = regulator_bulk_enable(rclk->desc.num_regulators, rclk->regulators);
+ if (ret)
+ pr_err("Failed to enable regulators for %s: %d\n",
+ clk_hw_get_name(hw), ret);
+
+ return ret;
+}
+
+static void qcom_clk_ref_unprepare(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+
+ if (rclk->desc.num_regulators)
+ regulator_bulk_disable(rclk->desc.num_regulators, rclk->regulators);
+}
+
+static int qcom_clk_ref_enable(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+ int ret;
+
+ ret = regmap_set_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK);
+ if (ret)
+ return ret;
+
+ udelay(10);
+
+ return 0;
+}
+
+static void qcom_clk_ref_disable(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+
+ regmap_clear_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK);
+ udelay(10);
+}
+
+static int qcom_clk_ref_is_enabled(struct clk_hw *hw)
+{
+ struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(rclk->regmap, rclk->desc.offset, &val);
+ if (ret)
+ return 0;
+
+ return !!(val & QCOM_CLK_REF_EN_MASK);
+}
+
+static const struct clk_ops qcom_clk_ref_ops = {
+ .prepare = qcom_clk_ref_prepare,
+ .unprepare = qcom_clk_ref_unprepare,
+ .enable = qcom_clk_ref_enable,
+ .disable = qcom_clk_ref_disable,
+ .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)
+{
+ 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];
+
+ /* Skip unpopulated indices; the array is indexed by clock ID. */
+ if (!desc)
+ continue;
+
+ if (WARN_ON(!desc->name))
+ continue;
+
+ 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);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct clk_hw *qcom_clk_ref_provider_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct qcom_clk_ref_provider *provider = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= provider->num_refs)
+ return ERR_PTR(-EINVAL);
+
+ if (!provider->refs[idx].regmap)
+ return ERR_PTR(-ENOENT);
+
+ return &provider->refs[idx].hw;
+}
+
+int qcom_clk_ref_probe(struct platform_device *pdev,
+ const struct regmap_config *config,
+ const struct qcom_clk_ref_desc * const *descs,
+ size_t num_clk_refs)
+{
+ struct qcom_clk_ref_provider *provider;
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ void __iomem *base;
+ int ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(dev, base, config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ provider = devm_kzalloc(dev, struct_size(provider, refs, num_clk_refs),
+ GFP_KERNEL);
+ if (!provider)
+ return -ENOMEM;
+
+ provider->num_refs = num_clk_refs;
+
+ ret = qcom_clk_ref_register(dev, regmap, provider->refs, descs,
+ provider->num_refs);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, qcom_clk_ref_provider_get, provider);
+}
+EXPORT_SYMBOL_GPL(qcom_clk_ref_probe);