File Index
Files and directories
Sign in
FILE

common.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/qcom/common.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  4 */
  5
  6#include <linux/export.h>
  7#include <linux/module.h>
  8#include <linux/regmap.h>
  9#include <linux/platform_device.h>
 10#include <linux/clk-provider.h>
 11#include <linux/interconnect-clk.h>
 12#include <linux/pm_runtime.h>
 13#include <linux/reset-controller.h>
 14#include <linux/of.h>
 15
 16#include "common.h"
 17#include "clk-alpha-pll.h"
 18#include "clk-branch.h"
 19#include "clk-rcg.h"
 20#include "clk-regmap.h"
 21#include "reset.h"
 22#include "gdsc.h"
 23
 24struct qcom_cc {
 25	struct qcom_reset_controller reset;
 26	struct clk_regmap **rclks;
 27	size_t num_rclks;
 28	struct dev_pm_domain_list *pd_list;
 29};
 30
 31const
 32struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
 33{
 34	if (!f)
 35		return NULL;
 36
 37	if (!f->freq)
 38		return f;
 39
 40	for (; f->freq; f++)
 41		if (rate <= f->freq)
 42			return f;
 43
 44	/* Default to our fastest rate */
 45	return f - 1;
 46}
 47EXPORT_SYMBOL_GPL(qcom_find_freq);
 48
 49const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
 50						  unsigned long rate)
 51{
 52	if (!f)
 53		return NULL;
 54
 55	if (!f->freq)
 56		return f;
 57
 58	for (; f->freq; f++)
 59		if (rate <= f->freq)
 60			return f;
 61
 62	/* Default to our fastest rate */
 63	return f - 1;
 64}
 65EXPORT_SYMBOL_GPL(qcom_find_freq_multi);
 66
 67const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
 68					    unsigned long rate)
 69{
 70	const struct freq_tbl *best = NULL;
 71
 72	for ( ; f->freq; f++) {
 73		if (rate >= f->freq)
 74			best = f;
 75		else
 76			break;
 77	}
 78
 79	return best;
 80}
 81EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
 82
 83int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
 84{
 85	int i, num_parents = clk_hw_get_num_parents(hw);
 86
 87	for (i = 0; i < num_parents; i++)
 88		if (src == map[i].src)
 89			return i;
 90
 91	return -ENOENT;
 92}
 93EXPORT_SYMBOL_GPL(qcom_find_src_index);
 94
 95int qcom_find_cfg_index(struct clk_hw *hw, const struct parent_map *map, u8 cfg)
 96{
 97	int i, num_parents = clk_hw_get_num_parents(hw);
 98
 99	for (i = 0; i < num_parents; i++)
100		if (cfg == map[i].cfg)
101			return i;
102
103	return -ENOENT;
104}
105EXPORT_SYMBOL_GPL(qcom_find_cfg_index);
106
107struct regmap *
108qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
109{
110	void __iomem *base;
111	struct device *dev = &pdev->dev;
112
113	base = devm_platform_ioremap_resource(pdev, 0);
114	if (IS_ERR(base))
115		return ERR_CAST(base);
116
117	return devm_regmap_init_mmio(dev, base, desc->config);
118}
119EXPORT_SYMBOL_GPL(qcom_cc_map);
120
121void
122qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count)
123{
124	u32 val;
125	u32 mask;
126
127	/* De-assert reset to FSM */
128	regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0);
129
130	/* Program bias count and lock count */
131	val = bias_count << PLL_BIAS_COUNT_SHIFT |
132		lock_count << PLL_LOCK_COUNT_SHIFT;
133	mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
134	mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
135	regmap_update_bits(map, reg, mask, val);
136
137	/* Enable PLL FSM voting */
138	regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);
139}
140EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode);
141
142static void qcom_cc_gdsc_unregister(void *data)
143{
144	gdsc_unregister(data);
145}
146
147/*
148 * Backwards compatibility with old DTs. Register a pass-through factor 1/1
149 * clock to translate 'path' clk into 'name' clk and register the 'path'
150 * clk as a fixed rate clock if it isn't present.
151 */
152static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
153				       const char *name, unsigned long rate,
154				       bool add_factor)
155{
156	struct device_node *node = NULL;
157	struct device_node *clocks_node;
158	struct clk_fixed_factor *factor;
159	struct clk_fixed_rate *fixed;
160	struct clk_init_data init_data = { };
161	int ret;
162
163	clocks_node = of_find_node_by_path("/clocks");
164	if (clocks_node) {
165		node = of_get_child_by_name(clocks_node, path);
166		of_node_put(clocks_node);
167	}
168
169	if (!node) {
170		fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
171		if (!fixed)
172			return -EINVAL;
173
174		fixed->fixed_rate = rate;
175		fixed->hw.init = &init_data;
176
177		init_data.name = path;
178		init_data.ops = &clk_fixed_rate_ops;
179
180		ret = devm_clk_hw_register(dev, &fixed->hw);
181		if (ret)
182			return ret;
183	}
184	of_node_put(node);
185
186	if (add_factor) {
187		factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
188		if (!factor)
189			return -EINVAL;
190
191		factor->mult = factor->div = 1;
192		factor->hw.init = &init_data;
193
194		init_data.name = name;
195		init_data.parent_names = &path;
196		init_data.num_parents = 1;
197		init_data.flags = 0;
198		init_data.ops = &clk_fixed_factor_ops;
199
200		ret = devm_clk_hw_register(dev, &factor->hw);
201		if (ret)
202			return ret;
203	}
204
205	return 0;
206}
207
208int qcom_cc_register_board_clk(struct device *dev, const char *path,
209			       const char *name, unsigned long rate)
210{
211	bool add_factor = true;
212
213	/*
214	 * TODO: The RPM clock driver currently does not support the xo clock.
215	 * When xo is added to the RPM clock driver, we should change this
216	 * function to skip registration of xo factor clocks.
217	 */
218
219	return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
220}
221EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
222
223int qcom_cc_register_sleep_clk(struct device *dev)
224{
225	return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
226					   32768, true);
227}
228EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
229
230/* Drop 'protected-clocks' from the list of clocks to register */
231static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
232{
233	struct device_node *np = dev->of_node;
234	u32 i;
235
236	of_property_for_each_u32(np, "protected-clocks", i) {
237		if (i >= cc->num_rclks)
238			continue;
239
240		cc->rclks[i] = NULL;
241	}
242}
243
244static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
245					 void *data)
246{
247	struct qcom_cc *cc = data;
248	unsigned int idx = clkspec->args[0];
249
250	if (idx >= cc->num_rclks) {
251		pr_err("%s: invalid index %u\n", __func__, idx);
252		return ERR_PTR(-EINVAL);
253	}
254
255	return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
256}
257
258static int qcom_cc_icc_register(struct device *dev,
259				const struct qcom_cc_desc *desc)
260{
261	struct icc_clk_data *icd;
262	struct clk_hw *hws;
263	int i;
264
265	if (!IS_ENABLED(CONFIG_INTERCONNECT_CLK))
266		return 0;
267
268	if (!desc->icc_hws)
269		return 0;
270
271	icd = devm_kcalloc(dev, desc->num_icc_hws, sizeof(*icd), GFP_KERNEL);
272	if (!icd)
273		return -ENOMEM;
274
275	for (i = 0; i < desc->num_icc_hws; i++) {
276		icd[i].master_id = desc->icc_hws[i].master_id;
277		icd[i].slave_id = desc->icc_hws[i].slave_id;
278		hws = &desc->clks[desc->icc_hws[i].clk_id]->hw;
279		icd[i].clk = devm_clk_hw_get_clk(dev, hws, "icc");
280		if (IS_ERR(icd[i].clk))
281			return dev_err_probe(dev, PTR_ERR(icd[i].clk),
282					     "(%d) clock entry is null\n", i);
283		icd[i].name = clk_hw_get_name(hws);
284	}
285
286	return devm_icc_clk_register(dev, desc->icc_first_node_id,
287						     desc->num_icc_hws, icd);
288}
289
290static int qcom_cc_clk_pll_configure(const struct qcom_cc_driver_data *data,
291				     struct regmap *regmap)
292{
293	const struct clk_init_data *init;
294	struct clk_alpha_pll *pll;
295	int i;
296
297	for (i = 0; i < data->num_alpha_plls; i++) {
298		pll = data->alpha_plls[i];
299		init = pll->clkr.hw.init;
300
301		if (!pll->config || !pll->regs) {
302			pr_err("%s: missing pll config or regs\n", init->name);
303			return -EINVAL;
304		}
305
306		qcom_clk_alpha_pll_configure(pll, regmap);
307	}
308
309	return 0;
310}
311
312static void qcom_cc_clk_regs_configure(struct device *dev, const struct qcom_cc_driver_data *data,
313				       struct regmap *regmap)
314{
315	int i;
316
317	for (i = 0; i < data->num_clk_cbcrs; i++)
318		qcom_branch_set_clk_en(regmap, data->clk_cbcrs[i]);
319
320	if (data->clk_regs_configure)
321		data->clk_regs_configure(dev, regmap);
322}
323
324int qcom_cc_really_probe(struct device *dev,
325			 const struct qcom_cc_desc *desc, struct regmap *regmap)
326{
327	int i, ret;
328	struct qcom_reset_controller *reset;
329	struct qcom_cc *cc;
330	struct gdsc_desc *scd;
331	size_t num_clks = desc->num_clks;
332	struct clk_regmap **rclks = desc->clks;
333	size_t num_clk_hws = desc->num_clk_hws;
334	struct clk_hw **clk_hws = desc->clk_hws;
335
336	cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
337	if (!cc)
338		return -ENOMEM;
339
340	ret = devm_pm_domain_attach_list(dev, NULL, &cc->pd_list);
341	if (ret < 0 && ret != -EEXIST)
342		return ret;
343
344	if (desc->use_rpm) {
345		ret = devm_pm_runtime_enable(dev);
346		if (ret)
347			return ret;
348
349		ret = pm_runtime_resume_and_get(dev);
350		if (ret)
351			return ret;
352	}
353
354	if (desc->driver_data) {
355		ret = qcom_cc_clk_pll_configure(desc->driver_data, regmap);
356		if (ret)
357			goto put_rpm;
358
359		qcom_cc_clk_regs_configure(dev, desc->driver_data, regmap);
360	}
361
362	reset = &cc->reset;
363	reset->rcdev.of_node = dev->of_node;
364	reset->rcdev.ops = &qcom_reset_ops;
365	reset->rcdev.owner = dev->driver->owner;
366	reset->rcdev.nr_resets = desc->num_resets;
367	reset->regmap = regmap;
368	reset->reset_map = desc->resets;
369
370	ret = devm_reset_controller_register(dev, &reset->rcdev);
371	if (ret)
372		goto put_rpm;
373
374	if (desc->gdscs && desc->num_gdscs) {
375		scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
376		if (!scd) {
377			ret = -ENOMEM;
378			goto put_rpm;
379		}
380		scd->dev = dev;
381		scd->scs = desc->gdscs;
382		scd->num = desc->num_gdscs;
383		scd->pd_list = cc->pd_list;
384		ret = gdsc_register(scd, &reset->rcdev, regmap);
385		if (ret)
386			goto put_rpm;
387		ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
388					       scd);
389		if (ret)
390			goto put_rpm;
391	}
392
393	if (desc->driver_data &&
394	    desc->driver_data->dfs_rcgs &&
395	    desc->driver_data->num_dfs_rcgs) {
396		ret = qcom_cc_register_rcg_dfs(regmap,
397					       desc->driver_data->dfs_rcgs,
398					       desc->driver_data->num_dfs_rcgs);
399		if (ret)
400			goto put_rpm;
401	}
402
403	cc->rclks = rclks;
404	cc->num_rclks = num_clks;
405
406	qcom_cc_drop_protected(dev, cc);
407
408	for (i = 0; i < num_clk_hws; i++) {
409		ret = devm_clk_hw_register(dev, clk_hws[i]);
410		if (ret)
411			goto put_rpm;
412	}
413
414	for (i = 0; i < num_clks; i++) {
415		if (!rclks[i])
416			continue;
417
418		ret = devm_clk_register_regmap(dev, rclks[i]);
419		if (ret)
420			goto put_rpm;
421	}
422
423	ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc);
424	if (ret)
425		goto put_rpm;
426
427	ret = qcom_cc_icc_register(dev, desc);
428
429put_rpm:
430	if (desc->use_rpm)
431		pm_runtime_put(dev);
432
433	return ret;
434}
435EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
436
437int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
438{
439	struct regmap *regmap;
440
441	regmap = qcom_cc_map(pdev, desc);
442	if (IS_ERR(regmap))
443		return PTR_ERR(regmap);
444
445	return qcom_cc_really_probe(&pdev->dev, desc, regmap);
446}
447EXPORT_SYMBOL_GPL(qcom_cc_probe);
448
449int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
450			   const struct qcom_cc_desc *desc)
451{
452	struct regmap *regmap;
453	void __iomem *base;
454
455	base = devm_platform_ioremap_resource(pdev, index);
456	if (IS_ERR(base))
457		return PTR_ERR(base);
458
459	regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
460	if (IS_ERR(regmap))
461		return PTR_ERR(regmap);
462
463	return qcom_cc_really_probe(&pdev->dev, desc, regmap);
464}
465EXPORT_SYMBOL_GPL(qcom_cc_probe_by_index);
466
467MODULE_LICENSE("GPL v2");
468MODULE_DESCRIPTION("QTI Common Clock module");
~/ Read-only file preview.