blob: c99aa0b4e440c9876b42fbcafbcc04af1acd7871 [file] [log] [blame]
Kongyang Liu80bf1f72024-06-11 17:41:14 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com>
4 */
5
6#include <clk-uclass.h>
7#include <dm.h>
8#include <div64.h>
9#include <linux/bitfield.h>
10#include <linux/clk-provider.h>
11#include <linux/kernel.h>
12
13#include "clk-common.h"
14#include "clk-pll.h"
15
16#define PLL_PRE_DIV_MIN 1
17#define PLL_PRE_DIV_MAX 127
18#define PLL_POST_DIV_MIN 1
19#define PLL_POST_DIV_MAX 127
20#define PLL_DIV_MIN 6
21#define PLL_DIV_MAX 127
22#define PLL_ICTRL_MIN 0
23#define PLL_ICTRL_MAX 7
24#define PLL_MODE_MIN 0
25#define PLL_MODE_MAX 3
26#define FOR_RANGE(x, RANGE) for (x = RANGE##_MIN; x <= RANGE##_MAX; x++)
27
28#define PLL_ICTRL GENMASK(26, 24)
29#define PLL_DIV_SEL GENMASK(23, 17)
30#define PLL_SEL_MODE GENMASK(16, 15)
31#define PLL_POST_DIV_SEL GENMASK(14, 8)
32#define PLL_PRE_DIV_SEL GENMASK(6, 0)
33#define PLL_MASK_ALL (PLL_ICTRL | PLL_DIV_SEL | PLL_SEL_MODE | PLL_POST_DIV_SEL | PLL_PRE_DIV_SEL)
34
35/* IPLL */
36#define to_clk_ipll(dev) container_of(dev, struct cv1800b_clk_ipll, clk)
37
38static int cv1800b_ipll_enable(struct clk *clk)
39{
40 struct cv1800b_clk_ipll *pll = to_clk_ipll(clk);
41
42 cv1800b_clk_clrbit(pll->base, &pll->pll_pwd);
43 return 0;
44}
45
46static int cv1800b_ipll_disable(struct clk *clk)
47{
48 struct cv1800b_clk_ipll *pll = to_clk_ipll(clk);
49
50 cv1800b_clk_setbit(pll->base, &pll->pll_pwd);
51 return 0;
52}
53
54static ulong cv1800b_ipll_get_rate(struct clk *clk)
55{
56 struct cv1800b_clk_ipll *pll = to_clk_ipll(clk);
57
58 ulong parent_rate = clk_get_parent_rate(clk);
59 u32 reg = readl(pll->base + pll->pll_reg);
60 u32 pre_div = FIELD_GET(PLL_PRE_DIV_SEL, reg);
61 u32 post_div = FIELD_GET(PLL_POST_DIV_SEL, reg);
62 u32 div = FIELD_GET(PLL_DIV_SEL, reg);
63
64 return DIV_ROUND_DOWN_ULL(parent_rate * div, pre_div * post_div);
65}
66
67static ulong cv1800b_ipll_set_rate(struct clk *clk, ulong rate)
68{
69 struct cv1800b_clk_ipll *pll = to_clk_ipll(clk);
70 ulong parent_rate = clk_get_parent_rate(clk);
71 u32 pre_div, post_div, div;
72 u32 pre_div_sel, post_div_sel, div_sel;
73 ulong new_rate, best_rate = 0;
74 u32 mode, ictrl;
75 u32 test, val;
76
77 FOR_RANGE(pre_div, PLL_PRE_DIV)
78 {
79 FOR_RANGE(post_div, PLL_POST_DIV)
80 {
81 FOR_RANGE(div, PLL_DIV)
82 {
83 new_rate =
84 DIV_ROUND_DOWN_ULL(parent_rate * div, pre_div * post_div);
85 if (rate - new_rate < rate - best_rate) {
86 best_rate = new_rate;
87 pre_div_sel = pre_div;
88 post_div_sel = post_div;
89 div_sel = div;
90 }
91 }
92 }
93 }
94
95 FOR_RANGE(mode, PLL_MODE)
96 {
97 FOR_RANGE(ictrl, PLL_ICTRL)
98 {
99 test = 184 * (1 + mode) * (1 + ictrl) / 2;
100 if (test > 20 * div_sel && test < 35 * div_sel) {
101 val = FIELD_PREP(PLL_PRE_DIV_SEL, pre_div_sel) |
102 FIELD_PREP(PLL_POST_DIV_SEL, post_div_sel) |
103 FIELD_PREP(PLL_DIV_SEL, div_sel) |
104 FIELD_PREP(PLL_ICTRL, ictrl) |
105 FIELD_PREP(PLL_SEL_MODE, mode);
106 clrsetbits_le32(pll->base + pll->pll_reg, PLL_MASK_ALL, val);
107 return best_rate;
108 }
109 }
110 }
111
112 return -EINVAL;
113}
114
115const struct clk_ops cv1800b_ipll_ops = {
116 .enable = cv1800b_ipll_enable,
117 .disable = cv1800b_ipll_disable,
118 .get_rate = cv1800b_ipll_get_rate,
119 .set_rate = cv1800b_ipll_set_rate,
120};
121
122U_BOOT_DRIVER(cv1800b_clk_ipll) = {
123 .name = "cv1800b_clk_ipll",
124 .id = UCLASS_CLK,
125 .ops = &cv1800b_ipll_ops,
126 .flags = DM_FLAG_PRE_RELOC,
127};
128
129/* FPLL */
130#define to_clk_fpll(dev) container_of(dev, struct cv1800b_clk_fpll, ipll.clk)
131
132static ulong cv1800b_fpll_get_rate(struct clk *clk)
133{
134 struct cv1800b_clk_fpll *pll = to_clk_fpll(clk);
135 u32 val, syn_set;
136 u32 pre_div, post_div, div;
137 u8 mult = 1;
138 ulong divisor, remainder, rate;
139
140 if (!cv1800b_clk_getbit(pll->ipll.base, &pll->syn.en))
141 return cv1800b_ipll_get_rate(clk);
142
143 syn_set = readl(pll->ipll.base + pll->syn.set);
144 if (syn_set == 0)
145 return 0;
146
147 val = readl(pll->ipll.base + pll->ipll.pll_reg);
148 pre_div = FIELD_GET(PLL_PRE_DIV_SEL, val);
149 post_div = FIELD_GET(PLL_POST_DIV_SEL, val);
150 div = FIELD_GET(PLL_DIV_SEL, val);
151
152 if (cv1800b_clk_getbit(pll->ipll.base, &pll->syn.clk_half))
153 mult = 2;
154
155 divisor = (ulong)pre_div * post_div * syn_set;
156 rate = (clk_get_parent_rate(clk) * div) << 25;
157 remainder = rate % divisor;
158 rate /= divisor;
159 return rate * mult + DIV_ROUND_CLOSEST_ULL(remainder * mult, divisor);
160}
161
162static ulong cv1800b_find_syn(ulong rate, ulong parent_rate, ulong pre_div, ulong post_div,
163 ulong div, u32 *syn)
164{
165 u32 syn_min = (4 << 26) + 1;
166 u32 syn_max = U32_MAX;
167 u32 mid;
168 ulong new_rate;
169 u32 mult = 1;
170 ulong divisor, remainder;
171
172 while (syn_min < syn_max) {
173 mid = ((ulong)syn_min + syn_max) / 2;
174 divisor = pre_div * post_div * mid;
175 new_rate = (parent_rate * div) << 25;
176 remainder = do_div(new_rate, divisor);
177 new_rate = new_rate * mult + DIV_ROUND_CLOSEST_ULL(remainder * mult, divisor);
178 if (new_rate > rate) {
179 syn_max = mid + 1;
180 } else if (new_rate < rate) {
181 syn_min = mid - 1;
182 } else {
183 syn_min = mid;
184 break;
185 }
186 }
187 *syn = syn_min;
188 return new_rate;
189}
190
191static ulong cv1800b_fpll_set_rate(struct clk *clk, ulong rate)
192{
193 struct cv1800b_clk_fpll *pll = to_clk_fpll(clk);
194 ulong parent_rate = clk_get_parent_rate(clk);
195 u32 pre_div, post_div, div;
196 u32 pre_div_sel, post_div_sel, div_sel;
197 u32 syn, syn_sel;
198 ulong new_rate, best_rate = 0;
199 u32 mult = 1;
200 u32 mode, ictrl;
201
202 if (!cv1800b_clk_getbit(pll->ipll.base, &pll->syn.en))
203 return cv1800b_ipll_set_rate(clk, rate);
204
205 if (cv1800b_clk_getbit(pll->ipll.base, &pll->syn.clk_half))
206 mult = 2;
207
208 FOR_RANGE(pre_div, PLL_PRE_DIV)
209 {
210 FOR_RANGE(post_div, PLL_POST_DIV)
211 {
212 FOR_RANGE(div, PLL_DIV)
213 {
214 new_rate = cv1800b_find_syn(rate, parent_rate, pre_div, post_div,
215 div, &syn);
216 if (rate - new_rate < rate - best_rate) {
217 best_rate = new_rate;
218 pre_div_sel = pre_div;
219 post_div_sel = post_div;
220 div_sel = div;
221 syn_sel = syn;
222 }
223 }
224 }
225 }
226
227 FOR_RANGE(mode, PLL_MODE)
228 {
229 FOR_RANGE(ictrl, PLL_ICTRL)
230 {
231 u32 test = 184 * (1 + mode) * (1 + ictrl) / 2;
232
233 if (test > 10 * div_sel && test <= 24 * div_sel) {
234 u32 val = FIELD_PREP(PLL_PRE_DIV_SEL, pre_div_sel) |
235 FIELD_PREP(PLL_POST_DIV_SEL, post_div_sel) |
236 FIELD_PREP(PLL_DIV_SEL, div_sel) |
237 FIELD_PREP(PLL_ICTRL, ictrl) |
238 FIELD_PREP(PLL_SEL_MODE, mode);
239 clrsetbits_le32(pll->ipll.base + pll->ipll.pll_reg, PLL_MASK_ALL,
240 val);
241 writel(syn_sel, pll->ipll.base + pll->syn.set);
242 return best_rate;
243 }
244 }
245 }
246
247 return -EINVAL;
248}
249
250static int cv1800b_fpll_set_parent(struct clk *clk, struct clk *parent)
251{
252 struct cv1800b_clk_fpll *pll = to_clk_fpll(clk);
253
254 if (parent->id == CV1800B_CLK_BYPASS)
255 cv1800b_clk_setbit(pll->ipll.base, &pll->syn.en);
256 else
257 cv1800b_clk_clrbit(pll->ipll.base, &pll->syn.en);
258
259 return 0;
260}
261
262const struct clk_ops cv1800b_fpll_ops = {
263 .enable = cv1800b_ipll_enable,
264 .disable = cv1800b_ipll_disable,
265 .get_rate = cv1800b_fpll_get_rate,
266 .set_rate = cv1800b_fpll_set_rate,
267 .set_parent = cv1800b_fpll_set_parent,
268};
269
270U_BOOT_DRIVER(cv1800b_clk_fpll) = {
271 .name = "cv1800b_clk_fpll",
272 .id = UCLASS_CLK,
273 .ops = &cv1800b_fpll_ops,
274 .flags = DM_FLAG_PRE_RELOC,
275};