2 * (C) Copyright 2015 Google, Inc
4 * SPDX-License-Identifier: GPL-2.0
13 #include <asm/arch/clock.h>
14 #include <asm/arch/cru_rk3036.h>
15 #include <asm/arch/hardware.h>
17 #include <dt-bindings/clock/rk3036-cru.h>
19 DECLARE_GLOBAL_DATA_PTR
;
21 struct rk3036_clk_plat
{
22 enum rk_clk_id clk_id
;
25 struct rk3036_clk_priv
{
26 struct rk3036_cru
*cru
;
31 VCO_MAX_HZ
= 2400U * 1000000,
32 VCO_MIN_HZ
= 600 * 1000000,
33 OUTPUT_MAX_HZ
= 2400U * 1000000,
34 OUTPUT_MIN_HZ
= 24 * 1000000,
37 #define RATE_TO_DIV(input_rate, output_rate) \
38 ((input_rate) / (output_rate) - 1);
40 #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
42 #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
44 .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
45 .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\
46 _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\
47 OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\
48 #hz "Hz cannot be hit with PLL "\
49 "divisors on line " __stringify(__LINE__));
52 static const struct pll_div apll_init_cfg
= PLL_DIVISORS(APLL_HZ
, 1, 3, 1);
53 static const struct pll_div gpll_init_cfg
= PLL_DIVISORS(GPLL_HZ
, 2, 2, 1);
55 static inline unsigned int log2(unsigned int value
)
57 return fls(value
) - 1;
60 void *rockchip_get_cru(void)
66 ret
= uclass_get_device(UCLASS_CLK
, 0, &dev
);
70 addr
= dev_get_addr(dev
);
71 if (addr
== FDT_ADDR_T_NONE
)
72 return ERR_PTR(-EINVAL
);
77 static int rkclk_set_pll(struct rk3036_cru
*cru
, enum rk_clk_id clk_id
,
78 const struct pll_div
*div
)
80 int pll_id
= rk_pll_id(clk_id
);
81 struct rk3036_pll
*pll
= &cru
->pll
[pll_id
];
83 /* All PLLs have same VCO and output frequency range restrictions. */
84 uint vco_hz
= OSC_HZ
/ 1000 * div
->fbdiv
/ div
->refdiv
* 1000;
85 uint output_hz
= vco_hz
/ div
->postdiv1
/ div
->postdiv2
;
87 debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\
88 vco=%u Hz, output=%u Hz\n",
89 pll
, div
->fbdiv
, div
->refdiv
, div
->postdiv1
,
90 div
->postdiv2
, vco_hz
, output_hz
);
91 assert(vco_hz
>= VCO_MIN_HZ
&& vco_hz
<= VCO_MAX_HZ
&&
92 output_hz
>= OUTPUT_MIN_HZ
&& output_hz
<= OUTPUT_MAX_HZ
);
94 /* use interger mode */
95 rk_clrreg(&pll
->con1
, 1 << PLL_DSMPD_SHIFT
);
97 rk_clrsetreg(&pll
->con0
,
98 PLL_POSTDIV1_MASK
<< PLL_POSTDIV1_SHIFT
| PLL_FBDIV_MASK
,
99 (div
->postdiv1
<< PLL_POSTDIV1_SHIFT
) | div
->fbdiv
);
100 rk_clrsetreg(&pll
->con1
, PLL_POSTDIV2_MASK
<< PLL_POSTDIV2_SHIFT
|
101 PLL_REFDIV_MASK
<< PLL_REFDIV_SHIFT
,
102 (div
->postdiv2
<< PLL_POSTDIV2_SHIFT
|
103 div
->refdiv
<< PLL_REFDIV_SHIFT
));
105 /* waiting for pll lock */
106 while (readl(&pll
->con1
) & (1 << PLL_LOCK_STATUS_SHIFT
))
112 static void rkclk_init(struct rk3036_cru
*cru
)
118 /* pll enter slow-mode */
119 rk_clrsetreg(&cru
->cru_mode_con
,
120 GPLL_MODE_MASK
<< GPLL_MODE_SHIFT
|
121 APLL_MODE_MASK
<< APLL_MODE_SHIFT
,
122 GPLL_MODE_SLOW
<< GPLL_MODE_SHIFT
|
123 APLL_MODE_SLOW
<< APLL_MODE_SHIFT
);
126 rkclk_set_pll(cru
, CLK_ARM
, &apll_init_cfg
);
127 rkclk_set_pll(cru
, CLK_GENERAL
, &gpll_init_cfg
);
130 * select apll as core clock pll source and
131 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
132 * core hz : apll = 1:1
134 aclk_div
= APLL_HZ
/ CORE_ACLK_HZ
- 1;
135 assert((aclk_div
+ 1) * CORE_ACLK_HZ
== APLL_HZ
&& aclk_div
< 0x7);
137 pclk_div
= APLL_HZ
/ CORE_PERI_HZ
- 1;
138 assert((pclk_div
+ 1) * CORE_PERI_HZ
== APLL_HZ
&& pclk_div
< 0xf);
140 rk_clrsetreg(&cru
->cru_clksel_con
[0],
141 CORE_CLK_PLL_SEL_MASK
<< CORE_CLK_PLL_SEL_SHIFT
|
142 CORE_DIV_CON_MASK
<< CORE_DIV_CON_SHIFT
,
143 CORE_CLK_PLL_SEL_APLL
<< CORE_CLK_PLL_SEL_SHIFT
|
144 0 << CORE_DIV_CON_SHIFT
);
146 rk_clrsetreg(&cru
->cru_clksel_con
[1],
147 CORE_ACLK_DIV_MASK
<< CORE_ACLK_DIV_SHIFT
|
148 CORE_PERI_DIV_MASK
<< CORE_PERI_DIV_SHIFT
,
149 aclk_div
<< CORE_ACLK_DIV_SHIFT
|
150 pclk_div
<< CORE_PERI_DIV_SHIFT
);
153 * select apll as cpu clock pll source and
154 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
156 aclk_div
= APLL_HZ
/ CPU_ACLK_HZ
- 1;
157 assert((aclk_div
+ 1) * CPU_ACLK_HZ
== APLL_HZ
&& aclk_div
< 0x1f);
159 pclk_div
= APLL_HZ
/ CPU_PCLK_HZ
- 1;
160 assert((pclk_div
+ 1) * CPU_PCLK_HZ
== APLL_HZ
&& pclk_div
< 0x7);
162 hclk_div
= APLL_HZ
/ CPU_HCLK_HZ
- 1;
163 assert((hclk_div
+ 1) * CPU_HCLK_HZ
== APLL_HZ
&& hclk_div
< 0x3);
165 rk_clrsetreg(&cru
->cru_clksel_con
[0],
166 CPU_CLK_PLL_SEL_MASK
<< CPU_CLK_PLL_SEL_SHIFT
|
167 ACLK_CPU_DIV_MASK
<< ACLK_CPU_DIV_SHIFT
,
168 CPU_CLK_PLL_SEL_APLL
<< CPU_CLK_PLL_SEL_SHIFT
|
169 aclk_div
<< ACLK_CPU_DIV_SHIFT
);
171 rk_clrsetreg(&cru
->cru_clksel_con
[1],
172 CPU_PCLK_DIV_MASK
<< CPU_PCLK_DIV_SHIFT
|
173 CPU_HCLK_DIV_MASK
<< CPU_HCLK_DIV_SHIFT
,
174 pclk_div
<< CPU_PCLK_DIV_SHIFT
|
175 hclk_div
<< CPU_HCLK_DIV_SHIFT
);
178 * select gpll as peri clock pll source and
179 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
181 aclk_div
= GPLL_HZ
/ PERI_ACLK_HZ
- 1;
182 assert((aclk_div
+ 1) * PERI_ACLK_HZ
== GPLL_HZ
&& aclk_div
< 0x1f);
184 hclk_div
= log2(PERI_ACLK_HZ
/ PERI_HCLK_HZ
);
185 assert((1 << hclk_div
) * PERI_HCLK_HZ
==
186 PERI_ACLK_HZ
&& (pclk_div
< 0x4));
188 pclk_div
= log2(PERI_ACLK_HZ
/ PERI_PCLK_HZ
);
189 assert((1 << pclk_div
) * PERI_PCLK_HZ
==
190 PERI_ACLK_HZ
&& pclk_div
< 0x8);
192 rk_clrsetreg(&cru
->cru_clksel_con
[10],
193 PERI_PLL_SEL_MASK
<< PERI_PLL_SEL_SHIFT
|
194 PERI_PCLK_DIV_MASK
<< PERI_PCLK_DIV_SHIFT
|
195 PERI_HCLK_DIV_MASK
<< PERI_HCLK_DIV_SHIFT
|
196 PERI_ACLK_DIV_MASK
<< PERI_ACLK_DIV_SHIFT
,
197 PERI_PLL_GPLL
<< PERI_PLL_SEL_SHIFT
|
198 pclk_div
<< PERI_PCLK_DIV_SHIFT
|
199 hclk_div
<< PERI_HCLK_DIV_SHIFT
|
200 aclk_div
<< PERI_ACLK_DIV_SHIFT
);
202 /* PLL enter normal-mode */
203 rk_clrsetreg(&cru
->cru_mode_con
,
204 GPLL_MODE_MASK
<< GPLL_MODE_SHIFT
|
205 APLL_MODE_MASK
<< APLL_MODE_SHIFT
,
206 GPLL_MODE_NORM
<< GPLL_MODE_SHIFT
|
207 APLL_MODE_NORM
<< APLL_MODE_SHIFT
);
210 /* Get pll rate by id */
211 static uint32_t rkclk_pll_get_rate(struct rk3036_cru
*cru
,
212 enum rk_clk_id clk_id
)
214 uint32_t refdiv
, fbdiv
, postdiv1
, postdiv2
;
216 int pll_id
= rk_pll_id(clk_id
);
217 struct rk3036_pll
*pll
= &cru
->pll
[pll_id
];
218 static u8 clk_shift
[CLK_COUNT
] = {
219 0xff, APLL_MODE_SHIFT
, DPLL_MODE_SHIFT
, 0xff,
220 GPLL_MODE_SHIFT
, 0xff
222 static u8 clk_mask
[CLK_COUNT
] = {
223 0xff, APLL_MODE_MASK
, DPLL_MODE_MASK
, 0xff,
229 con
= readl(&cru
->cru_mode_con
);
230 shift
= clk_shift
[clk_id
];
231 mask
= clk_mask
[clk_id
];
233 switch ((con
>> shift
) & mask
) {
239 con
= readl(&pll
->con0
);
240 postdiv1
= (con
>> PLL_POSTDIV1_SHIFT
) & PLL_POSTDIV1_MASK
;
241 fbdiv
= (con
>> PLL_FBDIV_SHIFT
) & PLL_FBDIV_MASK
;
242 con
= readl(&pll
->con1
);
243 postdiv2
= (con
>> PLL_POSTDIV2_SHIFT
) & PLL_POSTDIV2_MASK
;
244 refdiv
= (con
>> PLL_REFDIV_SHIFT
) & PLL_REFDIV_MASK
;
245 return (24 * fbdiv
/ (refdiv
* postdiv1
* postdiv2
)) * 1000000;
252 static ulong
rockchip_mmc_get_clk(struct rk3036_cru
*cru
, uint clk_general_rate
,
261 con
= readl(&cru
->cru_clksel_con
[12]);
262 mux
= (con
>> EMMC_PLL_SHIFT
) & EMMC_PLL_MASK
;
263 div
= (con
>> EMMC_DIV_SHIFT
) & EMMC_DIV_MASK
;
266 con
= readl(&cru
->cru_clksel_con
[12]);
267 mux
= (con
>> MMC0_PLL_SHIFT
) & MMC0_PLL_MASK
;
268 div
= (con
>> MMC0_DIV_SHIFT
) & MMC0_DIV_MASK
;
274 src_rate
= mux
== EMMC_SEL_24M
? OSC_HZ
: clk_general_rate
;
275 return DIV_TO_RATE(src_rate
, div
);
278 static ulong
rockchip_mmc_set_clk(struct rk3036_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 auto divide 2 in internal */
287 src_clk_div
= (clk_general_rate
/ 2 + freq
- 1) / freq
;
289 if (src_clk_div
> 0x7f) {
290 src_clk_div
= (OSC_HZ
/ 2 + freq
- 1) / freq
;
298 rk_clrsetreg(&cru
->cru_clksel_con
[12],
299 EMMC_PLL_MASK
<< EMMC_PLL_SHIFT
|
300 EMMC_DIV_MASK
<< EMMC_DIV_SHIFT
,
301 mux
<< EMMC_PLL_SHIFT
|
302 (src_clk_div
- 1) << EMMC_DIV_SHIFT
);
305 rk_clrsetreg(&cru
->cru_clksel_con
[11],
306 MMC0_PLL_MASK
<< MMC0_PLL_SHIFT
|
307 MMC0_DIV_MASK
<< MMC0_DIV_SHIFT
,
308 mux
<< MMC0_PLL_SHIFT
|
309 (src_clk_div
- 1) << MMC0_DIV_SHIFT
);
315 return rockchip_mmc_get_clk(cru
, clk_general_rate
, periph
);
318 static ulong
rk3036_clk_get_rate(struct udevice
*dev
)
320 struct rk3036_clk_plat
*plat
= dev_get_platdata(dev
);
321 struct rk3036_clk_priv
*priv
= dev_get_priv(dev
);
323 debug("%s\n", dev
->name
);
324 return rkclk_pll_get_rate(priv
->cru
, plat
->clk_id
);
327 static ulong
rk3036_clk_set_rate(struct udevice
*dev
, ulong rate
)
329 debug("%s\n", dev
->name
);
334 static ulong
rk3036_set_periph_rate(struct udevice
*dev
, int periph
, ulong rate
)
336 struct rk3036_clk_priv
*priv
= dev_get_priv(dev
);
341 new_rate
= rockchip_mmc_set_clk(priv
->cru
, clk_get_rate(dev
),
351 static struct clk_ops rk3036_clk_ops
= {
352 .get_rate
= rk3036_clk_get_rate
,
353 .set_rate
= rk3036_clk_set_rate
,
354 .set_periph_rate
= rk3036_set_periph_rate
,
357 static int rk3036_clk_probe(struct udevice
*dev
)
359 struct rk3036_clk_plat
*plat
= dev_get_platdata(dev
);
360 struct rk3036_clk_priv
*priv
= dev_get_priv(dev
);
362 if (plat
->clk_id
!= CLK_OSC
) {
363 struct rk3036_clk_priv
*parent_priv
= dev_get_priv(dev
->parent
);
365 priv
->cru
= parent_priv
->cru
;
368 priv
->cru
= (struct rk3036_cru
*)dev_get_addr(dev
);
369 rkclk_init(priv
->cru
);
374 static const char *const clk_name
[] = {
383 static int rk3036_clk_bind(struct udevice
*dev
)
385 struct rk3036_clk_plat
*plat
= dev_get_platdata(dev
);
388 /* We only need to set up the root clock */
389 if (dev
->of_offset
== -1) {
390 plat
->clk_id
= CLK_OSC
;
394 /* Create devices for P main clocks */
395 for (pll
= 1; pll
< CLK_COUNT
; pll
++) {
396 struct udevice
*child
;
397 struct rk3036_clk_plat
*cplat
;
399 debug("%s %s\n", __func__
, clk_name
[pll
]);
400 ret
= device_bind_driver(dev
, "clk_rk3036", clk_name
[pll
],
405 cplat
= dev_get_platdata(child
);
409 /* The reset driver does not have a device node, so bind it here */
410 ret
= device_bind_driver(gd
->dm_root
, "rk3036_reset", "reset", &dev
);
412 debug("Warning: No RK3036 reset driver: ret=%d\n", ret
);
417 static const struct udevice_id rk3036_clk_ids
[] = {
418 { .compatible
= "rockchip,rk3036-cru" },
422 U_BOOT_DRIVER(clk_rk3036
) = {
423 .name
= "clk_rk3036",
425 .of_match
= rk3036_clk_ids
,
426 .priv_auto_alloc_size
= sizeof(struct rk3036_clk_priv
),
427 .platdata_auto_alloc_size
= sizeof(struct rk3036_clk_plat
),
428 .ops
= &rk3036_clk_ops
,
429 .bind
= rk3036_clk_bind
,
430 .probe
= rk3036_clk_probe
,