xref: /linux/drivers/clk/qcom/clk-ref.c (revision 502d45774af09f1c681c754c4b7cdfb5d7f72fd9)
1*22c70e57SQiang Yu // SPDX-License-Identifier: GPL-2.0
2*22c70e57SQiang Yu /*
3*22c70e57SQiang Yu  * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
4*22c70e57SQiang Yu  */
5*22c70e57SQiang Yu 
6*22c70e57SQiang Yu #include <linux/clk-provider.h>
7*22c70e57SQiang Yu #include <linux/clk/qcom.h>
8*22c70e57SQiang Yu #include <linux/delay.h>
9*22c70e57SQiang Yu #include <linux/err.h>
10*22c70e57SQiang Yu #include <linux/export.h>
11*22c70e57SQiang Yu #include <linux/platform_device.h>
12*22c70e57SQiang Yu #include <linux/regmap.h>
13*22c70e57SQiang Yu #include <linux/regulator/consumer.h>
14*22c70e57SQiang Yu #include <linux/slab.h>
15*22c70e57SQiang Yu 
16*22c70e57SQiang Yu #define QCOM_CLK_REF_EN_MASK BIT(0)
17*22c70e57SQiang Yu 
18*22c70e57SQiang Yu struct qcom_clk_ref_provider {
19*22c70e57SQiang Yu 	size_t num_refs;
20*22c70e57SQiang Yu 	struct qcom_clk_ref refs[] __counted_by(num_refs);
21*22c70e57SQiang Yu };
22*22c70e57SQiang Yu 
to_qcom_clk_ref(struct clk_hw * hw)23*22c70e57SQiang Yu static inline struct qcom_clk_ref *to_qcom_clk_ref(struct clk_hw *hw)
24*22c70e57SQiang Yu {
25*22c70e57SQiang Yu 	return container_of(hw, struct qcom_clk_ref, hw);
26*22c70e57SQiang Yu }
27*22c70e57SQiang Yu 
28*22c70e57SQiang Yu static const struct clk_parent_data qcom_clk_ref_parent_data = {
29*22c70e57SQiang Yu 	.index = 0,
30*22c70e57SQiang Yu };
31*22c70e57SQiang Yu 
qcom_clk_ref_prepare(struct clk_hw * hw)32*22c70e57SQiang Yu static int qcom_clk_ref_prepare(struct clk_hw *hw)
33*22c70e57SQiang Yu {
34*22c70e57SQiang Yu 	struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
35*22c70e57SQiang Yu 	int ret;
36*22c70e57SQiang Yu 
37*22c70e57SQiang Yu 	if (!rclk->desc.num_regulators)
38*22c70e57SQiang Yu 		return 0;
39*22c70e57SQiang Yu 
40*22c70e57SQiang Yu 	ret = regulator_bulk_enable(rclk->desc.num_regulators, rclk->regulators);
41*22c70e57SQiang Yu 	if (ret)
42*22c70e57SQiang Yu 		pr_err("Failed to enable regulators for %s: %d\n",
43*22c70e57SQiang Yu 		       clk_hw_get_name(hw), ret);
44*22c70e57SQiang Yu 
45*22c70e57SQiang Yu 	return ret;
46*22c70e57SQiang Yu }
47*22c70e57SQiang Yu 
qcom_clk_ref_unprepare(struct clk_hw * hw)48*22c70e57SQiang Yu static void qcom_clk_ref_unprepare(struct clk_hw *hw)
49*22c70e57SQiang Yu {
50*22c70e57SQiang Yu 	struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
51*22c70e57SQiang Yu 
52*22c70e57SQiang Yu 	if (rclk->desc.num_regulators)
53*22c70e57SQiang Yu 		regulator_bulk_disable(rclk->desc.num_regulators, rclk->regulators);
54*22c70e57SQiang Yu }
55*22c70e57SQiang Yu 
qcom_clk_ref_enable(struct clk_hw * hw)56*22c70e57SQiang Yu static int qcom_clk_ref_enable(struct clk_hw *hw)
57*22c70e57SQiang Yu {
58*22c70e57SQiang Yu 	struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
59*22c70e57SQiang Yu 	int ret;
60*22c70e57SQiang Yu 
61*22c70e57SQiang Yu 	ret = regmap_set_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK);
62*22c70e57SQiang Yu 	if (ret)
63*22c70e57SQiang Yu 		return ret;
64*22c70e57SQiang Yu 
65*22c70e57SQiang Yu 	udelay(10);
66*22c70e57SQiang Yu 
67*22c70e57SQiang Yu 	return 0;
68*22c70e57SQiang Yu }
69*22c70e57SQiang Yu 
qcom_clk_ref_disable(struct clk_hw * hw)70*22c70e57SQiang Yu static void qcom_clk_ref_disable(struct clk_hw *hw)
71*22c70e57SQiang Yu {
72*22c70e57SQiang Yu 	struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
73*22c70e57SQiang Yu 
74*22c70e57SQiang Yu 	regmap_clear_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK);
75*22c70e57SQiang Yu 	udelay(10);
76*22c70e57SQiang Yu }
77*22c70e57SQiang Yu 
qcom_clk_ref_is_enabled(struct clk_hw * hw)78*22c70e57SQiang Yu static int qcom_clk_ref_is_enabled(struct clk_hw *hw)
79*22c70e57SQiang Yu {
80*22c70e57SQiang Yu 	struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
81*22c70e57SQiang Yu 	u32 val;
82*22c70e57SQiang Yu 	int ret;
83*22c70e57SQiang Yu 
84*22c70e57SQiang Yu 	ret = regmap_read(rclk->regmap, rclk->desc.offset, &val);
85*22c70e57SQiang Yu 	if (ret)
86*22c70e57SQiang Yu 		return 0;
87*22c70e57SQiang Yu 
88*22c70e57SQiang Yu 	return !!(val & QCOM_CLK_REF_EN_MASK);
89*22c70e57SQiang Yu }
90*22c70e57SQiang Yu 
91*22c70e57SQiang Yu static const struct clk_ops qcom_clk_ref_ops = {
92*22c70e57SQiang Yu 	.prepare = qcom_clk_ref_prepare,
93*22c70e57SQiang Yu 	.unprepare = qcom_clk_ref_unprepare,
94*22c70e57SQiang Yu 	.enable = qcom_clk_ref_enable,
95*22c70e57SQiang Yu 	.disable = qcom_clk_ref_disable,
96*22c70e57SQiang Yu 	.is_enabled = qcom_clk_ref_is_enabled,
97*22c70e57SQiang Yu };
98*22c70e57SQiang Yu 
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)99*22c70e57SQiang Yu static int qcom_clk_ref_register(struct device *dev, struct regmap *regmap,
100*22c70e57SQiang Yu 				 struct qcom_clk_ref *clk_refs,
101*22c70e57SQiang Yu 				 const struct qcom_clk_ref_desc * const *descs,
102*22c70e57SQiang Yu 				 size_t num_clk_refs)
103*22c70e57SQiang Yu {
104*22c70e57SQiang Yu 	const struct qcom_clk_ref_desc *desc;
105*22c70e57SQiang Yu 	struct clk_init_data init_data = {};
106*22c70e57SQiang Yu 	struct qcom_clk_ref *clk_ref;
107*22c70e57SQiang Yu 	size_t clk_idx;
108*22c70e57SQiang Yu 	unsigned int i;
109*22c70e57SQiang Yu 	int ret;
110*22c70e57SQiang Yu 
111*22c70e57SQiang Yu 	for (clk_idx = 0; clk_idx < num_clk_refs; clk_idx++) {
112*22c70e57SQiang Yu 		clk_ref = &clk_refs[clk_idx];
113*22c70e57SQiang Yu 		desc = descs[clk_idx];
114*22c70e57SQiang Yu 
115*22c70e57SQiang Yu 		/* Skip unpopulated indices; the array is indexed by clock ID. */
116*22c70e57SQiang Yu 		if (!desc)
117*22c70e57SQiang Yu 			continue;
118*22c70e57SQiang Yu 
119*22c70e57SQiang Yu 		if (WARN_ON(!desc->name))
120*22c70e57SQiang Yu 			continue;
121*22c70e57SQiang Yu 
122*22c70e57SQiang Yu 		clk_ref->regmap = regmap;
123*22c70e57SQiang Yu 		clk_ref->desc = *desc;
124*22c70e57SQiang Yu 
125*22c70e57SQiang Yu 		if (clk_ref->desc.num_regulators) {
126*22c70e57SQiang Yu 			clk_ref->regulators = devm_kcalloc(dev, clk_ref->desc.num_regulators,
127*22c70e57SQiang Yu 							   sizeof(*clk_ref->regulators),
128*22c70e57SQiang Yu 							   GFP_KERNEL);
129*22c70e57SQiang Yu 			if (!clk_ref->regulators)
130*22c70e57SQiang Yu 				return -ENOMEM;
131*22c70e57SQiang Yu 
132*22c70e57SQiang Yu 			for (i = 0; i < clk_ref->desc.num_regulators; i++)
133*22c70e57SQiang Yu 				clk_ref->regulators[i].supply =
134*22c70e57SQiang Yu 					clk_ref->desc.regulator_names[i];
135*22c70e57SQiang Yu 
136*22c70e57SQiang Yu 			ret = devm_regulator_bulk_get(dev, clk_ref->desc.num_regulators,
137*22c70e57SQiang Yu 						      clk_ref->regulators);
138*22c70e57SQiang Yu 			if (ret)
139*22c70e57SQiang Yu 				return dev_err_probe(dev, ret,
140*22c70e57SQiang Yu 						     "Failed to get regulators for %s\n",
141*22c70e57SQiang Yu 						     clk_ref->desc.name);
142*22c70e57SQiang Yu 		}
143*22c70e57SQiang Yu 
144*22c70e57SQiang Yu 		init_data.name = clk_ref->desc.name;
145*22c70e57SQiang Yu 		init_data.parent_data = &qcom_clk_ref_parent_data;
146*22c70e57SQiang Yu 		init_data.num_parents = 1;
147*22c70e57SQiang Yu 		init_data.ops = &qcom_clk_ref_ops;
148*22c70e57SQiang Yu 		clk_ref->hw.init = &init_data;
149*22c70e57SQiang Yu 
150*22c70e57SQiang Yu 		ret = devm_clk_hw_register(dev, &clk_ref->hw);
151*22c70e57SQiang Yu 		if (ret)
152*22c70e57SQiang Yu 			return ret;
153*22c70e57SQiang Yu 	}
154*22c70e57SQiang Yu 
155*22c70e57SQiang Yu 	return 0;
156*22c70e57SQiang Yu }
157*22c70e57SQiang Yu 
qcom_clk_ref_provider_get(struct of_phandle_args * clkspec,void * data)158*22c70e57SQiang Yu static struct clk_hw *qcom_clk_ref_provider_get(struct of_phandle_args *clkspec, void *data)
159*22c70e57SQiang Yu {
160*22c70e57SQiang Yu 	struct qcom_clk_ref_provider *provider = data;
161*22c70e57SQiang Yu 	unsigned int idx = clkspec->args[0];
162*22c70e57SQiang Yu 
163*22c70e57SQiang Yu 	if (idx >= provider->num_refs)
164*22c70e57SQiang Yu 		return ERR_PTR(-EINVAL);
165*22c70e57SQiang Yu 
166*22c70e57SQiang Yu 	if (!provider->refs[idx].regmap)
167*22c70e57SQiang Yu 		return ERR_PTR(-ENOENT);
168*22c70e57SQiang Yu 
169*22c70e57SQiang Yu 	return &provider->refs[idx].hw;
170*22c70e57SQiang Yu }
171*22c70e57SQiang Yu 
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)172*22c70e57SQiang Yu int qcom_clk_ref_probe(struct platform_device *pdev,
173*22c70e57SQiang Yu 		       const struct regmap_config *config,
174*22c70e57SQiang Yu 		       const struct qcom_clk_ref_desc * const *descs,
175*22c70e57SQiang Yu 		       size_t num_clk_refs)
176*22c70e57SQiang Yu {
177*22c70e57SQiang Yu 	struct qcom_clk_ref_provider *provider;
178*22c70e57SQiang Yu 	struct device *dev = &pdev->dev;
179*22c70e57SQiang Yu 	struct regmap *regmap;
180*22c70e57SQiang Yu 	void __iomem *base;
181*22c70e57SQiang Yu 	int ret;
182*22c70e57SQiang Yu 
183*22c70e57SQiang Yu 	base = devm_platform_ioremap_resource(pdev, 0);
184*22c70e57SQiang Yu 	if (IS_ERR(base))
185*22c70e57SQiang Yu 		return PTR_ERR(base);
186*22c70e57SQiang Yu 
187*22c70e57SQiang Yu 	regmap = devm_regmap_init_mmio(dev, base, config);
188*22c70e57SQiang Yu 	if (IS_ERR(regmap))
189*22c70e57SQiang Yu 		return PTR_ERR(regmap);
190*22c70e57SQiang Yu 
191*22c70e57SQiang Yu 	provider = devm_kzalloc(dev, struct_size(provider, refs, num_clk_refs),
192*22c70e57SQiang Yu 				GFP_KERNEL);
193*22c70e57SQiang Yu 	if (!provider)
194*22c70e57SQiang Yu 		return -ENOMEM;
195*22c70e57SQiang Yu 
196*22c70e57SQiang Yu 	provider->num_refs = num_clk_refs;
197*22c70e57SQiang Yu 
198*22c70e57SQiang Yu 	ret = qcom_clk_ref_register(dev, regmap, provider->refs, descs,
199*22c70e57SQiang Yu 				    provider->num_refs);
200*22c70e57SQiang Yu 	if (ret)
201*22c70e57SQiang Yu 		return ret;
202*22c70e57SQiang Yu 
203*22c70e57SQiang Yu 	return devm_of_clk_add_hw_provider(dev, qcom_clk_ref_provider_get, provider);
204*22c70e57SQiang Yu }
205*22c70e57SQiang Yu EXPORT_SYMBOL_GPL(qcom_clk_ref_probe);
206