]>
Commit | Line | Data |
---|---|---|
3f2ef139 | 1 | /* |
2 | * (C) Copyright 2015 Google, Inc | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0 | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
135aa950 | 8 | #include <clk-uclass.h> |
3f2ef139 | 9 | #include <dm.h> |
10 | #include <errno.h> | |
11 | #include <syscon.h> | |
12 | #include <asm/io.h> | |
13 | #include <asm/arch/clock.h> | |
14 | #include <asm/arch/cru_rk3036.h> | |
15 | #include <asm/arch/hardware.h> | |
3f2ef139 | 16 | #include <dm/lists.h> |
898d6439 | 17 | #include <dt-bindings/clock/rk3036-cru.h> |
abd0128e | 18 | #include <linux/log2.h> |
3f2ef139 | 19 | |
20 | DECLARE_GLOBAL_DATA_PTR; | |
21 | ||
3f2ef139 | 22 | enum { |
23 | VCO_MAX_HZ = 2400U * 1000000, | |
24 | VCO_MIN_HZ = 600 * 1000000, | |
25 | OUTPUT_MAX_HZ = 2400U * 1000000, | |
26 | OUTPUT_MIN_HZ = 24 * 1000000, | |
27 | }; | |
28 | ||
29 | #define RATE_TO_DIV(input_rate, output_rate) \ | |
30 | ((input_rate) / (output_rate) - 1); | |
31 | ||
32 | #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) | |
33 | ||
34 | #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ | |
35 | .refdiv = _refdiv,\ | |
36 | .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ | |
37 | .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\ | |
38 | _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\ | |
39 | OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\ | |
40 | #hz "Hz cannot be hit with PLL "\ | |
41 | "divisors on line " __stringify(__LINE__)); | |
42 | ||
6a464d9c | 43 | /* use integer mode*/ |
3f2ef139 | 44 | static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1); |
45 | static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); | |
46 | ||
3f2ef139 | 47 | static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id, |
48 | const struct pll_div *div) | |
49 | { | |
50 | int pll_id = rk_pll_id(clk_id); | |
51 | struct rk3036_pll *pll = &cru->pll[pll_id]; | |
52 | ||
53 | /* All PLLs have same VCO and output frequency range restrictions. */ | |
54 | uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000; | |
55 | uint output_hz = vco_hz / div->postdiv1 / div->postdiv2; | |
56 | ||
57 | debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\ | |
58 | vco=%u Hz, output=%u Hz\n", | |
59 | pll, div->fbdiv, div->refdiv, div->postdiv1, | |
60 | div->postdiv2, vco_hz, output_hz); | |
61 | assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && | |
62 | output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ); | |
63 | ||
6a464d9c KY |
64 | /* use integer mode */ |
65 | rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); | |
3f2ef139 | 66 | |
67 | rk_clrsetreg(&pll->con0, | |
37943aae | 68 | PLL_POSTDIV1_MASK | PLL_FBDIV_MASK, |
3f2ef139 | 69 | (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv); |
37943aae KY |
70 | rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, |
71 | (div->postdiv2 << PLL_POSTDIV2_SHIFT | | |
72 | div->refdiv << PLL_REFDIV_SHIFT)); | |
3f2ef139 | 73 | |
74 | /* waiting for pll lock */ | |
75 | while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)) | |
76 | udelay(1); | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | static void rkclk_init(struct rk3036_cru *cru) | |
82 | { | |
83 | u32 aclk_div; | |
84 | u32 hclk_div; | |
85 | u32 pclk_div; | |
86 | ||
87 | /* pll enter slow-mode */ | |
88 | rk_clrsetreg(&cru->cru_mode_con, | |
37943aae | 89 | GPLL_MODE_MASK | APLL_MODE_MASK, |
3f2ef139 | 90 | GPLL_MODE_SLOW << GPLL_MODE_SHIFT | |
91 | APLL_MODE_SLOW << APLL_MODE_SHIFT); | |
92 | ||
93 | /* init pll */ | |
94 | rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); | |
95 | rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); | |
96 | ||
97 | /* | |
37943aae KY |
98 | * select apll as cpu/core clock pll source and |
99 | * set up dependent divisors for PERI and ACLK clocks. | |
3f2ef139 | 100 | * core hz : apll = 1:1 |
101 | */ | |
102 | aclk_div = APLL_HZ / CORE_ACLK_HZ - 1; | |
103 | assert((aclk_div + 1) * CORE_ACLK_HZ == APLL_HZ && aclk_div < 0x7); | |
104 | ||
105 | pclk_div = APLL_HZ / CORE_PERI_HZ - 1; | |
106 | assert((pclk_div + 1) * CORE_PERI_HZ == APLL_HZ && pclk_div < 0xf); | |
107 | ||
108 | rk_clrsetreg(&cru->cru_clksel_con[0], | |
37943aae | 109 | CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK, |
3f2ef139 | 110 | CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | |
111 | 0 << CORE_DIV_CON_SHIFT); | |
112 | ||
113 | rk_clrsetreg(&cru->cru_clksel_con[1], | |
37943aae | 114 | CORE_ACLK_DIV_MASK | CORE_PERI_DIV_MASK, |
3f2ef139 | 115 | aclk_div << CORE_ACLK_DIV_SHIFT | |
116 | pclk_div << CORE_PERI_DIV_SHIFT); | |
117 | ||
118 | /* | |
1960b010 | 119 | * select apll as pd_bus bus clock source and |
3f2ef139 | 120 | * set up dependent divisors for PCLK/HCLK and ACLK clocks. |
121 | */ | |
1960b010 KY |
122 | aclk_div = GPLL_HZ / BUS_ACLK_HZ - 1; |
123 | assert((aclk_div + 1) * BUS_ACLK_HZ == GPLL_HZ && aclk_div <= 0x1f); | |
3f2ef139 | 124 | |
1960b010 KY |
125 | pclk_div = GPLL_HZ / BUS_PCLK_HZ - 1; |
126 | assert((pclk_div + 1) * BUS_PCLK_HZ == GPLL_HZ && pclk_div <= 0x7); | |
3f2ef139 | 127 | |
1960b010 KY |
128 | hclk_div = GPLL_HZ / BUS_HCLK_HZ - 1; |
129 | assert((hclk_div + 1) * BUS_HCLK_HZ == GPLL_HZ && hclk_div <= 0x3); | |
3f2ef139 | 130 | |
131 | rk_clrsetreg(&cru->cru_clksel_con[0], | |
37943aae | 132 | BUS_ACLK_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, |
1960b010 | 133 | BUS_ACLK_PLL_SEL_GPLL << BUS_ACLK_PLL_SEL_SHIFT | |
37943aae | 134 | aclk_div << BUS_ACLK_DIV_SHIFT); |
3f2ef139 | 135 | |
136 | rk_clrsetreg(&cru->cru_clksel_con[1], | |
37943aae KY |
137 | BUS_PCLK_DIV_MASK | BUS_HCLK_DIV_MASK, |
138 | pclk_div << BUS_PCLK_DIV_SHIFT | | |
139 | hclk_div << BUS_HCLK_DIV_SHIFT); | |
3f2ef139 | 140 | |
141 | /* | |
37943aae | 142 | * select gpll as pd_peri bus clock source and |
3f2ef139 | 143 | * set up dependent divisors for PCLK/HCLK and ACLK clocks. |
144 | */ | |
145 | aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; | |
146 | assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); | |
147 | ||
abd0128e | 148 | hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ); |
3f2ef139 | 149 | assert((1 << hclk_div) * PERI_HCLK_HZ == |
1960b010 | 150 | PERI_ACLK_HZ && (hclk_div < 0x4)); |
3f2ef139 | 151 | |
abd0128e | 152 | pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ); |
3f2ef139 | 153 | assert((1 << pclk_div) * PERI_PCLK_HZ == |
154 | PERI_ACLK_HZ && pclk_div < 0x8); | |
155 | ||
156 | rk_clrsetreg(&cru->cru_clksel_con[10], | |
37943aae KY |
157 | PERI_PLL_SEL_MASK | PERI_PCLK_DIV_MASK | |
158 | PERI_HCLK_DIV_MASK | PERI_ACLK_DIV_MASK, | |
3f2ef139 | 159 | PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | |
160 | pclk_div << PERI_PCLK_DIV_SHIFT | | |
161 | hclk_div << PERI_HCLK_DIV_SHIFT | | |
162 | aclk_div << PERI_ACLK_DIV_SHIFT); | |
163 | ||
164 | /* PLL enter normal-mode */ | |
165 | rk_clrsetreg(&cru->cru_mode_con, | |
37943aae | 166 | GPLL_MODE_MASK | APLL_MODE_MASK, |
3f2ef139 | 167 | GPLL_MODE_NORM << GPLL_MODE_SHIFT | |
168 | APLL_MODE_NORM << APLL_MODE_SHIFT); | |
169 | } | |
170 | ||
171 | /* Get pll rate by id */ | |
172 | static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru, | |
173 | enum rk_clk_id clk_id) | |
174 | { | |
175 | uint32_t refdiv, fbdiv, postdiv1, postdiv2; | |
176 | uint32_t con; | |
177 | int pll_id = rk_pll_id(clk_id); | |
178 | struct rk3036_pll *pll = &cru->pll[pll_id]; | |
179 | static u8 clk_shift[CLK_COUNT] = { | |
180 | 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff, | |
181 | GPLL_MODE_SHIFT, 0xff | |
182 | }; | |
37943aae KY |
183 | static u32 clk_mask[CLK_COUNT] = { |
184 | 0xffffffff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xffffffff, | |
185 | GPLL_MODE_MASK, 0xffffffff | |
3f2ef139 | 186 | }; |
187 | uint shift; | |
188 | uint mask; | |
189 | ||
190 | con = readl(&cru->cru_mode_con); | |
191 | shift = clk_shift[clk_id]; | |
192 | mask = clk_mask[clk_id]; | |
193 | ||
37943aae | 194 | switch ((con & mask) >> shift) { |
3f2ef139 | 195 | case GPLL_MODE_SLOW: |
196 | return OSC_HZ; | |
197 | case GPLL_MODE_NORM: | |
198 | ||
199 | /* normal mode */ | |
200 | con = readl(&pll->con0); | |
37943aae KY |
201 | postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT; |
202 | fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT; | |
3f2ef139 | 203 | con = readl(&pll->con1); |
37943aae KY |
204 | postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT; |
205 | refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT; | |
3f2ef139 | 206 | return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; |
207 | case GPLL_MODE_DEEP: | |
208 | default: | |
209 | return 32768; | |
210 | } | |
211 | } | |
212 | ||
213 | static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate, | |
898d6439 | 214 | int periph) |
3f2ef139 | 215 | { |
216 | uint src_rate; | |
217 | uint div, mux; | |
218 | u32 con; | |
219 | ||
220 | switch (periph) { | |
898d6439 | 221 | case HCLK_EMMC: |
7f0cfe47 | 222 | case SCLK_EMMC: |
3f2ef139 | 223 | con = readl(&cru->cru_clksel_con[12]); |
37943aae KY |
224 | mux = (con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT; |
225 | div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT; | |
3f2ef139 | 226 | break; |
898d6439 | 227 | case HCLK_SDIO: |
7f0cfe47 | 228 | case SCLK_SDIO: |
3f2ef139 | 229 | con = readl(&cru->cru_clksel_con[12]); |
37943aae KY |
230 | mux = (con & MMC0_PLL_MASK) >> MMC0_PLL_SHIFT; |
231 | div = (con & MMC0_DIV_MASK) >> MMC0_DIV_SHIFT; | |
3f2ef139 | 232 | break; |
233 | default: | |
234 | return -EINVAL; | |
235 | } | |
236 | ||
237 | src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate; | |
238 | return DIV_TO_RATE(src_rate, div); | |
239 | } | |
240 | ||
241 | static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate, | |
898d6439 | 242 | int periph, uint freq) |
3f2ef139 | 243 | { |
244 | int src_clk_div; | |
245 | int mux; | |
246 | ||
247 | debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); | |
248 | ||
249 | /* mmc clock auto divide 2 in internal */ | |
250 | src_clk_div = (clk_general_rate / 2 + freq - 1) / freq; | |
251 | ||
252 | if (src_clk_div > 0x7f) { | |
253 | src_clk_div = (OSC_HZ / 2 + freq - 1) / freq; | |
254 | mux = EMMC_SEL_24M; | |
255 | } else { | |
256 | mux = EMMC_SEL_GPLL; | |
257 | } | |
258 | ||
259 | switch (periph) { | |
898d6439 | 260 | case HCLK_EMMC: |
7f0cfe47 | 261 | case SCLK_EMMC: |
3f2ef139 | 262 | rk_clrsetreg(&cru->cru_clksel_con[12], |
37943aae | 263 | EMMC_PLL_MASK | EMMC_DIV_MASK, |
3f2ef139 | 264 | mux << EMMC_PLL_SHIFT | |
265 | (src_clk_div - 1) << EMMC_DIV_SHIFT); | |
266 | break; | |
898d6439 | 267 | case HCLK_SDIO: |
7f0cfe47 | 268 | case SCLK_SDIO: |
3f2ef139 | 269 | rk_clrsetreg(&cru->cru_clksel_con[11], |
37943aae | 270 | MMC0_PLL_MASK | MMC0_DIV_MASK, |
3f2ef139 | 271 | mux << MMC0_PLL_SHIFT | |
272 | (src_clk_div - 1) << MMC0_DIV_SHIFT); | |
273 | break; | |
274 | default: | |
275 | return -EINVAL; | |
276 | } | |
277 | ||
278 | return rockchip_mmc_get_clk(cru, clk_general_rate, periph); | |
279 | } | |
280 | ||
135aa950 | 281 | static ulong rk3036_clk_get_rate(struct clk *clk) |
3f2ef139 | 282 | { |
135aa950 | 283 | struct rk3036_clk_priv *priv = dev_get_priv(clk->dev); |
3f2ef139 | 284 | |
135aa950 SW |
285 | switch (clk->id) { |
286 | case 0 ... 63: | |
287 | return rkclk_pll_get_rate(priv->cru, clk->id); | |
288 | default: | |
289 | return -ENOENT; | |
290 | } | |
3f2ef139 | 291 | } |
292 | ||
135aa950 | 293 | static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate) |
3f2ef139 | 294 | { |
135aa950 SW |
295 | struct rk3036_clk_priv *priv = dev_get_priv(clk->dev); |
296 | ulong new_rate, gclk_rate; | |
3f2ef139 | 297 | |
135aa950 SW |
298 | gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL); |
299 | switch (clk->id) { | |
300 | case 0 ... 63: | |
301 | return 0; | |
898d6439 | 302 | case HCLK_EMMC: |
7f0cfe47 | 303 | case SCLK_EMMC: |
135aa950 SW |
304 | new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate, |
305 | clk->id, rate); | |
3f2ef139 | 306 | break; |
307 | default: | |
308 | return -ENOENT; | |
309 | } | |
310 | ||
311 | return new_rate; | |
312 | } | |
313 | ||
314 | static struct clk_ops rk3036_clk_ops = { | |
315 | .get_rate = rk3036_clk_get_rate, | |
316 | .set_rate = rk3036_clk_set_rate, | |
3f2ef139 | 317 | }; |
318 | ||
319 | static int rk3036_clk_probe(struct udevice *dev) | |
320 | { | |
3f2ef139 | 321 | struct rk3036_clk_priv *priv = dev_get_priv(dev); |
322 | ||
a821c4af | 323 | priv->cru = (struct rk3036_cru *)devfdt_get_addr(dev); |
3f2ef139 | 324 | rkclk_init(priv->cru); |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
3f2ef139 | 329 | static int rk3036_clk_bind(struct udevice *dev) |
330 | { | |
135aa950 | 331 | int ret; |
3f2ef139 | 332 | |
333 | /* The reset driver does not have a device node, so bind it here */ | |
11636258 | 334 | ret = device_bind_driver(gd->dm_root, "rk3036_sysreset", "reset", &dev); |
3f2ef139 | 335 | if (ret) |
336 | debug("Warning: No RK3036 reset driver: ret=%d\n", ret); | |
337 | ||
338 | return 0; | |
339 | } | |
340 | ||
341 | static const struct udevice_id rk3036_clk_ids[] = { | |
342 | { .compatible = "rockchip,rk3036-cru" }, | |
343 | { } | |
344 | }; | |
345 | ||
92ac73e4 | 346 | U_BOOT_DRIVER(rockchip_rk3036_cru) = { |
3f2ef139 | 347 | .name = "clk_rk3036", |
348 | .id = UCLASS_CLK, | |
349 | .of_match = rk3036_clk_ids, | |
350 | .priv_auto_alloc_size = sizeof(struct rk3036_clk_priv), | |
3f2ef139 | 351 | .ops = &rk3036_clk_ops, |
352 | .bind = rk3036_clk_bind, | |
353 | .probe = rk3036_clk_probe, | |
354 | }; |