2 * Freescale i.MX23/i.MX28 clock setup code
4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
5 * on behalf of DENX Software Engineering GmbH
7 * Based on code from LTIB:
8 * Copyright (C) 2010 Freescale Semiconductor, Inc.
10 * See file CREDITS for list of people who contributed to this
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 #include <asm/errno.h>
32 #include <asm/arch/clock.h>
33 #include <asm/arch/imx-regs.h>
36 * The PLL frequency is 480MHz and XTAL frequency is 24MHz
37 * iMX23: datasheet section 4.2
38 * iMX28: datasheet section 10.2
40 #define PLL_FREQ_KHZ 480000
41 #define PLL_FREQ_COEF 18
42 #define XTAL_FREQ_KHZ 24000
44 #define PLL_FREQ_MHZ (PLL_FREQ_KHZ / 1000)
45 #define XTAL_FREQ_MHZ (XTAL_FREQ_KHZ / 1000)
47 #if defined(CONFIG_MX23)
48 #define MXC_SSPCLK_MAX MXC_SSPCLK0
49 #elif defined(CONFIG_MX28)
50 #define MXC_SSPCLK_MAX MXC_SSPCLK3
53 static uint32_t mxs_get_pclk(void)
55 struct mxs_clkctrl_regs
*clkctrl_regs
=
56 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
58 uint32_t clkctrl
, clkseq
, div
;
59 uint8_t clkfrac
, frac
;
61 clkctrl
= readl(&clkctrl_regs
->hw_clkctrl_cpu
);
63 /* No support of fractional divider calculation */
65 (CLKCTRL_CPU_DIV_XTAL_FRAC_EN
| CLKCTRL_CPU_DIV_CPU_FRAC_EN
)) {
69 clkseq
= readl(&clkctrl_regs
->hw_clkctrl_clkseq
);
72 if (clkseq
& CLKCTRL_CLKSEQ_BYPASS_CPU
) {
73 div
= (clkctrl
& CLKCTRL_CPU_DIV_XTAL_MASK
) >>
74 CLKCTRL_CPU_DIV_XTAL_OFFSET
;
75 return XTAL_FREQ_MHZ
/ div
;
79 clkfrac
= readb(&clkctrl_regs
->hw_clkctrl_frac0
[CLKCTRL_FRAC0_CPU
]);
80 frac
= clkfrac
& CLKCTRL_FRAC_FRAC_MASK
;
81 div
= clkctrl
& CLKCTRL_CPU_DIV_CPU_MASK
;
82 return (PLL_FREQ_MHZ
* PLL_FREQ_COEF
/ frac
) / div
;
85 static uint32_t mxs_get_hclk(void)
87 struct mxs_clkctrl_regs
*clkctrl_regs
=
88 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
93 clkctrl
= readl(&clkctrl_regs
->hw_clkctrl_hbus
);
95 /* No support of fractional divider calculation */
96 if (clkctrl
& CLKCTRL_HBUS_DIV_FRAC_EN
)
99 div
= clkctrl
& CLKCTRL_HBUS_DIV_MASK
;
100 return mxs_get_pclk() / div
;
103 static uint32_t mxs_get_emiclk(void)
105 struct mxs_clkctrl_regs
*clkctrl_regs
=
106 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
108 uint32_t clkctrl
, clkseq
, div
;
109 uint8_t clkfrac
, frac
;
111 clkseq
= readl(&clkctrl_regs
->hw_clkctrl_clkseq
);
112 clkctrl
= readl(&clkctrl_regs
->hw_clkctrl_emi
);
115 if (clkseq
& CLKCTRL_CLKSEQ_BYPASS_EMI
) {
116 div
= (clkctrl
& CLKCTRL_EMI_DIV_XTAL_MASK
) >>
117 CLKCTRL_EMI_DIV_XTAL_OFFSET
;
118 return XTAL_FREQ_MHZ
/ div
;
122 clkfrac
= readb(&clkctrl_regs
->hw_clkctrl_frac0
[CLKCTRL_FRAC0_EMI
]);
123 frac
= clkfrac
& CLKCTRL_FRAC_FRAC_MASK
;
124 div
= clkctrl
& CLKCTRL_EMI_DIV_EMI_MASK
;
125 return (PLL_FREQ_MHZ
* PLL_FREQ_COEF
/ frac
) / div
;
128 static uint32_t mxs_get_gpmiclk(void)
130 struct mxs_clkctrl_regs
*clkctrl_regs
=
131 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
132 #if defined(CONFIG_MX23)
134 &clkctrl_regs
->hw_clkctrl_frac0
[CLKCTRL_FRAC0_CPU
];
135 #elif defined(CONFIG_MX28)
137 &clkctrl_regs
->hw_clkctrl_frac1
[CLKCTRL_FRAC1_GPMI
];
139 uint32_t clkctrl
, clkseq
, div
;
140 uint8_t clkfrac
, frac
;
142 clkseq
= readl(&clkctrl_regs
->hw_clkctrl_clkseq
);
143 clkctrl
= readl(&clkctrl_regs
->hw_clkctrl_gpmi
);
146 if (clkseq
& CLKCTRL_CLKSEQ_BYPASS_GPMI
) {
147 div
= clkctrl
& CLKCTRL_GPMI_DIV_MASK
;
148 return XTAL_FREQ_MHZ
/ div
;
152 clkfrac
= readb(reg
);
153 frac
= clkfrac
& CLKCTRL_FRAC_FRAC_MASK
;
154 div
= clkctrl
& CLKCTRL_GPMI_DIV_MASK
;
155 return (PLL_FREQ_MHZ
* PLL_FREQ_COEF
/ frac
) / div
;
159 * Set IO clock frequency, in kHz
161 void mxs_set_ioclk(enum mxs_ioclock io
, uint32_t freq
)
163 struct mxs_clkctrl_regs
*clkctrl_regs
=
164 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
171 if ((io
< MXC_IOCLK0
) || (io
> MXC_IOCLK1
))
174 div
= (PLL_FREQ_KHZ
* PLL_FREQ_COEF
) / freq
;
182 io_reg
= CLKCTRL_FRAC0_IO0
- io
; /* Register order is reversed */
183 writeb(CLKCTRL_FRAC_CLKGATE
,
184 &clkctrl_regs
->hw_clkctrl_frac0_set
[io_reg
]);
185 writeb(CLKCTRL_FRAC_CLKGATE
| (div
& CLKCTRL_FRAC_FRAC_MASK
),
186 &clkctrl_regs
->hw_clkctrl_frac0
[io_reg
]);
187 writeb(CLKCTRL_FRAC_CLKGATE
,
188 &clkctrl_regs
->hw_clkctrl_frac0_clr
[io_reg
]);
192 * Get IO clock, returns IO clock in kHz
194 static uint32_t mxs_get_ioclk(enum mxs_ioclock io
)
196 struct mxs_clkctrl_regs
*clkctrl_regs
=
197 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
201 if ((io
< MXC_IOCLK0
) || (io
> MXC_IOCLK1
))
204 io_reg
= CLKCTRL_FRAC0_IO0
- io
; /* Register order is reversed */
206 ret
= readb(&clkctrl_regs
->hw_clkctrl_frac0
[io_reg
]) &
207 CLKCTRL_FRAC_FRAC_MASK
;
209 return (PLL_FREQ_KHZ
* PLL_FREQ_COEF
) / ret
;
213 * Configure SSP clock frequency, in kHz
215 void mxs_set_sspclk(enum mxs_sspclock ssp
, uint32_t freq
, int xtal
)
217 struct mxs_clkctrl_regs
*clkctrl_regs
=
218 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
219 uint32_t clk
, clkreg
;
221 if (ssp
> MXC_SSPCLK_MAX
)
224 clkreg
= (uint32_t)(&clkctrl_regs
->hw_clkctrl_ssp0
) +
225 (ssp
* sizeof(struct mxs_register_32
));
227 clrbits_le32(clkreg
, CLKCTRL_SSP_CLKGATE
);
228 while (readl(clkreg
) & CLKCTRL_SSP_CLKGATE
)
234 clk
= mxs_get_ioclk(ssp
>> 1);
239 /* Calculate the divider and cap it if necessary */
241 if (clk
> CLKCTRL_SSP_DIV_MASK
)
242 clk
= CLKCTRL_SSP_DIV_MASK
;
244 clrsetbits_le32(clkreg
, CLKCTRL_SSP_DIV_MASK
, clk
);
245 while (readl(clkreg
) & CLKCTRL_SSP_BUSY
)
249 writel(CLKCTRL_CLKSEQ_BYPASS_SSP0
<< ssp
,
250 &clkctrl_regs
->hw_clkctrl_clkseq_set
);
252 writel(CLKCTRL_CLKSEQ_BYPASS_SSP0
<< ssp
,
253 &clkctrl_regs
->hw_clkctrl_clkseq_clr
);
257 * Return SSP frequency, in kHz
259 static uint32_t mxs_get_sspclk(enum mxs_sspclock ssp
)
261 struct mxs_clkctrl_regs
*clkctrl_regs
=
262 (struct mxs_clkctrl_regs
*)MXS_CLKCTRL_BASE
;
266 if (ssp
> MXC_SSPCLK_MAX
)
269 tmp
= readl(&clkctrl_regs
->hw_clkctrl_clkseq
);
270 if (tmp
& (CLKCTRL_CLKSEQ_BYPASS_SSP0
<< ssp
))
271 return XTAL_FREQ_KHZ
;
273 clkreg
= (uint32_t)(&clkctrl_regs
->hw_clkctrl_ssp0
) +
274 (ssp
* sizeof(struct mxs_register_32
));
276 tmp
= readl(clkreg
) & CLKCTRL_SSP_DIV_MASK
;
281 clk
= mxs_get_ioclk(ssp
>> 1);
287 * Set SSP/MMC bus frequency, in kHz)
289 void mxs_set_ssp_busclock(unsigned int bus
, uint32_t freq
)
291 struct mxs_ssp_regs
*ssp_regs
;
292 const enum mxs_sspclock clk
= mxs_ssp_clock_by_bus(bus
);
293 const uint32_t sspclk
= mxs_get_sspclk(clk
);
295 uint32_t divide
, rate
, tgtclk
;
297 ssp_regs
= mxs_ssp_regs_by_bus(bus
);
300 * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
301 * CLOCK_DIVIDE has to be an even value from 2 to 254, and
302 * CLOCK_RATE could be any integer from 0 to 255.
304 for (divide
= 2; divide
< 254; divide
+= 2) {
305 rate
= sspclk
/ freq
/ divide
;
310 tgtclk
= sspclk
/ divide
/ rate
;
311 while (tgtclk
> freq
) {
313 tgtclk
= sspclk
/ divide
/ rate
;
318 /* Always set timeout the maximum */
319 reg
= SSP_TIMING_TIMEOUT_MASK
|
320 (divide
<< SSP_TIMING_CLOCK_DIVIDE_OFFSET
) |
321 ((rate
- 1) << SSP_TIMING_CLOCK_RATE_OFFSET
);
322 writel(reg
, &ssp_regs
->hw_ssp_timing
);
324 debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n",
328 uint32_t mxc_get_clock(enum mxc_clock clk
)
332 return mxs_get_pclk() * 1000000;
334 return mxs_get_gpmiclk() * 1000000;
337 return mxs_get_hclk() * 1000000;
339 return mxs_get_emiclk();
341 return mxs_get_ioclk(MXC_IOCLK0
);
343 return mxs_get_ioclk(MXC_IOCLK1
);
345 return XTAL_FREQ_KHZ
* 1000;
347 return mxs_get_sspclk(MXC_SSPCLK0
);
350 return mxs_get_sspclk(MXC_SSPCLK1
);
352 return mxs_get_sspclk(MXC_SSPCLK2
);
354 return mxs_get_sspclk(MXC_SSPCLK3
);