]> git.sur5r.net Git - u-boot/blob - arch/arm/cpu/armv7/mx5/clock.c
imx-common: Factor out get_ahb_clk()
[u-boot] / arch / arm / cpu / armv7 / mx5 / clock.c
1 /*
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>
31 #include <asm/arch/clock.h>
32 #include <div64.h>
33 #include <asm/arch/sys_proto.h>
34
35 enum pll_clocks {
36         PLL1_CLOCK = 0,
37         PLL2_CLOCK,
38         PLL3_CLOCK,
39         PLL4_CLOCK,
40         PLL_CLOCKS,
41 };
42
43 struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = {
44         [PLL1_CLOCK] = (struct mxc_pll_reg *)PLL1_BASE_ADDR,
45         [PLL2_CLOCK] = (struct mxc_pll_reg *)PLL2_BASE_ADDR,
46         [PLL3_CLOCK] = (struct mxc_pll_reg *)PLL3_BASE_ADDR,
47 #ifdef  CONFIG_MX53
48         [PLL4_CLOCK] = (struct mxc_pll_reg *)PLL4_BASE_ADDR,
49 #endif
50 };
51
52 struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;
53
54 void set_usboh3_clk(void)
55 {
56         unsigned int reg;
57
58         reg = readl(&mxc_ccm->cscmr1) &
59                  ~MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK;
60         reg |= 1 << MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET;
61         writel(reg, &mxc_ccm->cscmr1);
62
63         reg = readl(&mxc_ccm->cscdr1);
64         reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK;
65         reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK;
66         reg |= 4 << MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET;
67         reg |= 1 << MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET;
68
69         writel(reg, &mxc_ccm->cscdr1);
70 }
71
72 void enable_usboh3_clk(unsigned char enable)
73 {
74         unsigned int reg;
75
76         reg = readl(&mxc_ccm->CCGR2);
77         if (enable)
78                 reg |= 1 << MXC_CCM_CCGR2_CG14_OFFSET;
79         else
80                 reg &= ~(1 << MXC_CCM_CCGR2_CG14_OFFSET);
81         writel(reg, &mxc_ccm->CCGR2);
82 }
83
84 void set_usb_phy1_clk(void)
85 {
86         unsigned int reg;
87
88         reg = readl(&mxc_ccm->cscmr1);
89         reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
90         writel(reg, &mxc_ccm->cscmr1);
91 }
92
93 void enable_usb_phy1_clk(unsigned char enable)
94 {
95         unsigned int reg;
96
97         reg = readl(&mxc_ccm->CCGR4);
98         if (enable)
99                 reg |= 1 << MXC_CCM_CCGR4_CG5_OFFSET;
100         else
101                 reg &= ~(1 << MXC_CCM_CCGR4_CG5_OFFSET);
102         writel(reg, &mxc_ccm->CCGR4);
103 }
104
105 void set_usb_phy2_clk(void)
106 {
107         unsigned int reg;
108
109         reg = readl(&mxc_ccm->cscmr1);
110         reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
111         writel(reg, &mxc_ccm->cscmr1);
112 }
113
114 void enable_usb_phy2_clk(unsigned char enable)
115 {
116         unsigned int reg;
117
118         reg = readl(&mxc_ccm->CCGR4);
119         if (enable)
120                 reg |= 1 << MXC_CCM_CCGR4_CG6_OFFSET;
121         else
122                 reg &= ~(1 << MXC_CCM_CCGR4_CG6_OFFSET);
123         writel(reg, &mxc_ccm->CCGR4);
124 }
125
126 /*
127  * Calculate the frequency of PLLn.
128  */
129 static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq)
130 {
131         uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret;
132         uint64_t refclk, temp;
133         int32_t mfn_abs;
134
135         ctrl = readl(&pll->ctrl);
136
137         if (ctrl & MXC_DPLLC_CTL_HFSM) {
138                 mfn = __raw_readl(&pll->hfs_mfn);
139                 mfd = __raw_readl(&pll->hfs_mfd);
140                 op = __raw_readl(&pll->hfs_op);
141         } else {
142                 mfn = __raw_readl(&pll->mfn);
143                 mfd = __raw_readl(&pll->mfd);
144                 op = __raw_readl(&pll->op);
145         }
146
147         mfd &= MXC_DPLLC_MFD_MFD_MASK;
148         mfn &= MXC_DPLLC_MFN_MFN_MASK;
149         pdf = op & MXC_DPLLC_OP_PDF_MASK;
150         mfi = (op & MXC_DPLLC_OP_MFI_MASK) >> MXC_DPLLC_OP_MFI_OFFSET;
151
152         /* 21.2.3 */
153         if (mfi < 5)
154                 mfi = 5;
155
156         /* Sign extend */
157         if (mfn >= 0x04000000) {
158                 mfn |= 0xfc000000;
159                 mfn_abs = -mfn;
160         } else
161                 mfn_abs = mfn;
162
163         refclk = infreq * 2;
164         if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN)
165                 refclk *= 2;
166
167         do_div(refclk, pdf + 1);
168         temp = refclk * mfn_abs;
169         do_div(temp, mfd + 1);
170         ret = refclk * mfi;
171
172         if ((int)mfn < 0)
173                 ret -= temp;
174         else
175                 ret += temp;
176
177         return ret;
178 }
179
180 /*
181  * Get mcu main rate
182  */
183 u32 get_mcu_main_clk(void)
184 {
185         u32 reg, freq;
186
187         reg = (__raw_readl(&mxc_ccm->cacrr) & MXC_CCM_CACRR_ARM_PODF_MASK) >>
188                 MXC_CCM_CACRR_ARM_PODF_OFFSET;
189         freq = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
190         return freq / (reg + 1);
191 }
192
193 /*
194  * Get the rate of peripheral's root clock.
195  */
196 u32 get_periph_clk(void)
197 {
198         u32 reg;
199
200         reg = __raw_readl(&mxc_ccm->cbcdr);
201         if (!(reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL))
202                 return decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
203         reg = __raw_readl(&mxc_ccm->cbcmr);
204         switch ((reg & MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK) >>
205                 MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET) {
206         case 0:
207                 return decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
208         case 1:
209                 return decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK);
210         default:
211                 return 0;
212         }
213         /* NOTREACHED */
214 }
215
216 /*
217  * Get the rate of ipg clock.
218  */
219 static u32 get_ipg_clk(void)
220 {
221         uint32_t freq, reg, div;
222
223         freq = get_ahb_clk();
224
225         reg = __raw_readl(&mxc_ccm->cbcdr);
226         div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >>
227                         MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1;
228
229         return freq / div;
230 }
231
232 /*
233  * Get the rate of ipg_per clock.
234  */
235 static u32 get_ipg_per_clk(void)
236 {
237         u32 pred1, pred2, podf;
238
239         if (__raw_readl(&mxc_ccm->cbcmr) & MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL)
240                 return get_ipg_clk();
241         /* Fixme: not handle what about lpm*/
242         podf = __raw_readl(&mxc_ccm->cbcdr);
243         pred1 = (podf & MXC_CCM_CBCDR_PERCLK_PRED1_MASK) >>
244                 MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET;
245         pred2 = (podf & MXC_CCM_CBCDR_PERCLK_PRED2_MASK) >>
246                 MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET;
247         podf = (podf & MXC_CCM_CBCDR_PERCLK_PODF_MASK) >>
248                 MXC_CCM_CBCDR_PERCLK_PODF_OFFSET;
249
250         return get_periph_clk() / ((pred1 + 1) * (pred2 + 1) * (podf + 1));
251 }
252
253 /*
254  * Get the rate of uart clk.
255  */
256 static u32 get_uart_clk(void)
257 {
258         unsigned int freq, reg, pred, podf;
259
260         reg = __raw_readl(&mxc_ccm->cscmr1);
261         switch ((reg & MXC_CCM_CSCMR1_UART_CLK_SEL_MASK) >>
262                 MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET) {
263         case 0x0:
264                 freq = decode_pll(mxc_plls[PLL1_CLOCK],
265                                     CONFIG_SYS_MX5_HCLK);
266                 break;
267         case 0x1:
268                 freq = decode_pll(mxc_plls[PLL2_CLOCK],
269                                     CONFIG_SYS_MX5_HCLK);
270                 break;
271         case 0x2:
272                 freq = decode_pll(mxc_plls[PLL3_CLOCK],
273                                     CONFIG_SYS_MX5_HCLK);
274                 break;
275         default:
276                 return 66500000;
277         }
278
279         reg = __raw_readl(&mxc_ccm->cscdr1);
280
281         pred = (reg & MXC_CCM_CSCDR1_UART_CLK_PRED_MASK) >>
282                 MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET;
283
284         podf = (reg & MXC_CCM_CSCDR1_UART_CLK_PODF_MASK) >>
285                 MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET;
286         freq /= (pred + 1) * (podf + 1);
287
288         return freq;
289 }
290
291 /*
292  * This function returns the low power audio clock.
293  */
294 u32 get_lp_apm(void)
295 {
296         u32 ret_val = 0;
297         u32 ccsr = __raw_readl(&mxc_ccm->ccsr);
298
299         if (((ccsr >> 9) & 1) == 0)
300                 ret_val = CONFIG_SYS_MX5_HCLK;
301         else
302                 ret_val = ((32768 * 1024));
303
304         return ret_val;
305 }
306
307 /*
308  * get cspi clock rate.
309  */
310 u32 imx_get_cspiclk(void)
311 {
312         u32 ret_val = 0, pdf, pre_pdf, clk_sel;
313         u32 cscmr1 = __raw_readl(&mxc_ccm->cscmr1);
314         u32 cscdr2 = __raw_readl(&mxc_ccm->cscdr2);
315
316         pre_pdf = (cscdr2 & MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK) \
317                         >> MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET;
318         pdf = (cscdr2 & MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK) \
319                         >> MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET;
320         clk_sel = (cscmr1 & MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK) \
321                         >> MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET;
322
323         switch (clk_sel) {
324         case 0:
325                 ret_val = decode_pll(mxc_plls[PLL1_CLOCK],
326                                         CONFIG_SYS_MX5_HCLK) /
327                                         ((pre_pdf + 1) * (pdf + 1));
328                 break;
329         case 1:
330                 ret_val = decode_pll(mxc_plls[PLL2_CLOCK],
331                                         CONFIG_SYS_MX5_HCLK) /
332                                         ((pre_pdf + 1) * (pdf + 1));
333                 break;
334         case 2:
335                 ret_val = decode_pll(mxc_plls[PLL3_CLOCK],
336                                         CONFIG_SYS_MX5_HCLK) /
337                                         ((pre_pdf + 1) * (pdf + 1));
338                 break;
339         default:
340                 ret_val = get_lp_apm() / ((pre_pdf + 1) * (pdf + 1));
341                 break;
342         }
343
344         return ret_val;
345 }
346
347 /*
348  * The API of get mxc clockes.
349  */
350 unsigned int mxc_get_clock(enum mxc_clock clk)
351 {
352         switch (clk) {
353         case MXC_ARM_CLK:
354                 return get_mcu_main_clk();
355         case MXC_AHB_CLK:
356                 return get_ahb_clk();
357         case MXC_IPG_CLK:
358                 return get_ipg_clk();
359         case MXC_IPG_PERCLK:
360                 return get_ipg_per_clk();
361         case MXC_UART_CLK:
362                 return get_uart_clk();
363         case MXC_CSPI_CLK:
364                 return imx_get_cspiclk();
365         case MXC_FEC_CLK:
366                 return decode_pll(mxc_plls[PLL1_CLOCK],
367                                     CONFIG_SYS_MX5_HCLK);
368         case MXC_SATA_CLK:
369                 return get_ahb_clk();
370         default:
371                 break;
372         }
373         return -1;
374 }
375
376 u32 imx_get_uartclk(void)
377 {
378         return get_uart_clk();
379 }
380
381
382 u32 imx_get_fecclk(void)
383 {
384         return mxc_get_clock(MXC_IPG_CLK);
385 }
386
387 #ifdef CONFIG_MX53
388 /*
389  * The clock for the external interface can be set to use internal clock
390  * if fuse bank 4, row 3, bit 2 is set.
391  * This is an undocumented feature and it was confirmed by Freescale's support:
392  * Fuses (but not pins) may be used to configure SATA clocks.
393  * Particularly the i.MX53 Fuse_Map contains the next information
394  * about configuring SATA clocks :  SATA_ALT_REF_CLK[1:0] (offset 0x180C)
395  * '00' - 100MHz (External)
396  * '01' - 50MHz (External)
397  * '10' - 120MHz, internal (USB PHY)
398  * '11' - Reserved
399 */
400 void mxc_set_sata_internal_clock(void)
401 {
402         u32 *tmp_base =
403                 (u32 *)(IIM_BASE_ADDR + 0x180c);
404
405         set_usb_phy1_clk();
406
407         writel((readl(tmp_base) & (~0x7)) | 0x4, tmp_base);
408 }
409 #endif
410
411 /*
412  * Dump some core clockes.
413  */
414 int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
415 {
416         u32 freq;
417
418         freq = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
419         printf("PLL1       %8d MHz\n", freq / 1000000);
420         freq = decode_pll(mxc_plls[PLL2_CLOCK], CONFIG_SYS_MX5_HCLK);
421         printf("PLL2       %8d MHz\n", freq / 1000000);
422         freq = decode_pll(mxc_plls[PLL3_CLOCK], CONFIG_SYS_MX5_HCLK);
423         printf("PLL3       %8d MHz\n", freq / 1000000);
424 #ifdef  CONFIG_MX53
425         freq = decode_pll(mxc_plls[PLL4_CLOCK], CONFIG_SYS_MX5_HCLK);
426         printf("PLL4       %8d MHz\n", freq / 1000000);
427 #endif
428
429         printf("\n");
430         printf("AHB        %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000);
431         printf("IPG        %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000);
432         printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000);
433
434         return 0;
435 }
436
437 /***************************************************/
438
439 U_BOOT_CMD(
440         clocks, CONFIG_SYS_MAXARGS, 1, do_mx5_showclocks,
441         "display clocks",
442         ""
443 );