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 + 1);
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 + 1);
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,