2 * (C) Copyright 2016 Google, Inc
4 * SPDX-License-Identifier: GPL-2.0
8 #include <clk-uclass.h>
11 #include <asm/arch/scu_ast2500.h>
13 #include <dt-bindings/clock/ast2500-scu.h>
15 DECLARE_GLOBAL_DATA_PTR
;
18 * For H-PLL and M-PLL the formula is
19 * (Output Frequency) = CLKIN * ((M + 1) / (N + 1)) / (P + 1)
23 * They have the same layout in their control register.
27 * Get the rate of the M-PLL clock from input clock frequency and
28 * the value of the M-PLL Parameter Register.
30 static ulong
ast2500_get_mpll_rate(ulong clkin
, u32 mpll_reg
)
32 const ulong num
= (mpll_reg
>> SCU_MPLL_NUM_SHIFT
) & SCU_MPLL_NUM_MASK
;
33 const ulong denum
= (mpll_reg
>> SCU_MPLL_DENUM_SHIFT
)
34 & SCU_MPLL_DENUM_MASK
;
35 const ulong post_div
= (mpll_reg
>> SCU_MPLL_POST_SHIFT
)
38 return (clkin
* ((num
+ 1) / (denum
+ 1))) / post_div
;
42 * Get the rate of the H-PLL clock from input clock frequency and
43 * the value of the H-PLL Parameter Register.
45 static ulong
ast2500_get_hpll_rate(ulong clkin
, u32 hpll_reg
)
47 const ulong num
= (hpll_reg
>> SCU_HPLL_NUM_SHIFT
) & SCU_HPLL_NUM_MASK
;
48 const ulong denum
= (hpll_reg
>> SCU_HPLL_DENUM_SHIFT
)
49 & SCU_HPLL_DENUM_MASK
;
50 const ulong post_div
= (hpll_reg
>> SCU_HPLL_POST_SHIFT
)
53 return (clkin
* ((num
+ 1) / (denum
+ 1))) / post_div
;
56 static ulong
ast2500_get_clkin(struct ast2500_scu
*scu
)
58 return readl(&scu
->hwstrap
) & SCU_HWSTRAP_CLKIN_25MHZ
59 ? 25 * 1000 * 1000 : 24 * 1000 * 1000;
63 * Get current rate or uart clock
66 * @uart_index UART index, 1-5
68 * @return current setting for uart clock rate
70 static ulong
ast2500_get_uart_clk_rate(struct ast2500_scu
*scu
, int uart_index
)
73 * ast2500 datasheet is very confusing when it comes to UART clocks,
74 * especially when CLKIN = 25 MHz. The settings are in
75 * different registers and it is unclear how they interact.
77 * This has only been tested with default settings and CLKIN = 24 MHz.
81 if (readl(&scu
->misc_ctrl2
) &
82 (1 << (uart_index
- 1 + SCU_MISC2_UARTCLK_SHIFT
)))
83 uart_clkin
= 192 * 1000 * 1000;
85 uart_clkin
= 24 * 1000 * 1000;
87 if (readl(&scu
->misc_ctrl1
) & SCU_MISC_UARTCLK_DIV13
)
93 static ulong
ast2500_clk_get_rate(struct clk
*clk
)
95 struct ast2500_clk_priv
*priv
= dev_get_priv(clk
->dev
);
96 ulong clkin
= ast2500_get_clkin(priv
->scu
);
103 * This ignores dynamic/static slowdown of ARMCLK and may
106 rate
= ast2500_get_hpll_rate(clkin
,
107 readl(&priv
->scu
->h_pll_param
));
110 rate
= ast2500_get_mpll_rate(clkin
,
111 readl(&priv
->scu
->m_pll_param
));
114 rate
= ast2500_get_uart_clk_rate(priv
->scu
, 1);
117 rate
= ast2500_get_uart_clk_rate(priv
->scu
, 2);
120 rate
= ast2500_get_uart_clk_rate(priv
->scu
, 3);
123 rate
= ast2500_get_uart_clk_rate(priv
->scu
, 4);
126 rate
= ast2500_get_uart_clk_rate(priv
->scu
, 5);
135 static void ast2500_scu_unlock(struct ast2500_scu
*scu
)
137 writel(SCU_UNLOCK_VALUE
, &scu
->protection_key
);
138 while (!readl(&scu
->protection_key
))
142 static void ast2500_scu_lock(struct ast2500_scu
*scu
)
144 writel(~SCU_UNLOCK_VALUE
, &scu
->protection_key
);
145 while (readl(&scu
->protection_key
))
149 static ulong
ast2500_configure_ddr(struct ast2500_scu
*scu
, ulong rate
)
151 ulong clkin
= ast2500_get_clkin(scu
);
155 * There are not that many combinations of numerator, denumerator
156 * and post divider, so just brute force the best combination.
157 * However, to avoid overflow when multiplying, use kHz.
159 const ulong clkin_khz
= clkin
/ 1000;
160 const ulong rate_khz
= rate
/ 1000;
162 ulong best_denum
= 0;
165 ulong num
, denum
, post
;
167 for (denum
= 0; denum
<= SCU_MPLL_DENUM_MASK
; ++denum
) {
168 for (post
= 0; post
<= SCU_MPLL_POST_MASK
; ++post
) {
169 num
= (rate_khz
* (post
+ 1) / clkin_khz
) * (denum
+ 1);
170 ulong new_rate_khz
= (clkin_khz
171 * ((num
+ 1) / (denum
+ 1)))
174 /* Keep the rate below requested one. */
175 if (new_rate_khz
> rate_khz
)
178 if (new_rate_khz
- rate_khz
< delta
) {
179 delta
= new_rate_khz
- rate_khz
;
192 mpll_reg
= readl(&scu
->m_pll_param
);
193 mpll_reg
&= ~((SCU_MPLL_POST_MASK
<< SCU_MPLL_POST_SHIFT
)
194 | (SCU_MPLL_NUM_MASK
<< SCU_MPLL_NUM_SHIFT
)
195 | (SCU_MPLL_DENUM_MASK
<< SCU_MPLL_DENUM_SHIFT
));
196 mpll_reg
|= (best_post
<< SCU_MPLL_POST_SHIFT
)
197 | (best_num
<< SCU_MPLL_NUM_SHIFT
)
198 | (best_denum
<< SCU_MPLL_DENUM_SHIFT
);
200 ast2500_scu_unlock(scu
);
201 writel(mpll_reg
, &scu
->m_pll_param
);
202 ast2500_scu_lock(scu
);
204 return ast2500_get_mpll_rate(clkin
, mpll_reg
);
207 static ulong
ast2500_clk_set_rate(struct clk
*clk
, ulong rate
)
209 struct ast2500_clk_priv
*priv
= dev_get_priv(clk
->dev
);
215 new_rate
= ast2500_configure_ddr(priv
->scu
, rate
);
224 struct clk_ops ast2500_clk_ops
= {
225 .get_rate
= ast2500_clk_get_rate
,
226 .set_rate
= ast2500_clk_set_rate
,
229 static int ast2500_clk_probe(struct udevice
*dev
)
231 struct ast2500_clk_priv
*priv
= dev_get_priv(dev
);
233 priv
->scu
= dev_get_addr_ptr(dev
);
234 if (IS_ERR(priv
->scu
))
235 return PTR_ERR(priv
->scu
);
240 static int ast2500_clk_bind(struct udevice
*dev
)
244 /* The reset driver does not have a device node, so bind it here */
245 ret
= device_bind_driver(gd
->dm_root
, "ast_sysreset", "reset", &dev
);
247 debug("Warning: No reset driver: ret=%d\n", ret
);
252 static const struct udevice_id ast2500_clk_ids
[] = {
253 { .compatible
= "aspeed,ast2500-scu" },
257 U_BOOT_DRIVER(aspeed_ast2500_scu
) = {
258 .name
= "aspeed_ast2500_scu",
260 .of_match
= ast2500_clk_ids
,
261 .priv_auto_alloc_size
= sizeof(struct ast2500_clk_priv
),
262 .ops
= &ast2500_clk_ops
,
263 .bind
= ast2500_clk_bind
,
264 .probe
= ast2500_clk_probe
,