blob: 9e4c66ea85f4039caf2615004e836ecfa01f8b72 [file] [log] [blame]
maxims@google.com2d5a2ad2017-01-18 13:44:56 -08001/*
2 * (C) Copyright 2016 Google, Inc
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7#include <common.h>
8#include <clk-uclass.h>
9#include <dm.h>
10#include <asm/io.h>
11#include <asm/arch/scu_ast2500.h>
12#include <dm/lists.h>
13#include <dt-bindings/clock/ast2500-scu.h>
14
15DECLARE_GLOBAL_DATA_PTR;
16
17/*
18 * For H-PLL and M-PLL the formula is
19 * (Output Frequency) = CLKIN * ((M + 1) / (N + 1)) / (P + 1)
20 * M - Numerator
21 * N - Denumerator
22 * P - Post Divider
23 * They have the same layout in their control register.
24 */
25
26/*
27 * Get the rate of the M-PLL clock from input clock frequency and
28 * the value of the M-PLL Parameter Register.
29 */
30static ulong ast2500_get_mpll_rate(ulong clkin, u32 mpll_reg)
31{
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)
36 & SCU_MPLL_POST_MASK;
37
maxims@google.comd0672172017-01-30 11:35:04 -080038 return (clkin * ((num + 1) / (denum + 1))) / (post_div + 1);
maxims@google.com2d5a2ad2017-01-18 13:44:56 -080039}
40
41/*
42 * Get the rate of the H-PLL clock from input clock frequency and
43 * the value of the H-PLL Parameter Register.
44 */
45static ulong ast2500_get_hpll_rate(ulong clkin, u32 hpll_reg)
46{
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)
51 & SCU_HPLL_POST_MASK;
52
maxims@google.comd0672172017-01-30 11:35:04 -080053 return (clkin * ((num + 1) / (denum + 1))) / (post_div + 1);
maxims@google.com2d5a2ad2017-01-18 13:44:56 -080054}
55
56static ulong ast2500_get_clkin(struct ast2500_scu *scu)
57{
58 return readl(&scu->hwstrap) & SCU_HWSTRAP_CLKIN_25MHZ
59 ? 25 * 1000 * 1000 : 24 * 1000 * 1000;
60}
61
62/**
63 * Get current rate or uart clock
64 *
65 * @scu SCU registers
66 * @uart_index UART index, 1-5
67 *
68 * @return current setting for uart clock rate
69 */
70static ulong ast2500_get_uart_clk_rate(struct ast2500_scu *scu, int uart_index)
71{
72 /*
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.
76 *
77 * This has only been tested with default settings and CLKIN = 24 MHz.
78 */
79 ulong uart_clkin;
80
81 if (readl(&scu->misc_ctrl2) &
82 (1 << (uart_index - 1 + SCU_MISC2_UARTCLK_SHIFT)))
83 uart_clkin = 192 * 1000 * 1000;
84 else
85 uart_clkin = 24 * 1000 * 1000;
86
87 if (readl(&scu->misc_ctrl1) & SCU_MISC_UARTCLK_DIV13)
88 uart_clkin /= 13;
89
90 return uart_clkin;
91}
92
93static ulong ast2500_clk_get_rate(struct clk *clk)
94{
95 struct ast2500_clk_priv *priv = dev_get_priv(clk->dev);
96 ulong clkin = ast2500_get_clkin(priv->scu);
97 ulong rate;
98
99 switch (clk->id) {
100 case PLL_HPLL:
101 case ARMCLK:
102 /*
103 * This ignores dynamic/static slowdown of ARMCLK and may
104 * be inaccurate.
105 */
106 rate = ast2500_get_hpll_rate(clkin,
107 readl(&priv->scu->h_pll_param));
108 break;
109 case MCLK_DDR:
110 rate = ast2500_get_mpll_rate(clkin,
111 readl(&priv->scu->m_pll_param));
112 break;
maxims@google.com995167b2017-04-17 12:00:29 -0700113 case BCLK_PCLK:
114 {
115 ulong apb_div = 4 + 4 * ((readl(&priv->scu->clk_sel1)
116 >> SCU_PCLK_DIV_SHIFT) &
117 SCU_PCLK_DIV_MASK);
118 rate = ast2500_get_hpll_rate(clkin,
119 readl(&priv->scu->
120 h_pll_param));
121 rate = rate / apb_div;
122 }
123 break;
maxims@google.com2d5a2ad2017-01-18 13:44:56 -0800124 case PCLK_UART1:
125 rate = ast2500_get_uart_clk_rate(priv->scu, 1);
126 break;
127 case PCLK_UART2:
128 rate = ast2500_get_uart_clk_rate(priv->scu, 2);
129 break;
130 case PCLK_UART3:
131 rate = ast2500_get_uart_clk_rate(priv->scu, 3);
132 break;
133 case PCLK_UART4:
134 rate = ast2500_get_uart_clk_rate(priv->scu, 4);
135 break;
136 case PCLK_UART5:
137 rate = ast2500_get_uart_clk_rate(priv->scu, 5);
138 break;
139 default:
140 return -ENOENT;
141 }
142
143 return rate;
144}
145
maxims@google.com2d5a2ad2017-01-18 13:44:56 -0800146static ulong ast2500_configure_ddr(struct ast2500_scu *scu, ulong rate)
147{
148 ulong clkin = ast2500_get_clkin(scu);
149 u32 mpll_reg;
150
151 /*
152 * There are not that many combinations of numerator, denumerator
153 * and post divider, so just brute force the best combination.
154 * However, to avoid overflow when multiplying, use kHz.
155 */
156 const ulong clkin_khz = clkin / 1000;
157 const ulong rate_khz = rate / 1000;
158 ulong best_num = 0;
159 ulong best_denum = 0;
160 ulong best_post = 0;
161 ulong delta = rate;
162 ulong num, denum, post;
163
164 for (denum = 0; denum <= SCU_MPLL_DENUM_MASK; ++denum) {
165 for (post = 0; post <= SCU_MPLL_POST_MASK; ++post) {
166 num = (rate_khz * (post + 1) / clkin_khz) * (denum + 1);
167 ulong new_rate_khz = (clkin_khz
168 * ((num + 1) / (denum + 1)))
169 / (post + 1);
170
171 /* Keep the rate below requested one. */
172 if (new_rate_khz > rate_khz)
173 continue;
174
175 if (new_rate_khz - rate_khz < delta) {
176 delta = new_rate_khz - rate_khz;
177
178 best_num = num;
179 best_denum = denum;
180 best_post = post;
181
182 if (delta == 0)
183 goto rate_calc_done;
184 }
185 }
186 }
187
188 rate_calc_done:
189 mpll_reg = readl(&scu->m_pll_param);
190 mpll_reg &= ~((SCU_MPLL_POST_MASK << SCU_MPLL_POST_SHIFT)
191 | (SCU_MPLL_NUM_MASK << SCU_MPLL_NUM_SHIFT)
192 | (SCU_MPLL_DENUM_MASK << SCU_MPLL_DENUM_SHIFT));
193 mpll_reg |= (best_post << SCU_MPLL_POST_SHIFT)
194 | (best_num << SCU_MPLL_NUM_SHIFT)
195 | (best_denum << SCU_MPLL_DENUM_SHIFT);
196
maxims@google.comadea66c2017-04-17 12:00:23 -0700197 ast_scu_unlock(scu);
maxims@google.com2d5a2ad2017-01-18 13:44:56 -0800198 writel(mpll_reg, &scu->m_pll_param);
maxims@google.comadea66c2017-04-17 12:00:23 -0700199 ast_scu_lock(scu);
maxims@google.com2d5a2ad2017-01-18 13:44:56 -0800200
201 return ast2500_get_mpll_rate(clkin, mpll_reg);
202}
203
204static ulong ast2500_clk_set_rate(struct clk *clk, ulong rate)
205{
206 struct ast2500_clk_priv *priv = dev_get_priv(clk->dev);
207
208 ulong new_rate;
209 switch (clk->id) {
210 case PLL_MPLL:
211 case MCLK_DDR:
212 new_rate = ast2500_configure_ddr(priv->scu, rate);
213 break;
214 default:
215 return -ENOENT;
216 }
217
218 return new_rate;
219}
220
221struct clk_ops ast2500_clk_ops = {
222 .get_rate = ast2500_clk_get_rate,
223 .set_rate = ast2500_clk_set_rate,
224};
225
226static int ast2500_clk_probe(struct udevice *dev)
227{
228 struct ast2500_clk_priv *priv = dev_get_priv(dev);
229
230 priv->scu = dev_get_addr_ptr(dev);
231 if (IS_ERR(priv->scu))
232 return PTR_ERR(priv->scu);
233
234 return 0;
235}
236
237static int ast2500_clk_bind(struct udevice *dev)
238{
239 int ret;
240
241 /* The reset driver does not have a device node, so bind it here */
242 ret = device_bind_driver(gd->dm_root, "ast_sysreset", "reset", &dev);
243 if (ret)
244 debug("Warning: No reset driver: ret=%d\n", ret);
245
246 return 0;
247}
248
249static const struct udevice_id ast2500_clk_ids[] = {
250 { .compatible = "aspeed,ast2500-scu" },
251 { }
252};
253
254U_BOOT_DRIVER(aspeed_ast2500_scu) = {
255 .name = "aspeed_ast2500_scu",
256 .id = UCLASS_CLK,
257 .of_match = ast2500_clk_ids,
258 .priv_auto_alloc_size = sizeof(struct ast2500_clk_priv),
259 .ops = &ast2500_clk_ops,
260 .bind = ast2500_clk_bind,
261 .probe = ast2500_clk_probe,
262};