]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/arm/cpu/armv7/mx6/clock.c
Merge branch 'staging'
[karo-tx-uboot.git] / arch / arm / cpu / armv7 / mx6 / clock.c
1 /*
2  * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307 USA
21  */
22
23 #include <common.h>
24 #include <asm/io.h>
25 #include <asm/errno.h>
26 #include <asm/arch/imx-regs.h>
27 #include <asm/arch/ccm_regs.h>
28 #include <asm/arch/clock.h>
29
30 enum pll_clocks {
31         PLL_SYS,        /* System PLL */
32         PLL_BUS,        /* System Bus PLL*/
33         PLL_USBOTG,     /* OTG USB PLL */
34         PLL_ENET,       /* ENET PLL */
35 };
36
37 struct imx_ccm_reg *imx_ccm = (struct imx_ccm_reg *)CCM_BASE_ADDR;
38
39 static u32 decode_pll(enum pll_clocks pll, u32 infreq)
40 {
41         u32 div;
42
43         switch (pll) {
44         case PLL_SYS:
45                 div = __raw_readl(&imx_ccm->analog_pll_sys);
46                 div &= BM_ANADIG_PLL_SYS_DIV_SELECT;
47
48                 return infreq * (div >> 1);
49         case PLL_BUS:
50                 div = __raw_readl(&imx_ccm->analog_pll_528);
51                 div &= BM_ANADIG_PLL_528_DIV_SELECT;
52
53                 return infreq * (20 + (div << 1));
54         case PLL_USBOTG:
55                 div = __raw_readl(&imx_ccm->analog_usb1_pll_480_ctrl);
56                 div &= BM_ANADIG_USB1_PLL_480_CTRL_DIV_SELECT;
57
58                 return infreq * (20 + (div << 1));
59         case PLL_ENET:
60                 div = __raw_readl(&imx_ccm->analog_pll_enet);
61                 div &= BM_ANADIG_PLL_ENET_DIV_SELECT;
62
63                 return (div == 3 ? 125000000 : 25000000 * (div << 1));
64         default:
65                 return 0;
66         }
67         /* NOTREACHED */
68 }
69
70 static u32 get_mcu_main_clk(void)
71 {
72         u32 reg, freq;
73
74         reg = __raw_readl(&imx_ccm->cacrr);
75         reg &= MXC_CCM_CACRR_ARM_PODF_MASK;
76         reg >>= MXC_CCM_CACRR_ARM_PODF_OFFSET;
77         freq = decode_pll(PLL_SYS, CONFIG_SYS_MX6_HCLK);
78
79         return freq / (reg + 1);
80 }
81
82 static u32 get_periph_clk(void)
83 {
84         u32 reg, freq = 0;
85
86         reg = __raw_readl(&imx_ccm->cbcdr);
87         if (reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL) {
88                 reg = __raw_readl(&imx_ccm->cbcmr);
89                 reg &= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_MASK;
90                 reg >>= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_OFFSET;
91
92                 switch (reg) {
93                 case 0:
94                         freq = decode_pll(PLL_USBOTG, CONFIG_SYS_MX6_HCLK);
95                         break;
96                 case 1:
97                 case 2:
98                         freq = CONFIG_SYS_MX6_HCLK;
99                         break;
100                 default:
101                         break;
102                 }
103         } else {
104                 reg = __raw_readl(&imx_ccm->cbcmr);
105                 reg &= MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK;
106                 reg >>= MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET;
107
108                 switch (reg) {
109                 case 0:
110                         freq = decode_pll(PLL_BUS, CONFIG_SYS_MX6_HCLK);
111                         break;
112                 case 1:
113                         freq = PLL2_PFD2_FREQ;
114                         break;
115                 case 2:
116                         freq = PLL2_PFD0_FREQ;
117                         break;
118                 case 3:
119                         freq = PLL2_PFD2_DIV_FREQ;
120                         break;
121                 default:
122                         break;
123                 }
124         }
125
126         return freq;
127 }
128
129
130 static u32 get_ahb_clk(void)
131 {
132         u32 reg, ahb_podf;
133
134         reg = __raw_readl(&imx_ccm->cbcdr);
135         reg &= MXC_CCM_CBCDR_AHB_PODF_MASK;
136         ahb_podf = reg >> MXC_CCM_CBCDR_AHB_PODF_OFFSET;
137
138         return get_periph_clk() / (ahb_podf + 1);
139 }
140
141 static u32 get_ipg_clk(void)
142 {
143         u32 reg, ipg_podf;
144
145         reg = __raw_readl(&imx_ccm->cbcdr);
146         reg &= MXC_CCM_CBCDR_IPG_PODF_MASK;
147         ipg_podf = reg >> MXC_CCM_CBCDR_IPG_PODF_OFFSET;
148
149         return get_ahb_clk() / (ipg_podf + 1);
150 }
151
152 static u32 get_ipg_per_clk(void)
153 {
154         u32 reg, perclk_podf;
155
156         reg = __raw_readl(&imx_ccm->cscmr1);
157         perclk_podf = reg & MXC_CCM_CSCMR1_PERCLK_PODF_MASK;
158
159         return get_ipg_clk() / (perclk_podf + 1);
160 }
161
162 static u32 get_uart_clk(void)
163 {
164         u32 reg, uart_podf;
165
166         reg = __raw_readl(&imx_ccm->cscdr1);
167         reg &= MXC_CCM_CSCDR1_UART_CLK_PODF_MASK;
168         uart_podf = reg >> MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET;
169
170         return PLL3_80M / (uart_podf + 1);
171 }
172
173 static u32 get_cspi_clk(void)
174 {
175         u32 reg, cspi_podf;
176
177         reg = __raw_readl(&imx_ccm->cscdr2);
178         reg &= MXC_CCM_CSCDR2_ECSPI_CLK_PODF_MASK;
179         cspi_podf = reg >> MXC_CCM_CSCDR2_ECSPI_CLK_PODF_OFFSET;
180
181         return  PLL3_60M / (cspi_podf + 1);
182 }
183
184 static u32 get_axi_clk(void)
185 {
186         u32 root_freq, axi_podf;
187         u32 cbcdr =  __raw_readl(&imx_ccm->cbcdr);
188
189         axi_podf = cbcdr & MXC_CCM_CBCDR_AXI_PODF_MASK;
190         axi_podf >>= MXC_CCM_CBCDR_AXI_PODF_OFFSET;
191
192         if (cbcdr & MXC_CCM_CBCDR_AXI_SEL) {
193                 if (cbcdr & MXC_CCM_CBCDR_AXI_ALT_SEL)
194                         root_freq = PLL2_PFD2_FREQ;
195                 else
196                         root_freq = PLL3_PFD1_FREQ;
197         } else
198                 root_freq = get_periph_clk();
199
200         return  root_freq / (axi_podf + 1);
201 }
202
203 static u32 get_emi_slow_clk(void)
204 {
205         u32 emi_clk_sel, emi_slow_pof, cscmr1, root_freq = 0;
206
207         cscmr1 =  __raw_readl(&imx_ccm->cscmr1);
208         emi_clk_sel = cscmr1 & MXC_CCM_CSCMR1_ACLK_EMI_SLOW_MASK;
209         emi_clk_sel >>= MXC_CCM_CSCMR1_ACLK_EMI_SLOW_OFFSET;
210         emi_slow_pof = cscmr1 & MXC_CCM_CSCMR1_ACLK_EMI_SLOW_PODF_MASK;
211         emi_slow_pof >>= MXC_CCM_CSCMR1_ACLK_EMI_PODF_OFFSET;
212
213         switch (emi_clk_sel) {
214         case 0:
215                 root_freq = get_axi_clk();
216                 break;
217         case 1:
218                 root_freq = decode_pll(PLL_USBOTG, CONFIG_SYS_MX6_HCLK);
219                 break;
220         case 2:
221                 root_freq = PLL2_PFD2_FREQ;
222                 break;
223         case 3:
224                 root_freq = PLL2_PFD0_FREQ;
225                 break;
226         }
227
228         return root_freq / (emi_slow_pof + 1);
229 }
230
231 static u32 get_mmdc_ch0_clk(void)
232 {
233         u32 cbcdr = __raw_readl(&imx_ccm->cbcdr);
234         u32 mmdc_ch0_podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK) >>
235                                 MXC_CCM_CBCDR_MMDC_CH0_PODF_OFFSET;
236
237         return get_periph_clk() / (mmdc_ch0_podf + 1);
238 }
239
240 static u32 get_usdhc_clk(u32 port)
241 {
242         u32 root_freq = 0, usdhc_podf = 0, clk_sel = 0;
243         u32 cscmr1 = __raw_readl(&imx_ccm->cscmr1);
244         u32 cscdr1 = __raw_readl(&imx_ccm->cscdr1);
245
246         switch (port) {
247         case 0:
248                 usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC1_PODF_MASK) >>
249                                         MXC_CCM_CSCDR1_USDHC1_PODF_OFFSET;
250                 clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC1_CLK_SEL;
251
252                 break;
253         case 1:
254                 usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC2_PODF_MASK) >>
255                                         MXC_CCM_CSCDR1_USDHC2_PODF_OFFSET;
256                 clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC2_CLK_SEL;
257
258                 break;
259         case 2:
260                 usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC3_PODF_MASK) >>
261                                         MXC_CCM_CSCDR1_USDHC3_PODF_OFFSET;
262                 clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC3_CLK_SEL;
263
264                 break;
265         case 3:
266                 usdhc_podf = (cscdr1 & MXC_CCM_CSCDR1_USDHC4_PODF_MASK) >>
267                                         MXC_CCM_CSCDR1_USDHC4_PODF_OFFSET;
268                 clk_sel = cscmr1 & MXC_CCM_CSCMR1_USDHC4_CLK_SEL;
269
270                 break;
271         default:
272                 break;
273         }
274
275         if (clk_sel)
276                 root_freq = PLL2_PFD0_FREQ;
277         else
278                 root_freq = PLL2_PFD2_FREQ;
279
280         return root_freq / (usdhc_podf + 1);
281 }
282
283 u32 imx_get_uartclk(void)
284 {
285         return get_uart_clk();
286 }
287
288 u32 imx_get_fecclk(void)
289 {
290         return decode_pll(PLL_ENET, CONFIG_SYS_MX6_HCLK);
291 }
292
293 unsigned int mxc_get_clock(enum mxc_clock clk)
294 {
295         switch (clk) {
296         case MXC_ARM_CLK:
297                 return get_mcu_main_clk();
298         case MXC_PER_CLK:
299                 return get_periph_clk();
300         case MXC_AHB_CLK:
301                 return get_ahb_clk();
302         case MXC_IPG_CLK:
303                 return get_ipg_clk();
304         case MXC_IPG_PERCLK:
305                 return get_ipg_per_clk();
306         case MXC_UART_CLK:
307                 return get_uart_clk();
308         case MXC_CSPI_CLK:
309                 return get_cspi_clk();
310         case MXC_AXI_CLK:
311                 return get_axi_clk();
312         case MXC_EMI_SLOW_CLK:
313                 return get_emi_slow_clk();
314         case MXC_DDR_CLK:
315                 return get_mmdc_ch0_clk();
316         case MXC_ESDHC_CLK:
317                 return get_usdhc_clk(0);
318         case MXC_ESDHC2_CLK:
319                 return get_usdhc_clk(1);
320         case MXC_ESDHC3_CLK:
321                 return get_usdhc_clk(2);
322         case MXC_ESDHC4_CLK:
323                 return get_usdhc_clk(3);
324         case MXC_SATA_CLK:
325                 return get_ahb_clk();
326         default:
327                 break;
328         }
329
330         return -1;
331 }
332
333 /*
334  * Dump some core clockes.
335  */
336 int do_mx6_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
337 {
338         u32 freq;
339         freq = decode_pll(PLL_SYS, CONFIG_SYS_MX6_HCLK);
340         printf("PLL_SYS    %8d MHz\n", freq / 1000000);
341         freq = decode_pll(PLL_BUS, CONFIG_SYS_MX6_HCLK);
342         printf("PLL_BUS    %8d MHz\n", freq / 1000000);
343         freq = decode_pll(PLL_USBOTG, CONFIG_SYS_MX6_HCLK);
344         printf("PLL_OTG    %8d MHz\n", freq / 1000000);
345         freq = decode_pll(PLL_ENET, CONFIG_SYS_MX6_HCLK);
346         printf("PLL_NET    %8d MHz\n", freq / 1000000);
347
348         printf("\n");
349         printf("IPG        %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000);
350         printf("UART       %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000);
351         printf("CSPI       %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000);
352         printf("AHB        %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000);
353         printf("AXI        %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000);
354         printf("DDR        %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000);
355         printf("USDHC1     %8d kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000);
356         printf("USDHC2     %8d kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000);
357         printf("USDHC3     %8d kHz\n", mxc_get_clock(MXC_ESDHC3_CLK) / 1000);
358         printf("USDHC4     %8d kHz\n", mxc_get_clock(MXC_ESDHC4_CLK) / 1000);
359         printf("EMI SLOW   %8d kHz\n", mxc_get_clock(MXC_EMI_SLOW_CLK) / 1000);
360         printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000);
361
362         return 0;
363 }
364
365 /***************************************************/
366
367 U_BOOT_CMD(
368         clocks, CONFIG_SYS_MAXARGS, 1, do_mx6_showclocks,
369         "display clocks",
370         ""
371 );