]> git.sur5r.net Git - u-boot/blob - arch/m68k/cpu/mcf532x/speed.c
SPDX: Convert all of our single license tags to Linux Kernel style
[u-boot] / arch / m68k / cpu / mcf532x / speed.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *
4  * (C) Copyright 2000-2003
5  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6  *
7  * Copyright (C) 2004-2008, 2012 Freescale Semiconductor, Inc.
8  * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
9  */
10
11 #include <common.h>
12 #include <asm/processor.h>
13
14 #include <asm/immap.h>
15 #include <asm/io.h>
16
17 DECLARE_GLOBAL_DATA_PTR;
18
19 /* PLL min/max specifications */
20 #define MAX_FVCO        500000  /* KHz */
21 #define MAX_FSYS        80000   /* KHz */
22 #define MIN_FSYS        58333   /* KHz */
23
24 #ifdef CONFIG_MCF5301x
25 #define FREF            20000   /* KHz */
26 #define MAX_MFD         63      /* Multiplier */
27 #define MIN_MFD         0       /* Multiplier */
28 #define USBDIV          8
29
30 /* Low Power Divider specifications */
31 #define MIN_LPD         (0)     /* Divider (not encoded) */
32 #define MAX_LPD         (15)    /* Divider (not encoded) */
33 #define DEFAULT_LPD     (0)     /* Divider (not encoded) */
34 #endif
35
36 #ifdef CONFIG_MCF532x
37 #define FREF            16000   /* KHz */
38 #define MAX_MFD         135     /* Multiplier */
39 #define MIN_MFD         88      /* Multiplier */
40
41 /* Low Power Divider specifications */
42 #define MIN_LPD         (1 << 0)        /* Divider (not encoded) */
43 #define MAX_LPD         (1 << 15)       /* Divider (not encoded) */
44 #define DEFAULT_LPD     (1 << 1)        /* Divider (not encoded) */
45 #endif
46
47 #define BUSDIV          6       /* Divider */
48
49 /* Get the value of the current system clock */
50 int get_sys_clock(void)
51 {
52         ccm_t *ccm = (ccm_t *)(MMAP_CCM);
53         pll_t *pll = (pll_t *)(MMAP_PLL);
54         int divider;
55
56         /* Test to see if device is in LIMP mode */
57         if (in_be16(&ccm->misccr) & CCM_MISCCR_LIMP) {
58                 divider = in_be16(&ccm->cdr) & CCM_CDR_LPDIV(0xF);
59 #ifdef CONFIG_MCF5301x
60                 return (FREF / (3 * (1 << divider)));
61 #endif
62 #ifdef CONFIG_MCF532x
63                 return (FREF / (2 << divider));
64 #endif
65         } else {
66 #ifdef CONFIG_MCF5301x
67                 u32 pfdr = (in_be32(&pll->pcr) & 0x3F) + 1;
68                 u32 refdiv = (1 << ((in_be32(&pll->pcr) & PLL_PCR_REFDIV(7)) >> 8));
69                 u32 busdiv = ((in_be32(&pll->pdr) & 0x00F0) >> 4) + 1;
70
71                 return (((FREF * pfdr) / refdiv) / busdiv);
72 #endif
73 #ifdef CONFIG_MCF532x
74                 return (FREF * in_8(&pll->pfdr)) / (BUSDIV * 4);
75 #endif
76         }
77 }
78
79 /*
80  * Initialize the Low Power Divider circuit
81  *
82  * Parameters:
83  *  div     Desired system frequency divider
84  *
85  * Return Value:
86  *  The resulting output system frequency
87  */
88 int clock_limp(int div)
89 {
90         ccm_t *ccm = (ccm_t *)(MMAP_CCM);
91         u32 temp;
92
93         /* Check bounds of divider */
94         if (div < MIN_LPD)
95                 div = MIN_LPD;
96         if (div > MAX_LPD)
97                 div = MAX_LPD;
98
99         /* Save of the current value of the SSIDIV so we don't overwrite the value */
100         temp = (in_be16(&ccm->cdr) & CCM_CDR_SSIDIV(0xFF));
101
102         /* Apply the divider to the system clock */
103         out_be16(&ccm->cdr, CCM_CDR_LPDIV(div) | CCM_CDR_SSIDIV(temp));
104
105         setbits_be16(&ccm->misccr, CCM_MISCCR_LIMP);
106
107         return (FREF / (3 * (1 << div)));
108 }
109
110 /* Exit low power LIMP mode */
111 int clock_exit_limp(void)
112 {
113         ccm_t *ccm = (ccm_t *)(MMAP_CCM);
114         int fout;
115
116         /* Exit LIMP mode */
117         clrbits_be16(&ccm->misccr, CCM_MISCCR_LIMP);
118
119         /* Wait for PLL to lock */
120         while (!(in_be16(&ccm->misccr) & CCM_MISCCR_PLL_LOCK))
121                 ;
122
123         fout = get_sys_clock();
124
125         return fout;
126 }
127
128 /* Initialize the PLL
129  *
130  * Parameters:
131  *  fref    PLL reference clock frequency in KHz
132  *  fsys    Desired PLL output frequency in KHz
133  *  flags   Operating parameters
134  *
135  * Return Value:
136  *  The resulting output system frequency
137  */
138 int clock_pll(int fsys, int flags)
139 {
140 #ifdef CONFIG_MCF532x
141         u32 *sdram_workaround = (u32 *)(MMAP_SDRAM + 0x80);
142 #endif
143         sdram_t *sdram = (sdram_t *)(MMAP_SDRAM);
144         pll_t *pll = (pll_t *)(MMAP_PLL);
145         int fref, temp, fout, mfd;
146         u32 i;
147
148         fref = FREF;
149
150         if (fsys == 0) {
151                 /* Return current PLL output */
152 #ifdef CONFIG_MCF5301x
153                 u32 busdiv = ((in_be32(&pll->pdr) >> 4) & 0x0F) + 1;
154                 mfd = (in_be32(&pll->pcr) & 0x3F) + 1;
155
156                 return (fref * mfd) / busdiv;
157 #endif
158 #ifdef CONFIG_MCF532x
159                 mfd = in_8(&pll->pfdr);
160
161                 return (fref * mfd / (BUSDIV * 4));
162 #endif
163         }
164
165         /* Check bounds of requested system clock */
166         if (fsys > MAX_FSYS)
167                 fsys = MAX_FSYS;
168
169         if (fsys < MIN_FSYS)
170                 fsys = MIN_FSYS;
171
172         /*
173          * Multiplying by 100 when calculating the temp value,
174          * and then dividing by 100 to calculate the mfd allows
175          * for exact values without needing to include floating
176          * point libraries.
177          */
178         temp = (100 * fsys) / fref;
179 #ifdef CONFIG_MCF5301x
180         mfd = (BUSDIV * temp) / 100;
181
182         /* Determine the output frequency for selected values */
183         fout = ((fref * mfd) / BUSDIV);
184 #endif
185 #ifdef CONFIG_MCF532x
186         mfd = (4 * BUSDIV * temp) / 100;
187
188         /* Determine the output frequency for selected values */
189         fout = ((fref * mfd) / (BUSDIV * 4));
190 #endif
191
192 /* must not tamper with SDRAMC if running from SDRAM */
193 #if !defined(CONFIG_MONITOR_IS_IN_RAM)
194         /*
195          * Check to see if the SDRAM has already been initialized.
196          * If it has then the SDRAM needs to be put into self refresh
197          * mode before reprogramming the PLL.
198          */
199         if (in_be32(&sdram->ctrl) & SDRAMC_SDCR_REF)
200                 clrbits_be32(&sdram->ctrl, SDRAMC_SDCR_CKE);
201
202         /*
203          * Initialize the PLL to generate the new system clock frequency.
204          * The device must be put into LIMP mode to reprogram the PLL.
205          */
206
207         /* Enter LIMP mode */
208         clock_limp(DEFAULT_LPD);
209
210 #ifdef CONFIG_MCF5301x
211         out_be32(&pll->pdr,
212                 PLL_PDR_OUTDIV1((BUSDIV / 3) - 1) |
213                 PLL_PDR_OUTDIV2(BUSDIV - 1)     |
214                 PLL_PDR_OUTDIV3((BUSDIV / 2) - 1) |
215                 PLL_PDR_OUTDIV4(USBDIV - 1));
216
217         clrbits_be32(&pll->pcr, ~PLL_PCR_FBDIV_UNMASK);
218         setbits_be32(&pll->pcr, PLL_PCR_FBDIV(mfd - 1));
219 #endif
220 #ifdef CONFIG_MCF532x
221         /* Reprogram PLL for desired fsys */
222         out_8(&pll->podr,
223                 PLL_PODR_CPUDIV(BUSDIV / 3) | PLL_PODR_BUSDIV(BUSDIV));
224
225         out_8(&pll->pfdr, mfd);
226 #endif
227
228         /* Exit LIMP mode */
229         clock_exit_limp();
230
231         /* Return the SDRAM to normal operation if it is in use. */
232         if (in_be32(&sdram->ctrl) & SDRAMC_SDCR_REF)
233                 setbits_be32(&sdram->ctrl, SDRAMC_SDCR_CKE);
234
235 #ifdef CONFIG_MCF532x
236         /*
237          * software workaround for SDRAM opeartion after exiting LIMP
238          * mode errata
239          */
240         out_be32(sdram_workaround, CONFIG_SYS_SDRAM_BASE);
241 #endif
242
243         /* wait for DQS logic to relock */
244         for (i = 0; i < 0x200; i++) ;
245 #endif /* !defined(CONFIG_MONITOR_IS_IN_RAM) */
246
247         return fout;
248 }
249
250 /* get_clocks() fills in gd->cpu_clock and gd->bus_clk */
251 int get_clocks(void)
252 {
253         gd->bus_clk = clock_pll(CONFIG_SYS_CLK / 1000, 0) * 1000;
254         gd->cpu_clk = (gd->bus_clk * 3);
255
256 #ifdef CONFIG_SYS_I2C_FSL
257         gd->arch.i2c1_clk = gd->bus_clk;
258 #endif
259
260         return (0);
261 }