File Index
Files and directories
Sign in
FILE

clk-frac-synth.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/spear/clk-frac-synth.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2012 ST Microelectronics
  4 * Viresh Kumar <vireshk@kernel.org>
  5 *
  6 * Fractional Synthesizer clock implementation
  7 */
  8
  9#define pr_fmt(fmt) "clk-frac-synth: " fmt
 10
 11#include <linux/clk-provider.h>
 12#include <linux/slab.h>
 13#include <linux/io.h>
 14#include <linux/err.h>
 15#include "clk.h"
 16
 17#define DIV_FACTOR_MASK		0x1FFFF
 18
 19/*
 20 * DOC: Fractional Synthesizer clock
 21 *
 22 * Fout from synthesizer can be given from below equation:
 23 *
 24 * Fout= Fin/2*div (division factor)
 25 * div is 17 bits:-
 26 *	0-13 (fractional part)
 27 *	14-16 (integer part)
 28 *	div is (16-14 bits).(13-0 bits) (in binary)
 29 *
 30 *	Fout = Fin/(2 * div)
 31 *	Fout = ((Fin / 10000)/(2 * div)) * 10000
 32 *	Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000
 33 *	Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000
 34 *
 35 * div << 14 simply 17 bit value written at register.
 36 * Max error due to scaling down by 10000 is 10 KHz
 37 */
 38
 39#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
 40
 41static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate,
 42		int index)
 43{
 44	struct clk_frac *frac = to_clk_frac(hw);
 45	struct frac_rate_tbl *rtbl = frac->rtbl;
 46
 47	prate /= 10000;
 48	prate <<= 14;
 49	prate /= (2 * rtbl[index].div);
 50	prate *= 10000;
 51
 52	return prate;
 53}
 54
 55static int clk_frac_determine_rate(struct clk_hw *hw,
 56				   struct clk_rate_request *req)
 57{
 58	struct clk_frac *frac = to_clk_frac(hw);
 59	int unused;
 60
 61	req->rate = clk_round_rate_index(hw, req->rate, req->best_parent_rate,
 62					 frac_calc_rate, frac->rtbl_cnt, &unused);
 63
 64	return 0;
 65}
 66
 67static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
 68		unsigned long parent_rate)
 69{
 70	struct clk_frac *frac = to_clk_frac(hw);
 71	unsigned long flags = 0;
 72	unsigned int div = 1, val;
 73
 74	if (frac->lock)
 75		spin_lock_irqsave(frac->lock, flags);
 76
 77	val = readl_relaxed(frac->reg);
 78
 79	if (frac->lock)
 80		spin_unlock_irqrestore(frac->lock, flags);
 81
 82	div = val & DIV_FACTOR_MASK;
 83
 84	if (!div)
 85		return 0;
 86
 87	parent_rate = parent_rate / 10000;
 88
 89	parent_rate = (parent_rate << 14) / (2 * div);
 90	return parent_rate * 10000;
 91}
 92
 93/* Configures new clock rate of frac */
 94static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate,
 95				unsigned long prate)
 96{
 97	struct clk_frac *frac = to_clk_frac(hw);
 98	struct frac_rate_tbl *rtbl = frac->rtbl;
 99	unsigned long flags = 0, val;
100	int i;
101
102	clk_round_rate_index(hw, drate, prate, frac_calc_rate, frac->rtbl_cnt,
103			&i);
104
105	if (frac->lock)
106		spin_lock_irqsave(frac->lock, flags);
107
108	val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK;
109	val |= rtbl[i].div & DIV_FACTOR_MASK;
110	writel_relaxed(val, frac->reg);
111
112	if (frac->lock)
113		spin_unlock_irqrestore(frac->lock, flags);
114
115	return 0;
116}
117
118static const struct clk_ops clk_frac_ops = {
119	.recalc_rate = clk_frac_recalc_rate,
120	.determine_rate = clk_frac_determine_rate,
121	.set_rate = clk_frac_set_rate,
122};
123
124struct clk *clk_register_frac(const char *name, const char *parent_name,
125		unsigned long flags, void __iomem *reg,
126		struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock)
127{
128	struct clk_init_data init;
129	struct clk_frac *frac;
130	struct clk *clk;
131
132	if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
133		pr_err("Invalid arguments passed\n");
134		return ERR_PTR(-EINVAL);
135	}
136
137	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
138	if (!frac)
139		return ERR_PTR(-ENOMEM);
140
141	/* struct clk_frac assignments */
142	frac->reg = reg;
143	frac->rtbl = rtbl;
144	frac->rtbl_cnt = rtbl_cnt;
145	frac->lock = lock;
146	frac->hw.init = &init;
147
148	init.name = name;
149	init.ops = &clk_frac_ops;
150	init.flags = flags;
151	init.parent_names = &parent_name;
152	init.num_parents = 1;
153
154	clk = clk_register(NULL, &frac->hw);
155	if (!IS_ERR_OR_NULL(clk))
156		return clk;
157
158	pr_err("clk register failed\n");
159	kfree(frac);
160
161	return NULL;
162}
~/ Read-only file preview.