]>
Commit | Line | Data |
---|---|---|
fe8c2806 | 1 | /* |
b9365a26 | 2 | * (C) Copyright 2000-2006 |
fe8c2806 WD |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | * | |
550650dd SR |
5 | * (C) Copyright 2010 |
6 | * Stefan Roese, DENX Software Engineering, sr@denx.de. | |
7 | * | |
fe8c2806 WD |
8 | * See file CREDITS for list of people who contributed to this |
9 | * project. | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or | |
12 | * modify it under the terms of the GNU General Public License as | |
13 | * published by the Free Software Foundation; either version 2 of | |
14 | * the License, or (at your option) any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program; if not, write to the Free Software | |
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
24 | * MA 02111-1307 USA | |
25 | */ | |
3248f63a | 26 | |
fe8c2806 | 27 | /* |
31773496 JB |
28 | * This source code is dual-licensed. You may use it under the terms of the |
29 | * GNU General Public License version 2, or under the license below. | |
30 | * | |
fe8c2806 WD |
31 | * This source code has been made available to you by IBM on an AS-IS |
32 | * basis. Anyone receiving this source is licensed under IBM | |
33 | * copyrights to use it in any way he or she deems fit, including | |
34 | * copying it, modifying it, compiling it, and redistributing it either | |
35 | * with or without modifications. No license under IBM patents or | |
36 | * patent applications is to be implied by the copyright license. | |
37 | * | |
38 | * Any user of this software should understand that IBM cannot provide | |
39 | * technical support for this software and will not be responsible for | |
40 | * any consequences resulting from the use of this software. | |
41 | * | |
42 | * Any person who transfers this source code or any derivative work | |
43 | * must include the IBM copyright notice, this paragraph, and the | |
44 | * preceding two paragraphs in the transferred software. | |
45 | * | |
46 | * COPYRIGHT I B M CORPORATION 1995 | |
47 | * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M | |
48 | */ | |
3248f63a | 49 | |
fe8c2806 WD |
50 | #include <common.h> |
51 | #include <commproc.h> | |
52 | #include <asm/processor.h> | |
3248f63a | 53 | #include <asm/io.h> |
fe8c2806 | 54 | #include <watchdog.h> |
b36df561 | 55 | #include <asm/ppc4xx.h> |
fe8c2806 | 56 | |
d87080b7 WD |
57 | DECLARE_GLOBAL_DATA_PTR; |
58 | ||
e01bd218 SR |
59 | #if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \ |
60 | defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ | |
dbbd1257 | 61 | defined(CONFIG_405EX) || defined(CONFIG_440) |
fe8c2806 WD |
62 | |
63 | #if defined(CONFIG_440) | |
6e7fb6ea | 64 | |
887e2ec9 | 65 | #if defined(CONFIG_440GP) |
fe8c2806 WD |
66 | #define CR0_MASK 0x3fff0000 |
67 | #define CR0_EXTCLK_ENA 0x00600000 | |
68 | #define CR0_UDIV_POS 16 | |
887e2ec9 | 69 | #define UDIV_SUBTRACT 1 |
d1c3b275 | 70 | #define UART0_SDR CPC0_CR0 |
887e2ec9 SR |
71 | #define MFREG(a, d) d = mfdcr(a) |
72 | #define MTREG(a, d) mtdcr(a, d) | |
73 | #else /* #if defined(CONFIG_440GP) */ | |
74 | /* all other 440 PPC's access clock divider via sdr register */ | |
75 | #define CR0_MASK 0xdfffffff | |
76 | #define CR0_EXTCLK_ENA 0x00800000 | |
77 | #define CR0_UDIV_POS 0 | |
78 | #define UDIV_SUBTRACT 0 | |
d1c3b275 SR |
79 | #define UART0_SDR SDR0_UART0 |
80 | #define UART1_SDR SDR0_UART1 | |
b2815f79 SR |
81 | #if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ |
82 | defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ | |
83 | defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ | |
8ac41e3e | 84 | defined(CONFIG_460EX) || defined(CONFIG_460GT) |
d1c3b275 | 85 | #define UART2_SDR SDR0_UART2 |
887e2ec9 | 86 | #endif |
b2815f79 SR |
87 | #if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ |
88 | defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ | |
8ac41e3e | 89 | defined(CONFIG_460EX) || defined(CONFIG_460GT) |
d1c3b275 | 90 | #define UART3_SDR SDR0_UART3 |
887e2ec9 SR |
91 | #endif |
92 | #define MFREG(a, d) mfsdr(a, d) | |
93 | #define MTREG(a, d) mtsdr(a, d) | |
94 | #endif /* #if defined(CONFIG_440GP) */ | |
e01bd218 | 95 | #elif defined(CONFIG_405EP) || defined(CONFIG_405EZ) |
8749cfb4 SR |
96 | #define UCR0_MASK 0x0000007f |
97 | #define UCR1_MASK 0x00007f00 | |
98 | #define UCR0_UDIV_POS 0 | |
99 | #define UCR1_UDIV_POS 8 | |
100 | #define UDIV_MAX 127 | |
dbbd1257 | 101 | #elif defined(CONFIG_405EX) |
550650dd SR |
102 | #define MFREG(a, d) mfsdr(a, d) |
103 | #define MTREG(a, d) mtsdr(a, d) | |
dbbd1257 SR |
104 | #define CR0_MASK 0x000000ff |
105 | #define CR0_EXTCLK_ENA 0x00800000 | |
106 | #define CR0_UDIV_POS 0 | |
107 | #define UDIV_SUBTRACT 0 | |
d1c3b275 SR |
108 | #define UART0_SDR SDR0_UART0 |
109 | #define UART1_SDR SDR0_UART1 | |
8749cfb4 | 110 | #else /* CONFIG_405GP || CONFIG_405CR */ |
fe8c2806 | 111 | #define CR0_MASK 0x00001fff |
d7787c6e | 112 | #define CR0_EXTCLK_ENA 0x000000c0 |
fe8c2806 | 113 | #define CR0_UDIV_POS 1 |
8749cfb4 SR |
114 | #define UDIV_MAX 32 |
115 | #endif | |
116 | ||
6d0f6bcf | 117 | #if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK) |
0c8721a4 | 118 | #error "External serial clock not supported on AMCC PPC405EP!" |
fe8c2806 WD |
119 | #endif |
120 | ||
550650dd SR |
121 | #if (defined(CONFIG_405EX) || defined(CONFIG_405EZ) || \ |
122 | defined(CONFIG_440)) && !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) | |
123 | /* | |
124 | * For some SoC's, the cpu clock is on divider chain A, UART on | |
125 | * divider chain B ... so cpu clock is irrelevant. Get the | |
126 | * "optimized" values that are subject to the 1/2 opb clock | |
127 | * constraint. | |
128 | */ | |
129 | static u16 serial_bdiv(int baudrate, u32 *udiv) | |
fe8c2806 | 130 | { |
e01bd218 | 131 | sys_info_t sysinfo; |
550650dd SR |
132 | u32 div; /* total divisor udiv * bdiv */ |
133 | u32 umin; /* minimum udiv */ | |
134 | u16 diff; /* smallest diff */ | |
135 | u16 idiff; /* current diff */ | |
136 | u16 ibdiv; /* current bdiv */ | |
137 | u32 i; | |
138 | u32 est; /* current estimate */ | |
139 | u32 max; | |
140 | #if defined(CONFIG_405EZ) | |
141 | u32 cpr_pllc; | |
142 | u32 plloutb; | |
143 | u32 reg; | |
144 | #endif | |
fe8c2806 | 145 | |
e01bd218 | 146 | get_sys_info(&sysinfo); |
fe8c2806 | 147 | |
550650dd | 148 | #if defined(CONFIG_405EZ) |
273db7e1 | 149 | /* check the pll feedback source */ |
d1c3b275 | 150 | mfcpr(CPR0_PLLC, cpr_pllc); |
429d9571 | 151 | plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ? |
3248f63a SR |
152 | sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) * |
153 | sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB); | |
e01bd218 SR |
154 | div = plloutb / (16 * baudrate); /* total divisor */ |
155 | umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */ | |
550650dd SR |
156 | max = 256; /* highest possible */ |
157 | #else /* 405EZ */ | |
158 | div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */ | |
159 | umin = sysinfo.pllOpbDiv << 1; /* 2 x OPB divisor */ | |
160 | max = 32; /* highest possible */ | |
161 | #endif /* 405EZ */ | |
162 | ||
163 | *udiv = diff = max; | |
e01bd218 | 164 | |
550650dd SR |
165 | /* |
166 | * i is the test udiv value -- start with the largest | |
167 | * possible (max) to minimize serial clock and constrain | |
e01bd218 SR |
168 | * search to umin. |
169 | */ | |
550650dd | 170 | for (i = max; i > umin; i--) { |
e01bd218 SR |
171 | ibdiv = div / i; |
172 | est = i * ibdiv; | |
550650dd | 173 | idiff = (est > div) ? (est - div) : (div - est); |
e01bd218 | 174 | if (idiff == 0) { |
550650dd SR |
175 | *udiv = i; |
176 | break; /* can't do better */ | |
e01bd218 | 177 | } else if (idiff < diff) { |
550650dd SR |
178 | *udiv = i; /* best so far */ |
179 | diff = idiff; /* update lowest diff*/ | |
e01bd218 SR |
180 | } |
181 | } | |
182 | ||
550650dd | 183 | #if defined(CONFIG_405EZ) |
afabb498 | 184 | mfcpr(CPR0_PERD0, reg); |
e01bd218 | 185 | reg &= ~0x0000ffff; |
550650dd | 186 | reg |= ((*udiv - 0) << 8) | (*udiv - 0); |
afabb498 | 187 | mtcpr(CPR0_PERD0, reg); |
550650dd SR |
188 | #endif |
189 | ||
190 | return div / *udiv; | |
fe8c2806 | 191 | } |
550650dd | 192 | #endif /* #if (defined(CONFIG_405EP) ... */ |
fe8c2806 | 193 | |
fe8c2806 | 194 | /* |
550650dd SR |
195 | * This function returns the UART clock used by the common |
196 | * NS16550 driver. Additionally the SoC internal divisors for | |
197 | * optimal UART baudrate are configured. | |
fe8c2806 | 198 | */ |
550650dd | 199 | int get_serial_clock(void) |
fe8c2806 | 200 | { |
550650dd SR |
201 | u32 clk; |
202 | u32 udiv; | |
550650dd SR |
203 | #if !defined(CONFIG_405EZ) |
204 | u32 reg; | |
ff36fd85 | 205 | #endif |
550650dd SR |
206 | #if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) |
207 | PPC4xx_SYS_INFO sys_info; | |
dbbd1257 | 208 | #endif |
dbbd1257 SR |
209 | |
210 | /* | |
550650dd SR |
211 | * Programming of the internal divisors is SoC specific. |
212 | * Let's handle this in some #ifdef's for the SoC's. | |
dbbd1257 | 213 | */ |
dbbd1257 | 214 | |
550650dd | 215 | #if defined(CONFIG_405CR) || defined(CONFIG_405GP) |
d1c3b275 | 216 | reg = mfdcr(CPC0_CR0) & ~CR0_MASK; |
6d0f6bcf JCPV |
217 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK |
218 | clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
fe8c2806 WD |
219 | udiv = 1; |
220 | reg |= CR0_EXTCLK_ENA; | |
550650dd | 221 | #else /* CONFIG_SYS_EXT_SERIAL_CLOCK */ |
fe8c2806 | 222 | clk = gd->cpu_clk; |
6d0f6bcf | 223 | #ifdef CONFIG_SYS_405_UART_ERRATA_59 |
fe8c2806 | 224 | udiv = 31; /* Errata 59: stuck at 31 */ |
550650dd | 225 | #else /* CONFIG_SYS_405_UART_ERRATA_59 */ |
32bb34a7 WD |
226 | { |
227 | u32 tmp = CONFIG_SYS_BASE_BAUD * 16; | |
228 | ||
229 | udiv = (clk + tmp / 2) / tmp; | |
230 | } | |
8749cfb4 SR |
231 | if (udiv > UDIV_MAX) /* max. n bits for udiv */ |
232 | udiv = UDIV_MAX; | |
550650dd SR |
233 | #endif /* CONFIG_SYS_405_UART_ERRATA_59 */ |
234 | #endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */ | |
fe8c2806 | 235 | reg |= (udiv - 1) << CR0_UDIV_POS; /* set the UART divisor */ |
d1c3b275 | 236 | mtdcr (CPC0_CR0, reg); |
550650dd SR |
237 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK |
238 | clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
fe8c2806 | 239 | #else |
550650dd | 240 | clk = CONFIG_SYS_BASE_BAUD * 16; |
fe8c2806 | 241 | #endif |
550650dd | 242 | #endif /* CONFIG_405CR */ |
fe8c2806 | 243 | |
550650dd | 244 | #if defined(CONFIG_405EP) |
32bb34a7 WD |
245 | { |
246 | u32 tmp = CONFIG_SYS_BASE_BAUD * 16; | |
247 | ||
248 | reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK); | |
249 | clk = gd->cpu_clk; | |
250 | udiv = (clk + tmp / 2) / tmp; | |
251 | if (udiv > UDIV_MAX) /* max. n bits for udiv */ | |
252 | udiv = UDIV_MAX; | |
253 | } | |
550650dd SR |
254 | reg |= udiv << UCR0_UDIV_POS; /* set the UART divisor */ |
255 | reg |= udiv << UCR1_UDIV_POS; /* set the UART divisor */ | |
256 | mtdcr(CPC0_UCR, reg); | |
257 | clk = CONFIG_SYS_BASE_BAUD * 16; | |
258 | #endif /* CONFIG_405EP */ | |
fe8c2806 | 259 | |
550650dd SR |
260 | #if defined(CONFIG_405EX) || defined(CONFIG_440) |
261 | MFREG(UART0_SDR, reg); | |
262 | reg &= ~CR0_MASK; | |
fe8c2806 | 263 | |
550650dd SR |
264 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK |
265 | reg |= CR0_EXTCLK_ENA; | |
266 | udiv = 1; | |
267 | clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
268 | #else /* CONFIG_SYS_EXT_SERIAL_CLOCK */ | |
269 | clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16; | |
270 | #endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */ | |
fe8c2806 | 271 | |
550650dd | 272 | reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS; /* set the UART divisor */ |
fe8c2806 | 273 | |
fe8c2806 | 274 | /* |
550650dd SR |
275 | * Configure input clock to baudrate generator for all |
276 | * available serial ports here | |
fe8c2806 | 277 | */ |
550650dd SR |
278 | MTREG(UART0_SDR, reg); |
279 | #if defined(UART1_SDR) | |
280 | MTREG(UART1_SDR, reg); | |
068b60a0 | 281 | #endif |
550650dd SR |
282 | #if defined(UART2_SDR) |
283 | MTREG(UART2_SDR, reg); | |
284 | #endif | |
285 | #if defined(UART3_SDR) | |
286 | MTREG(UART3_SDR, reg); | |
287 | #endif | |
288 | #endif /* CONFIG_405EX ... */ | |
fe8c2806 | 289 | |
550650dd SR |
290 | #if defined(CONFIG_405EZ) |
291 | clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16; | |
292 | #endif /* CONFIG_405EZ */ | |
ff36fd85 | 293 | |
550650dd SR |
294 | /* |
295 | * Correct UART frequency in bd-info struct now that | |
296 | * the UART divisor is available | |
297 | */ | |
298 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK | |
299 | gd->uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
3248f63a | 300 | #else |
550650dd SR |
301 | get_sys_info(&sys_info); |
302 | gd->uart_clk = sys_info.freqUART / udiv; | |
303 | #endif | |
3248f63a | 304 | |
550650dd | 305 | return clk; |
3248f63a | 306 | } |
fe8c2806 | 307 | #endif /* CONFIG_405GP || CONFIG_405CR */ |