blob: 581035842fc31a27c3d10410873b979df1f7cf86 [file] [log] [blame]
Yanhong Wang5a85d052023-03-29 11:42:13 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2022-23 StarFive Technology Co., Ltd.
4 *
5 * Author: Yanhong Wang <yanhong.wang@starfivetech.com>
Xingyu Wu751e4402023-07-07 18:50:07 +08006 * Xingyu Wu <xingyu.wu@starfivetech.com>
Yanhong Wang5a85d052023-03-29 11:42:13 +08007 */
8
Yanhong Wang5a85d052023-03-29 11:42:13 +08009#include <asm/io.h>
10#include <malloc.h>
11#include <clk-uclass.h>
12#include <div64.h>
13#include <dm/device.h>
Xingyu Wu751e4402023-07-07 18:50:07 +080014#include <dm/read.h>
15#include <dt-bindings/clock/starfive,jh7110-crg.h>
Yanhong Wang5a85d052023-03-29 11:42:13 +080016#include <linux/bitops.h>
17#include <linux/clk-provider.h>
18#include <linux/delay.h>
19#include <linux/err.h>
20
Xingyu Wua3897e62023-07-07 18:50:11 +080021#include "clk.h"
22
Yanhong Wang5a85d052023-03-29 11:42:13 +080023#define UBOOT_DM_CLK_JH7110_PLLX "jh7110_clk_pllx"
24
25#define PLL_PD_OFF 1
26#define PLL_PD_ON 0
27
28#define CLK_DDR_BUS_MASK GENMASK(29, 24)
29#define CLK_DDR_BUS_OFFSET 0xAC
30#define CLK_DDR_BUS_OSC_DIV2 0
31#define CLK_DDR_BUS_PLL1_DIV2 1
32#define CLK_DDR_BUS_PLL1_DIV4 2
33#define CLK_DDR_BUS_PLL1_DIV8 3
34
Xingyu Wua3897e62023-07-07 18:50:11 +080035#define JH7110_PLL_ID_TRANS(id) ((id) + JH7110_EXTCLK_END)
36
Xingyu Wu751e4402023-07-07 18:50:07 +080037enum starfive_pll_type {
38 PLL0 = 0,
39 PLL1,
40 PLL2,
41 PLL_MAX = PLL2
42};
43
44struct starfive_pllx_rate {
45 u64 rate;
46 u32 prediv;
47 u32 fbdiv;
48 u32 frac;
49};
50
51struct starfive_pllx_offset {
52 u32 pd;
53 u32 prediv;
54 u32 fbdiv;
55 u32 frac;
56 u32 postdiv1;
57 u32 dacpd;
58 u32 dsmpd;
59 u32 pd_mask;
60 u32 prediv_mask;
61 u32 fbdiv_mask;
62 u32 frac_mask;
63 u32 postdiv1_mask;
64 u32 dacpd_mask;
65 u32 dsmpd_mask;
66};
67
68struct starfive_pllx_clk {
69 enum starfive_pll_type type;
70 const struct starfive_pllx_offset *offset;
71 const struct starfive_pllx_rate *rate_table;
72 int rate_count;
73 int flags;
74};
75
Yanhong Wang5a85d052023-03-29 11:42:13 +080076struct clk_jh7110_pllx {
77 struct clk clk;
78 void __iomem *base;
79 void __iomem *sysreg;
80 enum starfive_pll_type type;
81 const struct starfive_pllx_offset *offset;
82 const struct starfive_pllx_rate *rate_table;
83 int rate_count;
84};
85
86#define getbits_le32(addr, mask) ((in_le32(addr) & (mask)) >> __ffs((mask)))
87
88#define PLLX_SET(offset, mask, val) do {\
89 reg = readl((ulong *)((ulong)pll->base + (offset))); \
90 reg &= ~(mask); \
91 reg |= (mask) & ((val) << __ffs(mask)); \
92 writel(reg, (ulong *)((ulong)pll->base + (offset))); \
93 } while (0)
94
95#define PLLX_RATE(_rate, _pd, _fd) \
96 { \
97 .rate = (_rate), \
98 .prediv = (_pd), \
99 .fbdiv = (_fd), \
100 }
101
102#define to_clk_pllx(_clk) container_of(_clk, struct clk_jh7110_pllx, clk)
103
104static const struct starfive_pllx_rate jh7110_pll0_tbl[] = {
105 PLLX_RATE(375000000UL, 8, 125),
106 PLLX_RATE(500000000UL, 6, 125),
107 PLLX_RATE(625000000UL, 24, 625),
108 PLLX_RATE(750000000UL, 4, 125),
109 PLLX_RATE(875000000UL, 24, 875),
110 PLLX_RATE(1000000000UL, 3, 125),
111 PLLX_RATE(1250000000UL, 12, 625),
112 PLLX_RATE(1375000000UL, 24, 1375),
113 PLLX_RATE(1500000000UL, 2, 125),
114 PLLX_RATE(1625000000UL, 24, 1625),
115 PLLX_RATE(1750000000UL, 12, 875),
116 PLLX_RATE(1800000000UL, 3, 225),
117};
118
119static const struct starfive_pllx_rate jh7110_pll1_tbl[] = {
120 PLLX_RATE(1066000000UL, 12, 533),
121 PLLX_RATE(1200000000UL, 1, 50),
122 PLLX_RATE(1400000000UL, 6, 350),
123 PLLX_RATE(1600000000UL, 3, 200),
124};
125
126static const struct starfive_pllx_rate jh7110_pll2_tbl[] = {
127 PLLX_RATE(1228800000UL, 15, 768),
128 PLLX_RATE(1188000000UL, 2, 99),
129};
130
131static const struct starfive_pllx_offset jh7110_pll0_offset = {
132 .pd = 0x20,
133 .prediv = 0x24,
134 .fbdiv = 0x1c,
135 .frac = 0x20,
136 .postdiv1 = 0x20,
137 .dacpd = 0x18,
138 .dsmpd = 0x18,
139 .pd_mask = BIT(27),
140 .prediv_mask = GENMASK(5, 0),
141 .fbdiv_mask = GENMASK(11, 0),
142 .frac_mask = GENMASK(23, 0),
143 .postdiv1_mask = GENMASK(29, 28),
144 .dacpd_mask = BIT(24),
145 .dsmpd_mask = BIT(25)
146};
147
148static const struct starfive_pllx_offset jh7110_pll1_offset = {
149 .pd = 0x28,
150 .prediv = 0x2c,
151 .fbdiv = 0x24,
152 .frac = 0x28,
153 .postdiv1 = 0x28,
154 .dacpd = 0x24,
155 .dsmpd = 0x24,
156 .pd_mask = BIT(27),
157 .prediv_mask = GENMASK(5, 0),
158 .fbdiv_mask = GENMASK(28, 17),
159 .frac_mask = GENMASK(23, 0),
160 .postdiv1_mask = GENMASK(29, 28),
161 .dacpd_mask = BIT(15),
162 .dsmpd_mask = BIT(16)
163};
164
165static const struct starfive_pllx_offset jh7110_pll2_offset = {
166 .pd = 0x30,
167 .prediv = 0x34,
168 .fbdiv = 0x2c,
169 .frac = 0x30,
170 .postdiv1 = 0x30,
171 .dacpd = 0x2c,
172 .dsmpd = 0x2c,
173 .pd_mask = BIT(27),
174 .prediv_mask = GENMASK(5, 0),
175 .fbdiv_mask = GENMASK(28, 17),
176 .frac_mask = GENMASK(23, 0),
177 .postdiv1_mask = GENMASK(29, 28),
178 .dacpd_mask = BIT(15),
179 .dsmpd_mask = BIT(16)
180};
181
182struct starfive_pllx_clk starfive_jh7110_pll0 __initdata = {
183 .type = PLL0,
184 .offset = &jh7110_pll0_offset,
185 .rate_table = jh7110_pll0_tbl,
186 .rate_count = ARRAY_SIZE(jh7110_pll0_tbl),
187};
188
189struct starfive_pllx_clk starfive_jh7110_pll1 __initdata = {
190 .type = PLL1,
191 .offset = &jh7110_pll1_offset,
192 .rate_table = jh7110_pll1_tbl,
193 .rate_count = ARRAY_SIZE(jh7110_pll1_tbl),
194};
195
196struct starfive_pllx_clk starfive_jh7110_pll2 __initdata = {
197 .type = PLL2,
198 .offset = &jh7110_pll2_offset,
199 .rate_table = jh7110_pll2_tbl,
200 .rate_count = ARRAY_SIZE(jh7110_pll2_tbl),
201};
202
203static const struct starfive_pllx_rate *
204jh7110_get_pll_settings(struct clk_jh7110_pllx *pll, unsigned long rate)
205{
206 for (int i = 0; i < pll->rate_count; i++)
207 if (rate == pll->rate_table[i].rate)
208 return &pll->rate_table[i];
209
210 return NULL;
211}
212
213static void jh7110_pll_set_rate(struct clk_jh7110_pllx *pll,
214 const struct starfive_pllx_rate *rate)
215{
216 u32 reg;
217 bool set = (pll->type == PLL1) ? true : false;
218
219 if (set) {
220 reg = readl((ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
221 reg &= ~CLK_DDR_BUS_MASK;
222 reg |= CLK_DDR_BUS_OSC_DIV2 << __ffs(CLK_DDR_BUS_MASK);
223 writel(reg, (ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
224 }
225
226 PLLX_SET(pll->offset->pd, pll->offset->pd_mask, PLL_PD_OFF);
227 PLLX_SET(pll->offset->dacpd, pll->offset->dacpd_mask, 1);
228 PLLX_SET(pll->offset->dsmpd, pll->offset->dsmpd_mask, 1);
229 PLLX_SET(pll->offset->prediv, pll->offset->prediv_mask, rate->prediv);
230 PLLX_SET(pll->offset->fbdiv, pll->offset->fbdiv_mask, rate->fbdiv);
Hoegeun Kwondbc74612023-06-28 19:19:49 +0900231 PLLX_SET(pll->offset->postdiv1, pll->offset->postdiv1_mask, 0);
Yanhong Wang5a85d052023-03-29 11:42:13 +0800232 PLLX_SET(pll->offset->pd, pll->offset->pd_mask, PLL_PD_ON);
233
234 if (set) {
235 udelay(100);
236 reg = readl((ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
237 reg &= ~CLK_DDR_BUS_MASK;
238 reg |= CLK_DDR_BUS_PLL1_DIV2 << __ffs(CLK_DDR_BUS_MASK);
239 writel(reg, (ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
240 }
241}
242
243static ulong jh7110_pllx_recalc_rate(struct clk *clk)
244{
245 struct clk_jh7110_pllx *pll = to_clk_pllx(dev_get_clk_ptr(clk->dev));
246 u64 refclk = clk_get_parent_rate(clk);
247 u32 dacpd, dsmpd;
248 u32 prediv, fbdiv, postdiv1;
249 u64 frac;
250
251 dacpd = getbits_le32((ulong)pll->base + pll->offset->dacpd,
252 pll->offset->dacpd_mask);
253 dsmpd = getbits_le32((ulong)pll->base + pll->offset->dsmpd,
254 pll->offset->dsmpd_mask);
255 prediv = getbits_le32((ulong)pll->base + pll->offset->prediv,
256 pll->offset->prediv_mask);
257 fbdiv = getbits_le32((ulong)pll->base + pll->offset->fbdiv,
258 pll->offset->fbdiv_mask);
259 postdiv1 = 1 << getbits_le32((ulong)pll->base + pll->offset->postdiv1,
260 pll->offset->postdiv1_mask);
261 frac = (u64)getbits_le32((ulong)pll->base + pll->offset->frac,
262 pll->offset->frac_mask);
263
264 /* Integer Multiple Mode
265 * Both dacpd and dsmpd should be set as 1 while integer multiple mode.
266 *
267 * The frequency of outputs can be figured out as below.
268 *
269 * Fvco = Fref*Nl/M
270 * NI is integer frequency dividing ratio of feedback divider, set by fbdiv1[11:0] ,
271 * NI = 8, 9, 10, 12.13....4095
272 * M is frequency dividing ratio of pre-divider, set by prediv[5:0],M = 1,2...63
273 *
274 * Fclko1 = Fvco/Q1
275 * Q1 is frequency dividing ratio of post divider, set by postdiv1[1:0],Q1= 1,2,4,8
276 *
277 * Fraction Multiple Mode
278 *
279 * Both dacpd and dsmpd should be set as 0 while integer multiple mode.
280 *
281 * Fvco = Fref*(NI+NF)/M
282 * NI is integer frequency dividing ratio of feedback divider, set by fbdiv[11:0] ,
283 * NI = 8, 9, 10, 12.13....4095
284 * NF is fractional frequency dividing ratio, set by frac[23:0], NF =frac[23:0]/2^24= 0~0.99999994
285 * M is frequency dividing ratio of pre-divider, set by prediv[5:0],M = 1,2...63
286 *
287 * Fclko1 = Fvco/Q1
288 * Q1 is frequency dividing ratio of post divider, set by postdivl[1:0],Q1= 1,2,4,8
289 */
290 if (dacpd == 1 && dsmpd == 1)
291 frac = 0;
292 else if (dacpd == 0 && dsmpd == 0)
293 do_div(frac, 1 << 24);
294 else
295 return -EINVAL;
296
297 refclk *= (fbdiv + frac);
298 do_div(refclk, prediv * postdiv1);
299
300 return refclk;
301}
302
303static ulong jh7110_pllx_set_rate(struct clk *clk, ulong drate)
304{
305 struct clk_jh7110_pllx *pll = to_clk_pllx(dev_get_clk_ptr(clk->dev));
306 const struct starfive_pllx_rate *rate;
307
308 rate = jh7110_get_pll_settings(pll, drate);
309 if (!rate)
310 return -EINVAL;
311
312 jh7110_pll_set_rate(pll, rate);
313
314 return jh7110_pllx_recalc_rate(clk);
315}
316
Xingyu Wu751e4402023-07-07 18:50:07 +0800317static const struct clk_ops jh7110_clk_pllx_ops = {
Yanhong Wang5a85d052023-03-29 11:42:13 +0800318 .set_rate = jh7110_pllx_set_rate,
319 .get_rate = jh7110_pllx_recalc_rate,
320};
321
322struct clk *starfive_jh7110_pll(const char *name, const char *parent_name,
323 void __iomem *base, void __iomem *sysreg,
324 const struct starfive_pllx_clk *pll_clk)
325{
326 struct clk_jh7110_pllx *pll;
327 struct clk *clk;
328 int ret;
329
330 if (!pll_clk || !base || !sysreg)
331 return ERR_PTR(-EINVAL);
332
333 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
334 if (!pll)
335 return ERR_PTR(-ENOMEM);
336
337 pll->base = base;
338 pll->sysreg = sysreg;
339 pll->type = pll_clk->type;
340 pll->offset = pll_clk->offset;
341 pll->rate_table = pll_clk->rate_table;
342 pll->rate_count = pll_clk->rate_count;
343
344 clk = &pll->clk;
345 ret = clk_register(clk, UBOOT_DM_CLK_JH7110_PLLX, name, parent_name);
346 if (ret) {
347 kfree(pll);
348 return ERR_PTR(ret);
349 }
350
351 if (IS_ENABLED(CONFIG_SPL_BUILD) && pll->type == PLL0)
352 jh7110_pllx_set_rate(clk, 1000000000);
353
354 if (IS_ENABLED(CONFIG_SPL_BUILD) && pll->type == PLL2)
355 jh7110_pllx_set_rate(clk, 1188000000);
356
357 return clk;
358}
359
Xingyu Wu751e4402023-07-07 18:50:07 +0800360/* PLLx clock implementation */
Yanhong Wang5a85d052023-03-29 11:42:13 +0800361U_BOOT_DRIVER(jh7110_clk_pllx) = {
362 .name = UBOOT_DM_CLK_JH7110_PLLX,
363 .id = UCLASS_CLK,
Xingyu Wu751e4402023-07-07 18:50:07 +0800364 .ops = &jh7110_clk_pllx_ops,
365 .flags = DM_FLAG_PRE_RELOC,
366};
367
368static int jh7110_pll_clk_probe(struct udevice *dev)
369{
370 void __iomem *reg = (void __iomem *)dev_read_addr_ptr(dev->parent);
371 fdt_addr_t sysreg = ofnode_get_addr(ofnode_by_compatible(ofnode_null(),
372 "starfive,jh7110-syscrg"));
373
374 if (sysreg == FDT_ADDR_T_NONE)
375 return -EINVAL;
376
Xingyu Wua3897e62023-07-07 18:50:11 +0800377 clk_dm(JH7110_PLL_ID_TRANS(JH7110_SYSCLK_PLL0_OUT),
Xingyu Wu751e4402023-07-07 18:50:07 +0800378 starfive_jh7110_pll("pll0_out", "oscillator", reg,
379 (void __iomem *)sysreg, &starfive_jh7110_pll0));
Xingyu Wua3897e62023-07-07 18:50:11 +0800380 clk_dm(JH7110_PLL_ID_TRANS(JH7110_SYSCLK_PLL1_OUT),
Xingyu Wu751e4402023-07-07 18:50:07 +0800381 starfive_jh7110_pll("pll1_out", "oscillator", reg,
382 (void __iomem *)sysreg, &starfive_jh7110_pll1));
Xingyu Wua3897e62023-07-07 18:50:11 +0800383 clk_dm(JH7110_PLL_ID_TRANS(JH7110_SYSCLK_PLL2_OUT),
Xingyu Wu751e4402023-07-07 18:50:07 +0800384 starfive_jh7110_pll("pll2_out", "oscillator", reg,
385 (void __iomem *)sysreg, &starfive_jh7110_pll2));
386
387 return 0;
388}
389
Xingyu Wua3897e62023-07-07 18:50:11 +0800390static int jh7110_pll_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
391{
392 if (args->args_count > 1) {
393 debug("Invalid args_count: %d\n", args->args_count);
394 return -EINVAL;
395 }
396
397 if (args->args_count)
398 clk->id = JH7110_PLL_ID_TRANS(args->args[0]);
399 else
400 clk->id = 0;
401
402 return 0;
403}
404
Xingyu Wu751e4402023-07-07 18:50:07 +0800405static const struct udevice_id jh7110_pll_clk_of_match[] = {
406 { .compatible = "starfive,jh7110-pll", },
407 { }
408};
409
Xingyu Wua3897e62023-07-07 18:50:11 +0800410JH7110_CLK_OPS(pll);
411
Xingyu Wu751e4402023-07-07 18:50:07 +0800412/* PLL clk device */
413U_BOOT_DRIVER(jh7110_pll_clk) = {
414 .name = "jh7110_pll_clk",
415 .id = UCLASS_CLK,
416 .of_match = jh7110_pll_clk_of_match,
417 .probe = jh7110_pll_clk_probe,
Xingyu Wua3897e62023-07-07 18:50:11 +0800418 .ops = &jh7110_pll_clk_ops,
Yanhong Wang5a85d052023-03-29 11:42:13 +0800419};