1 // SPDX-License-Identifier: GPL-2.0
3 // Spreadtrum pll clock driver
5 // Copyright (C) 2015~2017 Spreadtrum, Inc.
6 // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
8 #include <linux/delay.h>
10 #include <linux/regmap.h>
11 #include <linux/slab.h>
15 #define CLK_PLL_1M 1000000
16 #define CLK_PLL_10M (CLK_PLL_1M * 10)
18 #define pindex(pll, member) \
19 (pll->factors[member].shift / (8 * sizeof(pll->regs_num)))
21 #define pshift(pll, member) \
22 (pll->factors[member].shift % (8 * sizeof(pll->regs_num)))
24 #define pwidth(pll, member) \
25 pll->factors[member].width
27 #define pmask(pll, member) \
28 ((pwidth(pll, member)) ? \
29 GENMASK(pwidth(pll, member) + pshift(pll, member) - 1, \
30 pshift(pll, member)) : 0)
32 #define pinternal(pll, cfg, member) \
33 (cfg[pindex(pll, member)] & pmask(pll, member))
35 #define pinternal_val(pll, cfg, member) \
36 (pinternal(pll, cfg, member) >> pshift(pll, member))
38 static inline unsigned int
39 sprd_pll_read(const struct sprd_pll
*pll
, u8 index
)
41 const struct sprd_clk_common
*common
= &pll
->common
;
44 if (WARN_ON(index
>= pll
->regs_num
))
47 regmap_read(common
->regmap
, common
->reg
+ index
* 4, &val
);
53 sprd_pll_write(const struct sprd_pll
*pll
, u8 index
,
56 const struct sprd_clk_common
*common
= &pll
->common
;
57 unsigned int offset
, reg
;
60 if (WARN_ON(index
>= pll
->regs_num
))
63 offset
= common
->reg
+ index
* 4;
64 ret
= regmap_read(common
->regmap
, offset
, ®
);
66 regmap_write(common
->regmap
, offset
, (reg
& ~msk
) | val
);
69 static unsigned long pll_get_refin(const struct sprd_pll
*pll
)
71 u32 shift
, mask
, index
, refin_id
= 3;
72 const unsigned long refin
[4] = { 2, 4, 13, 26 };
74 if (pwidth(pll
, PLL_REFIN
)) {
75 index
= pindex(pll
, PLL_REFIN
);
76 shift
= pshift(pll
, PLL_REFIN
);
77 mask
= pmask(pll
, PLL_REFIN
);
78 refin_id
= (sprd_pll_read(pll
, index
) & mask
) >> shift
;
83 return refin
[refin_id
];
86 static u32
pll_get_ibias(u64 rate
, const u64
*table
)
88 u32 i
, num
= table
[0];
90 for (i
= 1; i
< num
+ 1; i
++)
94 return (i
== num
+ 1) ? num
: i
;
97 static unsigned long _sprd_pll_recalc_rate(const struct sprd_pll
*pll
,
98 unsigned long parent_rate
)
101 u32 i
, mask
, regs_num
= pll
->regs_num
;
102 unsigned long rate
, nint
, kint
= 0;
106 cfg
= kcalloc(regs_num
, sizeof(*cfg
), GFP_KERNEL
);
110 for (i
= 0; i
< regs_num
; i
++)
111 cfg
[i
] = sprd_pll_read(pll
, i
);
113 refin
= pll_get_refin(pll
);
115 if (pinternal(pll
, cfg
, PLL_PREDIV
))
118 if (pwidth(pll
, PLL_POSTDIV
) &&
119 ((pll
->fflag
== 1 && pinternal(pll
, cfg
, PLL_POSTDIV
)) ||
120 (!pll
->fflag
&& !pinternal(pll
, cfg
, PLL_POSTDIV
))))
123 if (!pinternal(pll
, cfg
, PLL_DIV_S
)) {
124 rate
= refin
* pinternal_val(pll
, cfg
, PLL_N
) * CLK_PLL_10M
;
126 nint
= pinternal_val(pll
, cfg
, PLL_NINT
);
127 if (pinternal(pll
, cfg
, PLL_SDM_EN
))
128 kint
= pinternal_val(pll
, cfg
, PLL_KINT
);
130 mask
= pmask(pll
, PLL_KINT
);
134 rate
= DIV_ROUND_CLOSEST_ULL(refin
* kint
* k1
,
135 ((mask
>> __ffs(mask
)) + 1)) *
136 k2
+ refin
* nint
* CLK_PLL_1M
;
142 #define SPRD_PLL_WRITE_CHECK(pll, i, mask, val) \
143 (((sprd_pll_read(pll, i) & mask) == val) ? 0 : (-EFAULT))
145 static int _sprd_pll_set_rate(const struct sprd_pll
*pll
,
147 unsigned long parent_rate
)
151 u32 mask
, shift
, width
, ibias_val
, index
;
152 u32 regs_num
= pll
->regs_num
, i
= 0;
153 unsigned long kint
, nint
;
154 u64 tmp
, refin
, fvco
= rate
;
156 cfg
= kcalloc(regs_num
, sizeof(*cfg
), GFP_KERNEL
);
160 refin
= pll_get_refin(pll
);
162 mask
= pmask(pll
, PLL_PREDIV
);
163 index
= pindex(pll
, PLL_PREDIV
);
164 width
= pwidth(pll
, PLL_PREDIV
);
165 if (width
&& (sprd_pll_read(pll
, index
) & mask
))
168 mask
= pmask(pll
, PLL_POSTDIV
);
169 index
= pindex(pll
, PLL_POSTDIV
);
170 width
= pwidth(pll
, PLL_POSTDIV
);
171 cfg
[index
].msk
= mask
;
172 if (width
&& ((pll
->fflag
== 1 && fvco
<= pll
->fvco
) ||
173 (pll
->fflag
== 0 && fvco
> pll
->fvco
)))
174 cfg
[index
].val
|= mask
;
176 if (width
&& fvco
<= pll
->fvco
)
179 mask
= pmask(pll
, PLL_DIV_S
);
180 index
= pindex(pll
, PLL_DIV_S
);
181 cfg
[index
].val
|= mask
;
182 cfg
[index
].msk
|= mask
;
184 mask
= pmask(pll
, PLL_SDM_EN
);
185 index
= pindex(pll
, PLL_SDM_EN
);
186 cfg
[index
].val
|= mask
;
187 cfg
[index
].msk
|= mask
;
189 nint
= do_div(fvco
, refin
* CLK_PLL_1M
);
190 mask
= pmask(pll
, PLL_NINT
);
191 index
= pindex(pll
, PLL_NINT
);
192 shift
= pshift(pll
, PLL_NINT
);
193 cfg
[index
].val
|= (nint
<< shift
) & mask
;
194 cfg
[index
].msk
|= mask
;
196 mask
= pmask(pll
, PLL_KINT
);
197 index
= pindex(pll
, PLL_KINT
);
198 width
= pwidth(pll
, PLL_KINT
);
199 shift
= pshift(pll
, PLL_KINT
);
200 tmp
= fvco
- refin
* nint
* CLK_PLL_1M
;
201 tmp
= do_div(tmp
, 10000) * ((mask
>> shift
) + 1);
202 kint
= DIV_ROUND_CLOSEST_ULL(tmp
, refin
* 100);
203 cfg
[index
].val
|= (kint
<< shift
) & mask
;
204 cfg
[index
].msk
|= mask
;
206 ibias_val
= pll_get_ibias(fvco
, pll
->itable
);
208 mask
= pmask(pll
, PLL_IBIAS
);
209 index
= pindex(pll
, PLL_IBIAS
);
210 shift
= pshift(pll
, PLL_IBIAS
);
211 cfg
[index
].val
|= ibias_val
<< shift
& mask
;
212 cfg
[index
].msk
|= mask
;
214 for (i
= 0; i
< regs_num
; i
++) {
216 sprd_pll_write(pll
, i
, cfg
[i
].msk
, cfg
[i
].val
);
217 ret
|= SPRD_PLL_WRITE_CHECK(pll
, i
, cfg
[i
].msk
,
228 static unsigned long sprd_pll_recalc_rate(struct clk_hw
*hw
,
229 unsigned long parent_rate
)
231 struct sprd_pll
*pll
= hw_to_sprd_pll(hw
);
233 return _sprd_pll_recalc_rate(pll
, parent_rate
);
236 static int sprd_pll_set_rate(struct clk_hw
*hw
,
238 unsigned long parent_rate
)
240 struct sprd_pll
*pll
= hw_to_sprd_pll(hw
);
242 return _sprd_pll_set_rate(pll
, rate
, parent_rate
);
245 static int sprd_pll_clk_prepare(struct clk_hw
*hw
)
247 struct sprd_pll
*pll
= hw_to_sprd_pll(hw
);
254 static long sprd_pll_round_rate(struct clk_hw
*hw
, unsigned long rate
,
255 unsigned long *prate
)
260 const struct clk_ops sprd_pll_ops
= {
261 .prepare
= sprd_pll_clk_prepare
,
262 .recalc_rate
= sprd_pll_recalc_rate
,
263 .round_rate
= sprd_pll_round_rate
,
264 .set_rate
= sprd_pll_set_rate
,
266 EXPORT_SYMBOL_GPL(sprd_pll_ops
);