xref: /openbmc/linux/drivers/clk/spear/clk-frac-synth.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1*3bb16560SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2270b9f42SViresh Kumar /*
3270b9f42SViresh Kumar  * Copyright (C) 2012 ST Microelectronics
4da89947bSViresh Kumar  * Viresh Kumar <vireshk@kernel.org>
5270b9f42SViresh Kumar  *
6270b9f42SViresh Kumar  * Fractional Synthesizer clock implementation
7270b9f42SViresh Kumar  */
8270b9f42SViresh Kumar 
9270b9f42SViresh Kumar #define pr_fmt(fmt) "clk-frac-synth: " fmt
10270b9f42SViresh Kumar 
11270b9f42SViresh Kumar #include <linux/clk-provider.h>
12270b9f42SViresh Kumar #include <linux/slab.h>
13270b9f42SViresh Kumar #include <linux/io.h>
14270b9f42SViresh Kumar #include <linux/err.h>
15270b9f42SViresh Kumar #include "clk.h"
16270b9f42SViresh Kumar 
17270b9f42SViresh Kumar #define DIV_FACTOR_MASK		0x1FFFF
18270b9f42SViresh Kumar 
19270b9f42SViresh Kumar /*
20270b9f42SViresh Kumar  * DOC: Fractional Synthesizer clock
21270b9f42SViresh Kumar  *
22270b9f42SViresh Kumar  * Fout from synthesizer can be given from below equation:
23270b9f42SViresh Kumar  *
24270b9f42SViresh Kumar  * Fout= Fin/2*div (division factor)
25270b9f42SViresh Kumar  * div is 17 bits:-
26270b9f42SViresh Kumar  *	0-13 (fractional part)
27270b9f42SViresh Kumar  *	14-16 (integer part)
28270b9f42SViresh Kumar  *	div is (16-14 bits).(13-0 bits) (in binary)
29270b9f42SViresh Kumar  *
30270b9f42SViresh Kumar  *	Fout = Fin/(2 * div)
31270b9f42SViresh Kumar  *	Fout = ((Fin / 10000)/(2 * div)) * 10000
32270b9f42SViresh Kumar  *	Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000
33270b9f42SViresh Kumar  *	Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000
34270b9f42SViresh Kumar  *
35270b9f42SViresh Kumar  * div << 14 simply 17 bit value written at register.
36270b9f42SViresh Kumar  * Max error due to scaling down by 10000 is 10 KHz
37270b9f42SViresh Kumar  */
38270b9f42SViresh Kumar 
39270b9f42SViresh Kumar #define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
40270b9f42SViresh Kumar 
frac_calc_rate(struct clk_hw * hw,unsigned long prate,int index)41270b9f42SViresh Kumar static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate,
42270b9f42SViresh Kumar 		int index)
43270b9f42SViresh Kumar {
44270b9f42SViresh Kumar 	struct clk_frac *frac = to_clk_frac(hw);
45270b9f42SViresh Kumar 	struct frac_rate_tbl *rtbl = frac->rtbl;
46270b9f42SViresh Kumar 
47270b9f42SViresh Kumar 	prate /= 10000;
48270b9f42SViresh Kumar 	prate <<= 14;
49270b9f42SViresh Kumar 	prate /= (2 * rtbl[index].div);
50270b9f42SViresh Kumar 	prate *= 10000;
51270b9f42SViresh Kumar 
52270b9f42SViresh Kumar 	return prate;
53270b9f42SViresh Kumar }
54270b9f42SViresh Kumar 
clk_frac_round_rate(struct clk_hw * hw,unsigned long drate,unsigned long * prate)55270b9f42SViresh Kumar static long clk_frac_round_rate(struct clk_hw *hw, unsigned long drate,
56270b9f42SViresh Kumar 		unsigned long *prate)
57270b9f42SViresh Kumar {
58270b9f42SViresh Kumar 	struct clk_frac *frac = to_clk_frac(hw);
59270b9f42SViresh Kumar 	int unused;
60270b9f42SViresh Kumar 
61270b9f42SViresh Kumar 	return clk_round_rate_index(hw, drate, *prate, frac_calc_rate,
62270b9f42SViresh Kumar 			frac->rtbl_cnt, &unused);
63270b9f42SViresh Kumar }
64270b9f42SViresh Kumar 
clk_frac_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)65270b9f42SViresh Kumar static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
66270b9f42SViresh Kumar 		unsigned long parent_rate)
67270b9f42SViresh Kumar {
68270b9f42SViresh Kumar 	struct clk_frac *frac = to_clk_frac(hw);
69270b9f42SViresh Kumar 	unsigned long flags = 0;
70270b9f42SViresh Kumar 	unsigned int div = 1, val;
71270b9f42SViresh Kumar 
72270b9f42SViresh Kumar 	if (frac->lock)
73270b9f42SViresh Kumar 		spin_lock_irqsave(frac->lock, flags);
74270b9f42SViresh Kumar 
75270b9f42SViresh Kumar 	val = readl_relaxed(frac->reg);
76270b9f42SViresh Kumar 
77270b9f42SViresh Kumar 	if (frac->lock)
78270b9f42SViresh Kumar 		spin_unlock_irqrestore(frac->lock, flags);
79270b9f42SViresh Kumar 
80270b9f42SViresh Kumar 	div = val & DIV_FACTOR_MASK;
81270b9f42SViresh Kumar 
82270b9f42SViresh Kumar 	if (!div)
83270b9f42SViresh Kumar 		return 0;
84270b9f42SViresh Kumar 
85270b9f42SViresh Kumar 	parent_rate = parent_rate / 10000;
86270b9f42SViresh Kumar 
87270b9f42SViresh Kumar 	parent_rate = (parent_rate << 14) / (2 * div);
88270b9f42SViresh Kumar 	return parent_rate * 10000;
89270b9f42SViresh Kumar }
90270b9f42SViresh Kumar 
91270b9f42SViresh Kumar /* Configures new clock rate of frac */
clk_frac_set_rate(struct clk_hw * hw,unsigned long drate,unsigned long prate)92270b9f42SViresh Kumar static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate,
93270b9f42SViresh Kumar 				unsigned long prate)
94270b9f42SViresh Kumar {
95270b9f42SViresh Kumar 	struct clk_frac *frac = to_clk_frac(hw);
96270b9f42SViresh Kumar 	struct frac_rate_tbl *rtbl = frac->rtbl;
97270b9f42SViresh Kumar 	unsigned long flags = 0, val;
98270b9f42SViresh Kumar 	int i;
99270b9f42SViresh Kumar 
100270b9f42SViresh Kumar 	clk_round_rate_index(hw, drate, prate, frac_calc_rate, frac->rtbl_cnt,
101270b9f42SViresh Kumar 			&i);
102270b9f42SViresh Kumar 
103270b9f42SViresh Kumar 	if (frac->lock)
104270b9f42SViresh Kumar 		spin_lock_irqsave(frac->lock, flags);
105270b9f42SViresh Kumar 
106270b9f42SViresh Kumar 	val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK;
107270b9f42SViresh Kumar 	val |= rtbl[i].div & DIV_FACTOR_MASK;
108270b9f42SViresh Kumar 	writel_relaxed(val, frac->reg);
109270b9f42SViresh Kumar 
110270b9f42SViresh Kumar 	if (frac->lock)
111270b9f42SViresh Kumar 		spin_unlock_irqrestore(frac->lock, flags);
112270b9f42SViresh Kumar 
113270b9f42SViresh Kumar 	return 0;
114270b9f42SViresh Kumar }
115270b9f42SViresh Kumar 
116ba3892dfSBhumika Goyal static const struct clk_ops clk_frac_ops = {
117270b9f42SViresh Kumar 	.recalc_rate = clk_frac_recalc_rate,
118270b9f42SViresh Kumar 	.round_rate = clk_frac_round_rate,
119270b9f42SViresh Kumar 	.set_rate = clk_frac_set_rate,
120270b9f42SViresh Kumar };
121270b9f42SViresh Kumar 
clk_register_frac(const char * name,const char * parent_name,unsigned long flags,void __iomem * reg,struct frac_rate_tbl * rtbl,u8 rtbl_cnt,spinlock_t * lock)122270b9f42SViresh Kumar struct clk *clk_register_frac(const char *name, const char *parent_name,
123270b9f42SViresh Kumar 		unsigned long flags, void __iomem *reg,
124270b9f42SViresh Kumar 		struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock)
125270b9f42SViresh Kumar {
126270b9f42SViresh Kumar 	struct clk_init_data init;
127270b9f42SViresh Kumar 	struct clk_frac *frac;
128270b9f42SViresh Kumar 	struct clk *clk;
129270b9f42SViresh Kumar 
130270b9f42SViresh Kumar 	if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
131811284b0SArvind Yadav 		pr_err("Invalid arguments passed\n");
132270b9f42SViresh Kumar 		return ERR_PTR(-EINVAL);
133270b9f42SViresh Kumar 	}
134270b9f42SViresh Kumar 
135270b9f42SViresh Kumar 	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
13663b1a5d7SMarkus Elfring 	if (!frac)
137270b9f42SViresh Kumar 		return ERR_PTR(-ENOMEM);
138270b9f42SViresh Kumar 
139270b9f42SViresh Kumar 	/* struct clk_frac assignments */
140270b9f42SViresh Kumar 	frac->reg = reg;
141270b9f42SViresh Kumar 	frac->rtbl = rtbl;
142270b9f42SViresh Kumar 	frac->rtbl_cnt = rtbl_cnt;
143270b9f42SViresh Kumar 	frac->lock = lock;
144270b9f42SViresh Kumar 	frac->hw.init = &init;
145270b9f42SViresh Kumar 
146270b9f42SViresh Kumar 	init.name = name;
147270b9f42SViresh Kumar 	init.ops = &clk_frac_ops;
148270b9f42SViresh Kumar 	init.flags = flags;
149270b9f42SViresh Kumar 	init.parent_names = &parent_name;
150270b9f42SViresh Kumar 	init.num_parents = 1;
151270b9f42SViresh Kumar 
152270b9f42SViresh Kumar 	clk = clk_register(NULL, &frac->hw);
153270b9f42SViresh Kumar 	if (!IS_ERR_OR_NULL(clk))
154270b9f42SViresh Kumar 		return clk;
155270b9f42SViresh Kumar 
156270b9f42SViresh Kumar 	pr_err("clk register failed\n");
157270b9f42SViresh Kumar 	kfree(frac);
158270b9f42SViresh Kumar 
159270b9f42SViresh Kumar 	return NULL;
160270b9f42SViresh Kumar }
161