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