]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/arm/cpu/arm926ejs/mxs/clock.c
4d0cc04583324eab342cf6373a76bbc57ef6bd4f
[karo-tx-uboot.git] / arch / arm / cpu / arm926ejs / mxs / clock.c
1 /*
2  * Freescale i.MX23/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  * SPDX-License-Identifier:     GPL-2.0+
11  */
12
13 #include <common.h>
14 #include <asm/errno.h>
15 #include <asm/io.h>
16 #include <asm/arch/clock.h>
17 #include <asm/arch/imx-regs.h>
18
19 /*
20  * The PLL frequency is 480MHz and XTAL frequency is 24MHz
21  *   iMX23: datasheet section 4.2
22  *   iMX28: datasheet section 10.2
23  */
24 #define PLL_FREQ_KHZ    480000
25 #define PLL_FREQ_COEF   18
26 #define XTAL_FREQ_KHZ   24000
27
28 #define PLL_FREQ_MHZ    (PLL_FREQ_KHZ / 1000)
29 #define XTAL_FREQ_MHZ   (XTAL_FREQ_KHZ / 1000)
30
31 #if defined(CONFIG_MX23)
32 #define MXC_SSPCLK_MAX MXC_SSPCLK0
33 #elif defined(CONFIG_MX28)
34 #define MXC_SSPCLK_MAX MXC_SSPCLK3
35 #endif
36
37 static struct mxs_clkctrl_regs *clkctrl_regs = (void *)MXS_CLKCTRL_BASE;
38
39 static uint32_t get_frac_clk(uint32_t refclk, uint32_t div, uint32_t _mask)
40 {
41         uint32_t mask = (_mask + 1) >> 1;
42         uint32_t acc = div;
43         int period = 0;
44         int mult = 0;
45
46         if (div & mask)
47                 return 0;
48
49         do {
50                 acc += div;
51                 if (acc & mask) {
52                         acc &= ~mask;
53                         mult++;
54                 }
55                 period++;
56         } while (acc != div);
57
58         return refclk * mult / period;
59 }
60
61 static uint32_t mxs_get_pclk(void)
62 {
63         uint32_t clkctrl, clkseq, div;
64         uint8_t clkfrac, frac;
65
66         clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu);
67
68         div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
69         clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
70         frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
71         clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
72
73         if (clkctrl &
74                 (CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
75                 uint32_t refclk, mask;
76
77                 if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
78                         refclk = XTAL_FREQ_MHZ;
79                         mask = CLKCTRL_CPU_DIV_XTAL_MASK >>
80                                 CLKCTRL_CPU_DIV_XTAL_OFFSET;
81                         div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
82                                 CLKCTRL_CPU_DIV_XTAL_OFFSET;
83                 } else {
84                         refclk = PLL_FREQ_MHZ * PLL_FREQ_COEF / frac;
85                         mask = CLKCTRL_CPU_DIV_CPU_MASK;
86                 }
87                 return get_frac_clk(refclk, div, mask);
88         }
89
90         /* XTAL Path */
91         if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
92                 div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
93                         CLKCTRL_CPU_DIV_XTAL_OFFSET;
94                 return XTAL_FREQ_MHZ / div;
95         }
96
97         /* REF Path */
98         return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
99 }
100
101 static uint32_t mxs_get_hclk(void)
102 {
103         uint32_t div;
104         uint32_t clkctrl;
105         uint32_t refclk = mxs_get_pclk();
106
107         clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus);
108         div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
109
110         if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN)
111                 return get_frac_clk(refclk, div, CLKCTRL_HBUS_DIV_MASK);
112
113         return refclk / div;
114 }
115
116 static uint32_t mxs_get_emiclk(void)
117 {
118         uint32_t clkctrl, clkseq, div;
119         uint8_t clkfrac, frac;
120
121         clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
122         clkctrl = readl(&clkctrl_regs->hw_clkctrl_emi);
123
124         /* XTAL Path */
125         if (clkseq & CLKCTRL_CLKSEQ_BYPASS_EMI) {
126                 div = (clkctrl & CLKCTRL_EMI_DIV_XTAL_MASK) >>
127                         CLKCTRL_EMI_DIV_XTAL_OFFSET;
128                 return XTAL_FREQ_MHZ / div;
129         }
130
131         /* REF Path */
132         clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]);
133         frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
134         div = clkctrl & CLKCTRL_EMI_DIV_EMI_MASK;
135         return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
136 }
137
138 static uint32_t mxs_get_gpmiclk(void)
139 {
140 #if defined(CONFIG_MX23)
141         uint8_t *reg =
142                 &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU];
143 #elif defined(CONFIG_MX28)
144         uint8_t *reg =
145                 &clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_GPMI];
146 #endif
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(reg);
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 mxs_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 mxs_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 mxs_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
222 {
223         uint32_t clk, clkreg;
224
225         if (ssp > MXC_SSPCLK_MAX)
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 = mxs_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 mxs_get_sspclk(enum mxs_sspclock ssp)
264 {
265         uint32_t *clkreg;
266         uint32_t clk, tmp;
267
268         if (ssp > MXC_SSPCLK_MAX)
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 = mxs_get_ioclk(ssp >> 1);
283
284         return clk / tmp;
285 }
286
287 /*
288  * Set SSP/MMC bus frequency, in kHz)
289  */
290 void mxs_set_ssp_busclock(unsigned int bus, uint32_t freq)
291 {
292         struct mxs_ssp_regs *ssp_regs;
293         const enum mxs_sspclock clk = mxs_ssp_clock_by_bus(bus);
294         const uint32_t sspclk = mxs_get_sspclk(clk);
295         uint32_t reg;
296         uint32_t divide, rate, tgtclk;
297
298         ssp_regs = mxs_ssp_regs_by_bus(bus);
299
300         /*
301          * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)),
302          * CLOCK_DIVIDE has to be an even value from 2 to 254, and
303          * CLOCK_RATE could be any integer from 0 to 255.
304          */
305         for (divide = 2; divide < 254; divide += 2) {
306                 rate = sspclk / freq / divide;
307                 if (rate <= 256)
308                         break;
309         }
310
311         tgtclk = sspclk / divide / rate;
312         while (tgtclk > freq) {
313                 rate++;
314                 tgtclk = sspclk / divide / rate;
315         }
316         if (rate > 256)
317                 rate = 256;
318
319         /* Always set timeout the maximum */
320         reg = SSP_TIMING_TIMEOUT_MASK |
321                 (divide << SSP_TIMING_CLOCK_DIVIDE_OFFSET) |
322                 ((rate - 1) << SSP_TIMING_CLOCK_RATE_OFFSET);
323         writel(reg, &ssp_regs->hw_ssp_timing);
324
325         debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n",
326                 bus, tgtclk, freq);
327 }
328
329 void mxs_set_lcdclk(uint32_t freq)
330 {
331         uint32_t fp, x, k_rest, k_best, x_best, tk;
332         int32_t k_best_l = 999, k_best_t = 0, x_best_l = 0xff, x_best_t = 0xff;
333
334         if (freq == 0)
335                 return;
336
337 #if defined(CONFIG_MX23)
338         writel(CLKCTRL_CLKSEQ_BYPASS_PIX, &clkctrl_regs->hw_clkctrl_clkseq_clr);
339 #elif defined(CONFIG_MX28)
340         writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF, &clkctrl_regs->hw_clkctrl_clkseq_clr);
341 #endif
342
343         /*
344          *             /               18 \     1       1
345          * freq kHz = | 480000000 Hz * --  | * --- * ------
346          *             \                x /     k     1000
347          *
348          *      480000000 Hz   18
349          *      ------------ * --
350          *        freq kHz      x
351          * k = -------------------
352          *             1000
353          */
354
355         fp = ((PLL_FREQ_KHZ * 1000) / freq) * 18;
356
357         for (x = 18; x <= 35; x++) {
358                 tk = fp / x;
359                 if ((tk / 1000 == 0) || (tk / 1000 > 255))
360                         continue;
361
362                 k_rest = tk % 1000;
363
364                 if (k_rest < (k_best_l % 1000)) {
365                         k_best_l = tk;
366                         x_best_l = x;
367                 }
368
369                 if (k_rest > (k_best_t % 1000)) {
370                         k_best_t = tk;
371                         x_best_t = x;
372                 }
373         }
374
375         if (1000 - (k_best_t % 1000) > (k_best_l % 1000)) {
376                 k_best = k_best_l;
377                 x_best = x_best_l;
378         } else {
379                 k_best = k_best_t;
380                 x_best = x_best_t;
381         }
382
383         k_best /= 1000;
384
385 #if defined(CONFIG_MX23)
386         writeb(CLKCTRL_FRAC_CLKGATE,
387                 &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_PIX]);
388         writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
389                 &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_PIX]);
390         writeb(CLKCTRL_FRAC_CLKGATE,
391                 &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_PIX]);
392
393         writel(CLKCTRL_PIX_CLKGATE,
394                 &clkctrl_regs->hw_clkctrl_pix_set);
395         clrsetbits_le32(&clkctrl_regs->hw_clkctrl_pix,
396                         CLKCTRL_PIX_DIV_MASK | CLKCTRL_PIX_CLKGATE,
397                         k_best << CLKCTRL_PIX_DIV_OFFSET);
398
399         while (readl(&clkctrl_regs->hw_clkctrl_pix) & CLKCTRL_PIX_BUSY)
400                 ;
401 #elif defined(CONFIG_MX28)
402         writeb(CLKCTRL_FRAC_CLKGATE,
403                 &clkctrl_regs->hw_clkctrl_frac1_set[CLKCTRL_FRAC1_PIX]);
404         writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
405                 &clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_PIX]);
406         writeb(CLKCTRL_FRAC_CLKGATE,
407                 &clkctrl_regs->hw_clkctrl_frac1_clr[CLKCTRL_FRAC1_PIX]);
408
409         /* The i.MX28 Ref. Manual states:
410          * CLK_DIS_LCDIF Gate. If set to 1, CLK_DIS_LCDIF is gated off.
411          * 0: CLK_DIS_LCDIF is not gated.
412          * When this bit is modified, or when it is high,
413          * the DIV field should not change its value.
414          * The DIV field can change ONLY when this clock gate bit field is low.
415          * Note: This register does not have set/clear/toggle functionality!
416          */
417         /* clear CLKCTRL_DIS_LCDIF_CLKGATE */
418         writel(0, &clkctrl_regs->hw_clkctrl_lcdif);
419         writel(k_best << CLKCTRL_DIS_LCDIF_DIV_OFFSET,
420                 &clkctrl_regs->hw_clkctrl_lcdif);
421
422         while (readl(&clkctrl_regs->hw_clkctrl_lcdif) & CLKCTRL_DIS_LCDIF_BUSY)
423                 ;
424 #endif
425 }
426
427 static uint32_t mxs_get_xbus_clk(void)
428 {
429         uint32_t div;
430         uint32_t clkctrl;
431         uint32_t refclk = mxs_get_pclk();
432
433         clkctrl = readl(&clkctrl_regs->hw_clkctrl_xbus);
434         div = clkctrl & CLKCTRL_XBUS_DIV_MASK;
435
436         if (clkctrl & CLKCTRL_XBUS_DIV_FRAC_EN)
437                 return get_frac_clk(refclk, div, CLKCTRL_XBUS_DIV_MASK);
438
439         return refclk / div;
440 }
441
442 uint32_t mxc_get_clock(enum mxc_clock clk)
443 {
444         switch (clk) {
445         case MXC_ARM_CLK:
446                 return mxs_get_pclk() * 1000000;
447         case MXC_GPMI_CLK:
448                 return mxs_get_gpmiclk() * 1000000;
449         case MXC_AHB_CLK:
450         case MXC_IPG_CLK:
451                 return mxs_get_hclk() * 1000000;
452         case MXC_EMI_CLK:
453                 return mxs_get_emiclk();
454         case MXC_IO0_CLK:
455                 return mxs_get_ioclk(MXC_IOCLK0);
456         case MXC_IO1_CLK:
457                 return mxs_get_ioclk(MXC_IOCLK1);
458         case MXC_XTAL_CLK:
459                 return XTAL_FREQ_KHZ * 1000;
460         case MXC_SSP0_CLK:
461                 return mxs_get_sspclk(MXC_SSPCLK0);
462 #ifdef CONFIG_MX28
463         case MXC_SSP1_CLK:
464                 return mxs_get_sspclk(MXC_SSPCLK1);
465         case MXC_SSP2_CLK:
466                 return mxs_get_sspclk(MXC_SSPCLK2);
467         case MXC_SSP3_CLK:
468                 return mxs_get_sspclk(MXC_SSPCLK3);
469 #endif
470         case MXC_XBUS_CLK:
471                 return mxs_get_xbus_clk() * 1000000;
472         default:
473                 printf("Invalid clock selector %u\n", clk);
474         }
475
476         return 0;
477 }