1 // SPDX-License-Identifier: GPL-2.0
3 * (C) Copyright 2017 Rockchip Electronics Co., Ltd
7 #include <clk-uclass.h>
14 #include <asm/arch-rockchip/clock.h>
15 #include <asm/arch-rockchip/cru_rk322x.h>
16 #include <asm/arch-rockchip/hardware.h>
18 #include <dt-bindings/clock/rk3228-cru.h>
19 #include <linux/log2.h>
20 #include <linux/stringify.h>
23 VCO_MAX_HZ
= 3200U * 1000000,
24 VCO_MIN_HZ
= 800 * 1000000,
25 OUTPUT_MAX_HZ
= 3200U * 1000000,
26 OUTPUT_MIN_HZ
= 24 * 1000000,
29 #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
31 #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
33 .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ), \
34 .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\
35 _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) * \
36 OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz, \
37 #hz "Hz cannot be hit with PLL "\
38 "divisors on line " __stringify(__LINE__));
41 static const struct pll_div apll_init_cfg
= PLL_DIVISORS(APLL_HZ
, 1, 3, 1);
42 static const struct pll_div gpll_init_cfg
= PLL_DIVISORS(GPLL_HZ
, 2, 2, 1);
44 static int rkclk_set_pll(struct rk322x_cru
*cru
, enum rk_clk_id clk_id
,
45 const struct pll_div
*div
)
47 int pll_id
= rk_pll_id(clk_id
);
48 struct rk322x_pll
*pll
= &cru
->pll
[pll_id
];
50 /* All PLLs have same VCO and output frequency range restrictions. */
51 uint vco_hz
= OSC_HZ
/ 1000 * div
->fbdiv
/ div
->refdiv
* 1000;
52 uint output_hz
= vco_hz
/ div
->postdiv1
/ div
->postdiv2
;
54 debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n",
55 pll
, div
->fbdiv
, div
->refdiv
, div
->postdiv1
,
56 div
->postdiv2
, vco_hz
, output_hz
);
57 assert(vco_hz
>= VCO_MIN_HZ
&& vco_hz
<= VCO_MAX_HZ
&&
58 output_hz
>= OUTPUT_MIN_HZ
&& output_hz
<= OUTPUT_MAX_HZ
);
60 /* use integer mode */
61 rk_setreg(&pll
->con1
, 1 << PLL_DSMPD_SHIFT
);
63 rk_setreg(&pll
->con1
, 1 << PLL_PD_SHIFT
);
65 rk_clrsetreg(&pll
->con0
,
66 PLL_POSTDIV1_MASK
| PLL_FBDIV_MASK
,
67 (div
->postdiv1
<< PLL_POSTDIV1_SHIFT
) | div
->fbdiv
);
68 rk_clrsetreg(&pll
->con1
, PLL_POSTDIV2_MASK
| PLL_REFDIV_MASK
,
69 (div
->postdiv2
<< PLL_POSTDIV2_SHIFT
|
70 div
->refdiv
<< PLL_REFDIV_SHIFT
));
73 rk_clrreg(&pll
->con1
, 1 << PLL_PD_SHIFT
);
75 /* waiting for pll lock */
76 while (readl(&pll
->con1
) & (1 << PLL_LOCK_STATUS_SHIFT
))
82 static void rkclk_init(struct rk322x_cru
*cru
)
88 /* pll enter slow-mode */
89 rk_clrsetreg(&cru
->cru_mode_con
,
90 GPLL_MODE_MASK
| APLL_MODE_MASK
,
91 GPLL_MODE_SLOW
<< GPLL_MODE_SHIFT
|
92 APLL_MODE_SLOW
<< APLL_MODE_SHIFT
);
95 rkclk_set_pll(cru
, CLK_ARM
, &apll_init_cfg
);
96 rkclk_set_pll(cru
, CLK_GENERAL
, &gpll_init_cfg
);
99 * select apll as cpu/core clock pll source and
100 * set up dependent divisors for PERI and ACLK clocks.
101 * core hz : apll = 1:1
103 aclk_div
= APLL_HZ
/ CORE_ACLK_HZ
- 1;
104 assert((aclk_div
+ 1) * CORE_ACLK_HZ
== APLL_HZ
&& aclk_div
< 0x7);
106 pclk_div
= APLL_HZ
/ CORE_PERI_HZ
- 1;
107 assert((pclk_div
+ 1) * CORE_PERI_HZ
== APLL_HZ
&& pclk_div
< 0xf);
109 rk_clrsetreg(&cru
->cru_clksel_con
[0],
110 CORE_CLK_PLL_SEL_MASK
| CORE_DIV_CON_MASK
,
111 CORE_CLK_PLL_SEL_APLL
<< CORE_CLK_PLL_SEL_SHIFT
|
112 0 << CORE_DIV_CON_SHIFT
);
114 rk_clrsetreg(&cru
->cru_clksel_con
[1],
115 CORE_ACLK_DIV_MASK
| CORE_PERI_DIV_MASK
,
116 aclk_div
<< CORE_ACLK_DIV_SHIFT
|
117 pclk_div
<< CORE_PERI_DIV_SHIFT
);
120 * select gpll as pd_bus bus clock source and
121 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
123 aclk_div
= GPLL_HZ
/ BUS_ACLK_HZ
- 1;
124 assert((aclk_div
+ 1) * BUS_ACLK_HZ
== GPLL_HZ
&& aclk_div
<= 0x1f);
126 pclk_div
= BUS_ACLK_HZ
/ BUS_PCLK_HZ
- 1;
127 assert((pclk_div
+ 1) * BUS_PCLK_HZ
== BUS_ACLK_HZ
&& pclk_div
<= 0x7);
129 hclk_div
= BUS_ACLK_HZ
/ BUS_HCLK_HZ
- 1;
130 assert((hclk_div
+ 1) * BUS_HCLK_HZ
== BUS_ACLK_HZ
&& hclk_div
<= 0x3);
132 rk_clrsetreg(&cru
->cru_clksel_con
[0],
133 BUS_ACLK_PLL_SEL_MASK
| BUS_ACLK_DIV_MASK
,
134 BUS_ACLK_PLL_SEL_GPLL
<< BUS_ACLK_PLL_SEL_SHIFT
|
135 aclk_div
<< BUS_ACLK_DIV_SHIFT
);
137 rk_clrsetreg(&cru
->cru_clksel_con
[1],
138 BUS_PCLK_DIV_MASK
| BUS_HCLK_DIV_MASK
,
139 pclk_div
<< BUS_PCLK_DIV_SHIFT
|
140 hclk_div
<< BUS_HCLK_DIV_SHIFT
);
143 * select gpll as pd_peri bus clock source and
144 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
146 aclk_div
= GPLL_HZ
/ PERI_ACLK_HZ
- 1;
147 assert((aclk_div
+ 1) * PERI_ACLK_HZ
== GPLL_HZ
&& aclk_div
< 0x1f);
149 hclk_div
= ilog2(PERI_ACLK_HZ
/ PERI_HCLK_HZ
);
150 assert((1 << hclk_div
) * PERI_HCLK_HZ
==
151 PERI_ACLK_HZ
&& (hclk_div
< 0x4));
153 pclk_div
= ilog2(PERI_ACLK_HZ
/ PERI_PCLK_HZ
);
154 assert((1 << pclk_div
) * PERI_PCLK_HZ
==
155 PERI_ACLK_HZ
&& pclk_div
< 0x8);
157 rk_clrsetreg(&cru
->cru_clksel_con
[10],
158 PERI_PLL_SEL_MASK
| PERI_PCLK_DIV_MASK
|
159 PERI_HCLK_DIV_MASK
| PERI_ACLK_DIV_MASK
,
160 PERI_PLL_GPLL
<< PERI_PLL_SEL_SHIFT
|
161 pclk_div
<< PERI_PCLK_DIV_SHIFT
|
162 hclk_div
<< PERI_HCLK_DIV_SHIFT
|
163 aclk_div
<< PERI_ACLK_DIV_SHIFT
);
165 /* PLL enter normal-mode */
166 rk_clrsetreg(&cru
->cru_mode_con
,
167 GPLL_MODE_MASK
| APLL_MODE_MASK
,
168 GPLL_MODE_NORM
<< GPLL_MODE_SHIFT
|
169 APLL_MODE_NORM
<< APLL_MODE_SHIFT
);
172 /* Get pll rate by id */
173 static uint32_t rkclk_pll_get_rate(struct rk322x_cru
*cru
,
174 enum rk_clk_id clk_id
)
176 uint32_t refdiv
, fbdiv
, postdiv1
, postdiv2
;
178 int pll_id
= rk_pll_id(clk_id
);
179 struct rk322x_pll
*pll
= &cru
->pll
[pll_id
];
180 static u8 clk_shift
[CLK_COUNT
] = {
181 0xff, APLL_MODE_SHIFT
, DPLL_MODE_SHIFT
, 0xff,
182 GPLL_MODE_SHIFT
, 0xff
184 static u32 clk_mask
[CLK_COUNT
] = {
185 0xff, APLL_MODE_MASK
, DPLL_MODE_MASK
, 0xff,
191 con
= readl(&cru
->cru_mode_con
);
192 shift
= clk_shift
[clk_id
];
193 mask
= clk_mask
[clk_id
];
195 switch ((con
& mask
) >> shift
) {
201 con
= readl(&pll
->con0
);
202 postdiv1
= (con
& PLL_POSTDIV1_MASK
) >> PLL_POSTDIV1_SHIFT
;
203 fbdiv
= (con
& PLL_FBDIV_MASK
) >> PLL_FBDIV_SHIFT
;
204 con
= readl(&pll
->con1
);
205 postdiv2
= (con
& PLL_POSTDIV2_MASK
) >> PLL_POSTDIV2_SHIFT
;
206 refdiv
= (con
& PLL_REFDIV_MASK
) >> PLL_REFDIV_SHIFT
;
207 return (24 * fbdiv
/ (refdiv
* postdiv1
* postdiv2
)) * 1000000;
213 static ulong
rockchip_mmc_get_clk(struct rk322x_cru
*cru
, uint clk_general_rate
,
223 case SCLK_EMMC_SAMPLE
:
224 con
= readl(&cru
->cru_clksel_con
[11]);
225 mux
= (con
& EMMC_PLL_MASK
) >> EMMC_PLL_SHIFT
;
226 con
= readl(&cru
->cru_clksel_con
[12]);
227 div
= (con
& EMMC_DIV_MASK
) >> EMMC_DIV_SHIFT
;
231 con
= readl(&cru
->cru_clksel_con
[11]);
232 mux
= (con
& MMC0_PLL_MASK
) >> MMC0_PLL_SHIFT
;
233 div
= (con
& MMC0_DIV_MASK
) >> MMC0_DIV_SHIFT
;
239 src_rate
= mux
== EMMC_SEL_24M
? OSC_HZ
: clk_general_rate
;
240 return DIV_TO_RATE(src_rate
, div
) / 2;
243 static ulong
rk322x_mac_set_clk(struct rk322x_cru
*cru
, uint freq
)
248 * The gmac clock can be derived either from an external clock
249 * or can be generated from internally by a divider from SCLK_MAC.
251 if (readl(&cru
->cru_clksel_con
[5]) & BIT(5)) {
252 /* An external clock will always generate the right rate... */
255 u32 con
= readl(&cru
->cru_clksel_con
[5]);
259 if ((con
>> MAC_PLL_SEL_SHIFT
) & MAC_PLL_SEL_MASK
)
262 /* CPLL is not set */
265 div
= DIV_ROUND_UP(pll_rate
, freq
) - 1;
267 rk_clrsetreg(&cru
->cru_clksel_con
[5], CLK_MAC_DIV_MASK
,
268 div
<< CLK_MAC_DIV_SHIFT
);
270 debug("Unsupported div for gmac:%d\n", div
);
272 return DIV_TO_RATE(pll_rate
, div
);
278 static ulong
rockchip_mmc_set_clk(struct rk322x_cru
*cru
, uint clk_general_rate
,
279 int periph
, uint freq
)
284 debug("%s: clk_general_rate=%u\n", __func__
, clk_general_rate
);
286 /* mmc clock defaulg div 2 internal, need provide double in cru */
287 src_clk_div
= DIV_ROUND_UP(clk_general_rate
/ 2, freq
);
289 if (src_clk_div
> 128) {
290 src_clk_div
= DIV_ROUND_UP(OSC_HZ
/ 2, freq
);
291 assert(src_clk_div
- 1 < 128);
300 case SCLK_EMMC_SAMPLE
:
301 rk_clrsetreg(&cru
->cru_clksel_con
[11],
303 mux
<< EMMC_PLL_SHIFT
);
304 rk_clrsetreg(&cru
->cru_clksel_con
[12],
306 (src_clk_div
- 1) << EMMC_DIV_SHIFT
);
310 rk_clrsetreg(&cru
->cru_clksel_con
[11],
311 MMC0_PLL_MASK
| MMC0_DIV_MASK
,
312 mux
<< MMC0_PLL_SHIFT
|
313 (src_clk_div
- 1) << MMC0_DIV_SHIFT
);
319 return rockchip_mmc_get_clk(cru
, clk_general_rate
, periph
);
322 static int rk322x_ddr_set_clk(struct rk322x_cru
*cru
, unsigned int set_rate
)
324 struct pll_div dpll_cfg
;
326 /* clk_ddrc == DPLL = 24MHz / refdiv * fbdiv / postdiv1 / postdiv2 */
329 dpll_cfg
= (struct pll_div
)
330 {.refdiv
= 1, .fbdiv
= 50, .postdiv1
= 3, .postdiv2
= 1};
333 dpll_cfg
= (struct pll_div
)
334 {.refdiv
= 1, .fbdiv
= 75, .postdiv1
= 3, .postdiv2
= 1};
337 dpll_cfg
= (struct pll_div
)
338 {.refdiv
= 1, .fbdiv
= 100, .postdiv1
= 3, .postdiv2
= 1};
342 /* pll enter slow-mode */
343 rk_clrsetreg(&cru
->cru_mode_con
, DPLL_MODE_MASK
,
344 DPLL_MODE_SLOW
<< DPLL_MODE_SHIFT
);
345 rkclk_set_pll(cru
, CLK_DDR
, &dpll_cfg
);
346 /* PLL enter normal-mode */
347 rk_clrsetreg(&cru
->cru_mode_con
, DPLL_MODE_MASK
,
348 DPLL_MODE_NORM
<< DPLL_MODE_SHIFT
);
352 static ulong
rk322x_clk_get_rate(struct clk
*clk
)
354 struct rk322x_clk_priv
*priv
= dev_get_priv(clk
->dev
);
355 ulong rate
, gclk_rate
;
357 gclk_rate
= rkclk_pll_get_rate(priv
->cru
, CLK_GENERAL
);
360 rate
= rkclk_pll_get_rate(priv
->cru
, clk
->id
);
366 rate
= rockchip_mmc_get_clk(priv
->cru
, gclk_rate
, clk
->id
);
375 static ulong
rk322x_clk_set_rate(struct clk
*clk
, ulong rate
)
377 struct rk322x_clk_priv
*priv
= dev_get_priv(clk
->dev
);
378 ulong new_rate
, gclk_rate
;
380 gclk_rate
= rkclk_pll_get_rate(priv
->cru
, CLK_GENERAL
);
386 new_rate
= rockchip_mmc_set_clk(priv
->cru
, gclk_rate
,
390 new_rate
= rk322x_ddr_set_clk(priv
->cru
, rate
);
393 new_rate
= rk322x_mac_set_clk(priv
->cru
, rate
);
404 static int rk322x_gmac_set_parent(struct clk
*clk
, struct clk
*parent
)
406 struct rk322x_clk_priv
*priv
= dev_get_priv(clk
->dev
);
407 struct rk322x_cru
*cru
= priv
->cru
;
410 * If the requested parent is in the same clock-controller and the id
411 * is SCLK_MAC_SRC ("sclk_gmac_src"), switch to the internal clock.
413 if ((parent
->dev
== clk
->dev
) && (parent
->id
== SCLK_MAC_SRC
)) {
414 debug("%s: switching RGMII to SCLK_MAC_SRC\n", __func__
);
415 rk_clrsetreg(&cru
->cru_clksel_con
[5], BIT(5), 0);
420 * If the requested parent is in the same clock-controller and the id
421 * is SCLK_MAC_EXTCLK (sclk_mac_extclk), switch to the external clock.
423 if ((parent
->dev
== clk
->dev
) && (parent
->id
== SCLK_MAC_EXTCLK
)) {
424 debug("%s: switching RGMII to SCLK_MAC_EXTCLK\n", __func__
);
425 rk_clrsetreg(&cru
->cru_clksel_con
[5], BIT(5), BIT(5));
432 static int rk322x_gmac_extclk_set_parent(struct clk
*clk
, struct clk
*parent
)
434 struct rk322x_clk_priv
*priv
= dev_get_priv(clk
->dev
);
435 const char *clock_output_name
;
436 struct rk322x_cru
*cru
= priv
->cru
;
439 ret
= dev_read_string_index(parent
->dev
, "clock-output-names",
440 parent
->id
, &clock_output_name
);
444 if (!strcmp(clock_output_name
, "ext_gmac")) {
445 debug("%s: switching gmac extclk to ext_gmac\n", __func__
);
446 rk_clrsetreg(&cru
->cru_clksel_con
[29], BIT(10), 0);
448 } else if (!strcmp(clock_output_name
, "phy_50m_out")) {
449 debug("%s: switching gmac extclk to phy_50m_out\n", __func__
);
450 rk_clrsetreg(&cru
->cru_clksel_con
[29], BIT(10), BIT(10));
457 static int rk322x_clk_set_parent(struct clk
*clk
, struct clk
*parent
)
461 return rk322x_gmac_set_parent(clk
, parent
);
462 case SCLK_MAC_EXTCLK
:
463 return rk322x_gmac_extclk_set_parent(clk
, parent
);
466 debug("%s: unsupported clk %ld\n", __func__
, clk
->id
);
470 static struct clk_ops rk322x_clk_ops
= {
471 .get_rate
= rk322x_clk_get_rate
,
472 .set_rate
= rk322x_clk_set_rate
,
473 .set_parent
= rk322x_clk_set_parent
,
476 static int rk322x_clk_ofdata_to_platdata(struct udevice
*dev
)
478 struct rk322x_clk_priv
*priv
= dev_get_priv(dev
);
480 priv
->cru
= dev_read_addr_ptr(dev
);
485 static int rk322x_clk_probe(struct udevice
*dev
)
487 struct rk322x_clk_priv
*priv
= dev_get_priv(dev
);
489 rkclk_init(priv
->cru
);
494 static int rk322x_clk_bind(struct udevice
*dev
)
497 struct udevice
*sys_child
;
498 struct sysreset_reg
*priv
;
500 /* The reset driver does not have a device node, so bind it here */
501 ret
= device_bind_driver(dev
, "rockchip_sysreset", "sysreset",
504 debug("Warning: No sysreset driver: ret=%d\n", ret
);
506 priv
= malloc(sizeof(struct sysreset_reg
));
507 priv
->glb_srst_fst_value
= offsetof(struct rk322x_cru
,
508 cru_glb_srst_fst_value
);
509 priv
->glb_srst_snd_value
= offsetof(struct rk322x_cru
,
510 cru_glb_srst_snd_value
);
511 sys_child
->priv
= priv
;
514 #if CONFIG_IS_ENABLED(RESET_ROCKCHIP)
515 ret
= offsetof(struct rk322x_cru
, cru_softrst_con
[0]);
516 ret
= rockchip_reset_bind(dev
, ret
, 9);
518 debug("Warning: software reset driver bind faile\n");
524 static const struct udevice_id rk322x_clk_ids
[] = {
525 { .compatible
= "rockchip,rk3228-cru" },
529 U_BOOT_DRIVER(rockchip_rk322x_cru
) = {
530 .name
= "clk_rk322x",
532 .of_match
= rk322x_clk_ids
,
533 .priv_auto_alloc_size
= sizeof(struct rk322x_clk_priv
),
534 .ofdata_to_platdata
= rk322x_clk_ofdata_to_platdata
,
535 .ops
= &rk322x_clk_ops
,
536 .bind
= rk322x_clk_bind
,
537 .probe
= rk322x_clk_probe
,