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