]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/arm/cpu/arm926ejs/mxs/clock.c
i.MX video: use already defined CONFIG_IPUV3_CLK instead of CONFIG_IPU_CLKRATE
[karo-tx-uboot.git] / arch / arm / cpu / arm926ejs / mxs / clock.c
1 /*
2  * Freescale i.MX28 clock setup code
3  *
4  * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
5  * on behalf of DENX Software Engineering GmbH
6  *
7  * Based on code from LTIB:
8  * Copyright (C) 2010 Freescale Semiconductor, Inc.
9  *
10  * See file CREDITS for list of people who contributed to this
11  * project.
12  *
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.
17  *
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.
22  *
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,
26  * MA 02111-1307 USA
27  */
28
29 #include <common.h>
30 #include <asm/errno.h>
31 #include <asm/io.h>
32 #include <asm/arch/clock.h>
33 #include <asm/arch/imx-regs.h>
34
35 /* The PLL frequency is always 480MHz, see section 10.2 in iMX28 datasheet. */
36 #define PLL_FREQ_KHZ    480000
37 #define PLL_FREQ_COEF   18
38 /* The XTAL frequency is always 24MHz, see section 10.2 in iMX28 datasheet. */
39 #define XTAL_FREQ_KHZ   24000
40
41 #define PLL_FREQ_MHZ    (PLL_FREQ_KHZ / 1000)
42 #define XTAL_FREQ_MHZ   (XTAL_FREQ_KHZ / 1000)
43
44 static struct mxs_clkctrl_regs *clkctrl_regs = (void *)MXS_CLKCTRL_BASE;
45
46 static uint32_t get_frac_clk(uint32_t refclk, uint32_t div, uint32_t _mask)
47 {
48         uint32_t mask = (_mask + 1) >> 1;
49         uint32_t acc = div;
50         int period = 0;
51         int mult = 0;
52
53         if (div & mask)
54                 return 0;
55
56         do {
57                 acc += div;
58                 if (acc & mask) {
59                         acc &= ~mask;
60                         mult++;
61                 }
62                 period++;
63         } while (acc != div);
64
65         return refclk * mult / period;
66 }
67
68 static uint32_t mx28_get_pclk(void)
69 {
70         uint32_t clkctrl, clkseq, div;
71         uint8_t clkfrac, frac;
72
73         clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu);
74
75         div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
76         clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
77         frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
78         clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
79
80         if (clkctrl &
81                 (CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
82                 uint32_t refclk, mask;
83
84                 if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
85                         refclk = XTAL_FREQ_MHZ;
86                         mask = CLKCTRL_CPU_DIV_XTAL_MASK >>
87                                 CLKCTRL_CPU_DIV_XTAL_OFFSET;
88                         div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
89                                 CLKCTRL_CPU_DIV_XTAL_OFFSET;
90                 } else {
91                         refclk = PLL_FREQ_MHZ * PLL_FREQ_COEF / frac;
92                         mask = CLKCTRL_CPU_DIV_CPU_MASK;
93                 }
94                 return get_frac_clk(refclk, div, mask);
95         }
96
97         /* XTAL Path */
98         if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
99                 div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
100                         CLKCTRL_CPU_DIV_XTAL_OFFSET;
101                 return XTAL_FREQ_MHZ / div;
102         }
103
104         /* REF Path */
105         return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
106 }
107
108 static uint32_t mx28_get_hclk(void)
109 {
110         uint32_t div;
111         uint32_t clkctrl;
112         uint32_t refclk = mx28_get_pclk();
113
114         clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus);
115         div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
116
117         if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN)
118                 return get_frac_clk(refclk, div, CLKCTRL_HBUS_DIV_MASK);
119
120         return refclk / div;
121 }
122
123 static uint32_t mx28_get_emiclk(void)
124 {
125         uint32_t clkctrl, clkseq, div;
126         uint8_t clkfrac, frac;
127
128         clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
129         clkctrl = readl(&clkctrl_regs->hw_clkctrl_emi);
130
131         /* XTAL Path */
132         if (clkseq & CLKCTRL_CLKSEQ_BYPASS_EMI) {
133                 div = (clkctrl & CLKCTRL_EMI_DIV_XTAL_MASK) >>
134                         CLKCTRL_EMI_DIV_XTAL_OFFSET;
135                 return XTAL_FREQ_MHZ / div;
136         }
137
138         /* REF Path */
139         clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]);
140         frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
141         div = clkctrl & CLKCTRL_EMI_DIV_EMI_MASK;
142         return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
143 }
144
145 static uint32_t mx28_get_gpmiclk(void)
146 {
147         uint32_t clkctrl, clkseq, div;
148         uint8_t clkfrac, frac;
149
150         clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
151         clkctrl = readl(&clkctrl_regs->hw_clkctrl_gpmi);
152
153         /* XTAL Path */
154         if (clkseq & CLKCTRL_CLKSEQ_BYPASS_GPMI) {
155                 div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
156                 return XTAL_FREQ_MHZ / div;
157         }
158
159         /* REF Path */
160         clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_GPMI]);
161         frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
162         div = clkctrl & CLKCTRL_GPMI_DIV_MASK;
163         return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
164 }
165
166 /*
167  * Set IO clock frequency, in kHz
168  */
169 void mx28_set_ioclk(enum mxs_ioclock io, uint32_t freq)
170 {
171         uint32_t div;
172         int io_reg;
173
174         if (freq == 0)
175                 return;
176
177         if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
178                 return;
179
180         div = (PLL_FREQ_KHZ * PLL_FREQ_COEF) / freq;
181
182         if (div < 18)
183                 div = 18;
184
185         if (div > 35)
186                 div = 35;
187
188         io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
189         writeb(CLKCTRL_FRAC_CLKGATE,
190                 &clkctrl_regs->hw_clkctrl_frac0_set[io_reg]);
191         writeb(CLKCTRL_FRAC_CLKGATE | (div & CLKCTRL_FRAC_FRAC_MASK),
192                 &clkctrl_regs->hw_clkctrl_frac0[io_reg]);
193         writeb(CLKCTRL_FRAC_CLKGATE,
194                 &clkctrl_regs->hw_clkctrl_frac0_clr[io_reg]);
195 }
196
197 /*
198  * Get IO clock, returns IO clock in kHz
199  */
200 static uint32_t mx28_get_ioclk(enum mxs_ioclock io)
201 {
202         uint8_t ret;
203         int io_reg;
204
205         if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1)) {
206                 printf("%s: IO clock selector %u out of range %u..%u\n",
207                         __func__, io, MXC_IOCLK0, MXC_IOCLK1);
208                 return 0;
209         }
210         io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
211
212         ret = readb(&clkctrl_regs->hw_clkctrl_frac0[io_reg]) &
213                 CLKCTRL_FRAC_FRAC_MASK;
214
215         return (PLL_FREQ_KHZ * PLL_FREQ_COEF) / ret;
216 }
217
218 /*
219  * Configure SSP clock frequency, in kHz
220  */
221 void mx28_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
222 {
223         uint32_t clk, clkreg;
224
225         if (ssp > MXC_SSPCLK3)
226                 return;
227
228         clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
229                         (ssp * sizeof(struct mxs_register_32));
230
231         clrbits_le32(clkreg, CLKCTRL_SSP_CLKGATE);
232         while (readl(clkreg) & CLKCTRL_SSP_CLKGATE)
233                 ;
234
235         if (xtal)
236                 clk = XTAL_FREQ_KHZ;
237         else
238                 clk = mx28_get_ioclk(ssp >> 1);
239
240         if (freq > clk)
241                 return;
242
243         /* Calculate the divider and cap it if necessary */
244         clk /= freq;
245         if (clk > CLKCTRL_SSP_DIV_MASK)
246                 clk = CLKCTRL_SSP_DIV_MASK;
247
248         clrsetbits_le32(clkreg, CLKCTRL_SSP_DIV_MASK, clk);
249         while (readl(clkreg) & CLKCTRL_SSP_BUSY)
250                 ;
251
252         if (xtal)
253                 writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
254                         &clkctrl_regs->hw_clkctrl_clkseq_set);
255         else
256                 writel(CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp,
257                         &clkctrl_regs->hw_clkctrl_clkseq_clr);
258 }
259
260 /*
261  * Return SSP frequency, in kHz
262  */
263 static uint32_t mx28_get_sspclk(enum mxs_sspclock ssp)
264 {
265         uint32_t *clkreg;
266         uint32_t clk, tmp;
267
268         if (ssp > MXC_SSPCLK3)
269                 return 0;
270
271         tmp = readl(&clkctrl_regs->hw_clkctrl_clkseq);
272         if (tmp & (CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp))
273                 return XTAL_FREQ_KHZ;
274
275         clkreg = &clkctrl_regs->hw_clkctrl_ssp0 +
276                         ssp * sizeof(struct mxs_register_32);
277
278         tmp = readl(clkreg) & CLKCTRL_SSP_DIV_MASK;
279         if (tmp == 0)
280                 return 0;
281
282         clk = mx28_get_ioclk(ssp >> 1);
283         return clk / tmp;
284 }
285
286 /*
287  * Set SSP/MMC bus frequency, in kHz)
288  */
289 void mx28_set_ssp_busclock(unsigned int bus, uint32_t freq)
290 {
291         struct mxs_ssp_regs *ssp_regs;
292         const uint32_t sspclk = mx28_get_sspclk(bus);
293         uint32_t reg;
294         uint32_t divide, rate, tgtclk;
295
296         ssp_regs = (struct mxs_ssp_regs *)(MXS_SSP0_BASE + (bus * 0x2000));
297
298         /*
299          * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
300          * CLOCK_DIVIDE has to be an even value from 2 to 254, and
301          * CLOCK_RATE could be any integer from 0 to 255.
302          */
303         for (divide = 2; divide < 254; divide += 2) {
304                 rate = sspclk / freq / divide;
305                 if (rate <= 256)
306                         break;
307         }
308
309         tgtclk = sspclk / divide / rate;
310         while (tgtclk > freq) {
311                 rate++;
312                 tgtclk = sspclk / divide / rate;
313         }
314         if (rate > 256)
315                 rate = 256;
316
317         /* Always set timeout the maximum */
318         reg = SSP_TIMING_TIMEOUT_MASK |
319                 (divide << SSP_TIMING_CLOCK_DIVIDE_OFFSET) |
320                 ((rate - 1) << SSP_TIMING_CLOCK_RATE_OFFSET);
321         writel(reg, &ssp_regs->hw_ssp_timing);
322
323         debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n",
324                 bus, tgtclk, freq);
325 }
326
327 static uint32_t mx28_get_xbus_clk(void)
328 {
329         struct mxs_clkctrl_regs *clkctrl_regs =
330                 (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
331         uint32_t div;
332         uint32_t clkctrl;
333         uint32_t refclk = mx28_get_pclk();
334
335         clkctrl = readl(&clkctrl_regs->hw_clkctrl_xbus);
336         div = clkctrl & CLKCTRL_XBUS_DIV_MASK;
337
338         if (clkctrl & CLKCTRL_XBUS_DIV_FRAC_EN)
339                 return get_frac_clk(refclk, div, CLKCTRL_XBUS_DIV_MASK);
340
341         return refclk / div;
342 }
343
344 uint32_t mxc_get_clock(enum mxc_clock clk)
345 {
346         switch (clk) {
347         case MXC_ARM_CLK:
348                 return mx28_get_pclk() * 1000000;
349         case MXC_GPMI_CLK:
350                 return mx28_get_gpmiclk() * 1000000;
351         case MXC_AHB_CLK:
352         case MXC_IPG_CLK:
353                 return mx28_get_hclk() * 1000000;
354         case MXC_EMI_CLK:
355                 return mx28_get_emiclk();
356         case MXC_IO0_CLK:
357                 return mx28_get_ioclk(MXC_IOCLK0);
358         case MXC_IO1_CLK:
359                 return mx28_get_ioclk(MXC_IOCLK1);
360         case MXC_SSP0_CLK:
361                 return mx28_get_sspclk(MXC_SSPCLK0);
362         case MXC_SSP1_CLK:
363                 return mx28_get_sspclk(MXC_SSPCLK1);
364         case MXC_SSP2_CLK:
365                 return mx28_get_sspclk(MXC_SSPCLK2);
366         case MXC_SSP3_CLK:
367                 return mx28_get_sspclk(MXC_SSPCLK3);
368         case MXC_XTAL_CLK:
369                 return XTAL_FREQ_KHZ * 1000;
370         case MXC_XBUS_CLK:
371                 return mx28_get_xbus_clk() * 1000000;
372         default:
373                 printf("Invalid clock selector %u\n", clk);
374         }
375
376         return 0;
377 }