blob: e92f10623a92bb6e72c2d4c3e947f58dffcce78b [file] [log] [blame]
Stefano Babica521a772010-01-20 18:19:32 +01001/*
2 * (C) Copyright 2007
3 * Sascha Hauer, Pengutronix
4 *
5 * (C) Copyright 2009 Freescale Semiconductor, Inc.
6 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25
26#include <common.h>
27#include <asm/io.h>
28#include <asm/errno.h>
29#include <asm/arch/imx-regs.h>
30#include <asm/arch/crm_regs.h>
Stefano Babicac41d4d2010-03-05 17:54:37 +010031#include <asm/arch/clock.h>
Marek Vasut3c844f32011-09-23 11:43:47 +020032#include <div64.h>
Stefano Babica521a772010-01-20 18:19:32 +010033
34enum pll_clocks {
35 PLL1_CLOCK = 0,
36 PLL2_CLOCK,
37 PLL3_CLOCK,
Marek Vasut3c844f32011-09-23 11:43:47 +020038 PLL4_CLOCK,
Stefano Babica521a772010-01-20 18:19:32 +010039 PLL_CLOCKS,
40};
41
42struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = {
43 [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR,
44 [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR,
45 [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR,
Marek Vasut3c844f32011-09-23 11:43:47 +020046#ifdef CONFIG_MX53
47 [PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR,
48#endif
Stefano Babica521a772010-01-20 18:19:32 +010049};
50
Stefano Babicac41d4d2010-03-05 17:54:37 +010051struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;
Stefano Babica521a772010-01-20 18:19:32 +010052
Wolfgang Grandegger8c1e4d32011-11-11 14:03:34 +010053void set_usboh3_clk(void)
54{
55 unsigned int reg;
56
57 reg = readl(&mxc_ccm->cscmr1) &
58 ~MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK;
59 reg |= 1 << MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET;
60 writel(reg, &mxc_ccm->cscmr1);
61
62 reg = readl(&mxc_ccm->cscdr1);
63 reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK;
64 reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK;
65 reg |= 4 << MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET;
66 reg |= 1 << MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET;
67
68 writel(reg, &mxc_ccm->cscdr1);
69}
70
71void enable_usboh3_clk(unsigned char enable)
72{
73 unsigned int reg;
74
75 reg = readl(&mxc_ccm->CCGR2);
76 if (enable)
77 reg |= 1 << MXC_CCM_CCGR2_CG14_OFFSET;
78 else
79 reg &= ~(1 << MXC_CCM_CCGR2_CG14_OFFSET);
80 writel(reg, &mxc_ccm->CCGR2);
81}
82
83void set_usb_phy1_clk(void)
84{
85 unsigned int reg;
86
87 reg = readl(&mxc_ccm->cscmr1);
88 reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
89 writel(reg, &mxc_ccm->cscmr1);
90}
91
92void enable_usb_phy1_clk(unsigned char enable)
93{
94 unsigned int reg;
95
96 reg = readl(&mxc_ccm->CCGR4);
97 if (enable)
98 reg |= 1 << MXC_CCM_CCGR4_CG5_OFFSET;
99 else
100 reg &= ~(1 << MXC_CCM_CCGR4_CG5_OFFSET);
101 writel(reg, &mxc_ccm->CCGR4);
102}
103
104void set_usb_phy2_clk(void)
105{
106 unsigned int reg;
107
108 reg = readl(&mxc_ccm->cscmr1);
109 reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
110 writel(reg, &mxc_ccm->cscmr1);
111}
112
113void enable_usb_phy2_clk(unsigned char enable)
114{
115 unsigned int reg;
116
117 reg = readl(&mxc_ccm->CCGR4);
118 if (enable)
119 reg |= 1 << MXC_CCM_CCGR4_CG6_OFFSET;
120 else
121 reg &= ~(1 << MXC_CCM_CCGR4_CG6_OFFSET);
122 writel(reg, &mxc_ccm->CCGR4);
123}
124
Stefano Babica521a772010-01-20 18:19:32 +0100125/*
Marek Vasut3c844f32011-09-23 11:43:47 +0200126 * Calculate the frequency of PLLn.
Stefano Babica521a772010-01-20 18:19:32 +0100127 */
Marek Vasut3c844f32011-09-23 11:43:47 +0200128static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq)
Stefano Babica521a772010-01-20 18:19:32 +0100129{
Marek Vasut3c844f32011-09-23 11:43:47 +0200130 uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret;
131 uint64_t refclk, temp;
132 int32_t mfn_abs;
Stefano Babica521a772010-01-20 18:19:32 +0100133
Marek Vasut3c844f32011-09-23 11:43:47 +0200134 ctrl = readl(&pll->ctrl);
Stefano Babica521a772010-01-20 18:19:32 +0100135
Marek Vasut3c844f32011-09-23 11:43:47 +0200136 if (ctrl & MXC_DPLLC_CTL_HFSM) {
137 mfn = __raw_readl(&pll->hfs_mfn);
138 mfd = __raw_readl(&pll->hfs_mfd);
139 op = __raw_readl(&pll->hfs_op);
140 } else {
141 mfn = __raw_readl(&pll->mfn);
142 mfd = __raw_readl(&pll->mfd);
143 op = __raw_readl(&pll->op);
144 }
145
146 mfd &= MXC_DPLLC_MFD_MFD_MASK;
147 mfn &= MXC_DPLLC_MFN_MFN_MASK;
148 pdf = op & MXC_DPLLC_OP_PDF_MASK;
149 mfi = (op & MXC_DPLLC_OP_MFI_MASK) >> MXC_DPLLC_OP_MFI_OFFSET;
150
151 /* 21.2.3 */
152 if (mfi < 5)
153 mfi = 5;
154
155 /* Sign extend */
156 if (mfn >= 0x04000000) {
157 mfn |= 0xfc000000;
158 mfn_abs = -mfn;
159 } else
160 mfn_abs = mfn;
161
162 refclk = infreq * 2;
163 if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN)
164 refclk *= 2;
165
Simon Glass3d557882011-11-05 04:25:22 +0000166 do_div(refclk, pdf + 1);
Marek Vasut3c844f32011-09-23 11:43:47 +0200167 temp = refclk * mfn_abs;
168 do_div(temp, mfd + 1);
169 ret = refclk * mfi;
170
171 if ((int)mfn < 0)
172 ret -= temp;
173 else
174 ret += temp;
175
176 return ret;
Stefano Babica521a772010-01-20 18:19:32 +0100177}
178
179/*
180 * Get mcu main rate
181 */
182u32 get_mcu_main_clk(void)
183{
184 u32 reg, freq;
185
186 reg = (__raw_readl(&mxc_ccm->cacrr) & MXC_CCM_CACRR_ARM_PODF_MASK) >>
187 MXC_CCM_CACRR_ARM_PODF_OFFSET;
Jason Liue7a7ed22010-10-18 11:09:26 +0800188 freq = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100189 return freq / (reg + 1);
190}
191
192/*
193 * Get the rate of peripheral's root clock.
194 */
195static u32 get_periph_clk(void)
196{
197 u32 reg;
198
199 reg = __raw_readl(&mxc_ccm->cbcdr);
200 if (!(reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL))
Jason Liue7a7ed22010-10-18 11:09:26 +0800201 return decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100202 reg = __raw_readl(&mxc_ccm->cbcmr);
203 switch ((reg & MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK) >>
204 MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET) {
205 case 0:
Jason Liue7a7ed22010-10-18 11:09:26 +0800206 return decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100207 case 1:
Jason Liue7a7ed22010-10-18 11:09:26 +0800208 return decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100209 default:
210 return 0;
211 }
212 /* NOTREACHED */
213}
214
215/*
Marek Vasut6674bf52011-09-22 09:20:37 +0000216 * Get the rate of ahb clock.
217 */
218static u32 get_ahb_clk(void)
219{
220 uint32_t freq, div, reg;
221
222 freq = get_periph_clk();
223
224 reg = __raw_readl(&mxc_ccm->cbcdr);
225 div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
226 MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
227
228 return freq / div;
229}
230
231/*
Stefano Babica521a772010-01-20 18:19:32 +0100232 * Get the rate of ipg clock.
233 */
234static u32 get_ipg_clk(void)
235{
Marek Vasut6674bf52011-09-22 09:20:37 +0000236 uint32_t freq, reg, div;
Stefano Babica521a772010-01-20 18:19:32 +0100237
Marek Vasut6674bf52011-09-22 09:20:37 +0000238 freq = get_ahb_clk();
239
240 reg = __raw_readl(&mxc_ccm->cbcdr);
241 div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >>
242 MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1;
243
244 return freq / div;
Stefano Babica521a772010-01-20 18:19:32 +0100245}
246
247/*
248 * Get the rate of ipg_per clock.
249 */
250static u32 get_ipg_per_clk(void)
251{
252 u32 pred1, pred2, podf;
253
254 if (__raw_readl(&mxc_ccm->cbcmr) & MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL)
255 return get_ipg_clk();
256 /* Fixme: not handle what about lpm*/
257 podf = __raw_readl(&mxc_ccm->cbcdr);
258 pred1 = (podf & MXC_CCM_CBCDR_PERCLK_PRED1_MASK) >>
259 MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET;
260 pred2 = (podf & MXC_CCM_CBCDR_PERCLK_PRED2_MASK) >>
261 MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET;
262 podf = (podf & MXC_CCM_CBCDR_PERCLK_PODF_MASK) >>
263 MXC_CCM_CBCDR_PERCLK_PODF_OFFSET;
264
265 return get_periph_clk() / ((pred1 + 1) * (pred2 + 1) * (podf + 1));
266}
267
268/*
269 * Get the rate of uart clk.
270 */
271static u32 get_uart_clk(void)
272{
273 unsigned int freq, reg, pred, podf;
274
275 reg = __raw_readl(&mxc_ccm->cscmr1);
276 switch ((reg & MXC_CCM_CSCMR1_UART_CLK_SEL_MASK) >>
277 MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET) {
278 case 0x0:
279 freq = decode_pll(mxc_plls[PLL1_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800280 CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100281 break;
282 case 0x1:
283 freq = decode_pll(mxc_plls[PLL2_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800284 CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100285 break;
286 case 0x2:
287 freq = decode_pll(mxc_plls[PLL3_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800288 CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100289 break;
290 default:
291 return 66500000;
292 }
293
294 reg = __raw_readl(&mxc_ccm->cscdr1);
295
296 pred = (reg & MXC_CCM_CSCDR1_UART_CLK_PRED_MASK) >>
297 MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET;
298
299 podf = (reg & MXC_CCM_CSCDR1_UART_CLK_PODF_MASK) >>
300 MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET;
301 freq /= (pred + 1) * (podf + 1);
302
303 return freq;
304}
305
306/*
307 * This function returns the low power audio clock.
308 */
309u32 get_lp_apm(void)
310{
311 u32 ret_val = 0;
312 u32 ccsr = __raw_readl(&mxc_ccm->ccsr);
313
314 if (((ccsr >> 9) & 1) == 0)
Jason Liue7a7ed22010-10-18 11:09:26 +0800315 ret_val = CONFIG_SYS_MX5_HCLK;
Stefano Babica521a772010-01-20 18:19:32 +0100316 else
317 ret_val = ((32768 * 1024));
318
319 return ret_val;
320}
321
322/*
323 * get cspi clock rate.
324 */
325u32 imx_get_cspiclk(void)
326{
327 u32 ret_val = 0, pdf, pre_pdf, clk_sel;
328 u32 cscmr1 = __raw_readl(&mxc_ccm->cscmr1);
329 u32 cscdr2 = __raw_readl(&mxc_ccm->cscdr2);
330
331 pre_pdf = (cscdr2 & MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK) \
332 >> MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET;
333 pdf = (cscdr2 & MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK) \
334 >> MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET;
335 clk_sel = (cscmr1 & MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK) \
336 >> MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET;
337
338 switch (clk_sel) {
339 case 0:
340 ret_val = decode_pll(mxc_plls[PLL1_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800341 CONFIG_SYS_MX5_HCLK) /
Stefano Babica521a772010-01-20 18:19:32 +0100342 ((pre_pdf + 1) * (pdf + 1));
343 break;
344 case 1:
345 ret_val = decode_pll(mxc_plls[PLL2_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800346 CONFIG_SYS_MX5_HCLK) /
Stefano Babica521a772010-01-20 18:19:32 +0100347 ((pre_pdf + 1) * (pdf + 1));
348 break;
349 case 2:
350 ret_val = decode_pll(mxc_plls[PLL3_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800351 CONFIG_SYS_MX5_HCLK) /
Stefano Babica521a772010-01-20 18:19:32 +0100352 ((pre_pdf + 1) * (pdf + 1));
353 break;
354 default:
355 ret_val = get_lp_apm() / ((pre_pdf + 1) * (pdf + 1));
356 break;
357 }
358
359 return ret_val;
360}
361
362/*
363 * The API of get mxc clockes.
364 */
365unsigned int mxc_get_clock(enum mxc_clock clk)
366{
367 switch (clk) {
368 case MXC_ARM_CLK:
369 return get_mcu_main_clk();
370 case MXC_AHB_CLK:
Marek Vasut6674bf52011-09-22 09:20:37 +0000371 return get_ahb_clk();
Stefano Babica521a772010-01-20 18:19:32 +0100372 case MXC_IPG_CLK:
373 return get_ipg_clk();
374 case MXC_IPG_PERCLK:
375 return get_ipg_per_clk();
376 case MXC_UART_CLK:
377 return get_uart_clk();
378 case MXC_CSPI_CLK:
379 return imx_get_cspiclk();
380 case MXC_FEC_CLK:
381 return decode_pll(mxc_plls[PLL1_CLOCK],
Jason Liue7a7ed22010-10-18 11:09:26 +0800382 CONFIG_SYS_MX5_HCLK);
Stefano Babica521a772010-01-20 18:19:32 +0100383 default:
384 break;
385 }
386 return -1;
387}
388
389u32 imx_get_uartclk(void)
390{
391 return get_uart_clk();
392}
393
394
395u32 imx_get_fecclk(void)
396{
397 return mxc_get_clock(MXC_IPG_CLK);
398}
399
400/*
401 * Dump some core clockes.
402 */
Stefano Babic6eb90102010-10-28 11:08:52 +0200403int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
Stefano Babica521a772010-01-20 18:19:32 +0100404{
405 u32 freq;
406
Jason Liue7a7ed22010-10-18 11:09:26 +0800407 freq = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
Marek Vasut421bf452011-09-14 14:09:04 +0000408 printf("PLL1 %8d MHz\n", freq / 1000000);
Jason Liue7a7ed22010-10-18 11:09:26 +0800409 freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
Marek Vasut421bf452011-09-14 14:09:04 +0000410 printf("PLL2 %8d MHz\n", freq / 1000000);
Jason Liue7a7ed22010-10-18 11:09:26 +0800411 freq = decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK);
Marek Vasut421bf452011-09-14 14:09:04 +0000412 printf("PLL3 %8d MHz\n", freq / 1000000);
Marek Vasut3c844f32011-09-23 11:43:47 +0200413#ifdef CONFIG_MX53
414 freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK);
Marek Vasut421bf452011-09-14 14:09:04 +0000415 printf("PLL4 %8d MHz\n", freq / 1000000);
Marek Vasut3c844f32011-09-23 11:43:47 +0200416#endif
Marek Vasut421bf452011-09-14 14:09:04 +0000417
418 printf("\n");
419 printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000);
420 printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000);
421 printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000);
Stefano Babica521a772010-01-20 18:19:32 +0100422
423 return 0;
424}
425
426/***************************************************/
427
428U_BOOT_CMD(
Stefano Babicc8a02c32011-08-17 17:52:40 +0200429 clocks, CONFIG_SYS_MAXARGS, 1, do_mx5_showclocks,
430 "display clocks",
Stefano Babica521a772010-01-20 18:19:32 +0100431 ""
432);