]>
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 | * | |
1b387ef5 | 8 | * SPDX-License-Identifier: GPL-2.0 IBM-pibs |
fe8c2806 | 9 | */ |
3248f63a | 10 | |
fe8c2806 | 11 | #include <common.h> |
fe8c2806 | 12 | #include <asm/processor.h> |
3248f63a | 13 | #include <asm/io.h> |
fe8c2806 | 14 | #include <watchdog.h> |
b36df561 | 15 | #include <asm/ppc4xx.h> |
fe8c2806 | 16 | |
d87080b7 WD |
17 | DECLARE_GLOBAL_DATA_PTR; |
18 | ||
3fb85889 | 19 | #if defined(CONFIG_405GP) || \ |
e01bd218 | 20 | defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ |
dbbd1257 | 21 | defined(CONFIG_405EX) || defined(CONFIG_440) |
fe8c2806 WD |
22 | |
23 | #if defined(CONFIG_440) | |
6e7fb6ea | 24 | |
887e2ec9 | 25 | #if defined(CONFIG_440GP) |
fe8c2806 WD |
26 | #define CR0_MASK 0x3fff0000 |
27 | #define CR0_EXTCLK_ENA 0x00600000 | |
28 | #define CR0_UDIV_POS 16 | |
887e2ec9 | 29 | #define UDIV_SUBTRACT 1 |
d1c3b275 | 30 | #define UART0_SDR CPC0_CR0 |
887e2ec9 SR |
31 | #define MFREG(a, d) d = mfdcr(a) |
32 | #define MTREG(a, d) mtdcr(a, d) | |
33 | #else /* #if defined(CONFIG_440GP) */ | |
34 | /* all other 440 PPC's access clock divider via sdr register */ | |
35 | #define CR0_MASK 0xdfffffff | |
36 | #define CR0_EXTCLK_ENA 0x00800000 | |
37 | #define CR0_UDIV_POS 0 | |
38 | #define UDIV_SUBTRACT 0 | |
d1c3b275 SR |
39 | #define UART0_SDR SDR0_UART0 |
40 | #define UART1_SDR SDR0_UART1 | |
b2815f79 SR |
41 | #if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ |
42 | defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ | |
43 | defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ | |
8ac41e3e | 44 | defined(CONFIG_460EX) || defined(CONFIG_460GT) |
d1c3b275 | 45 | #define UART2_SDR SDR0_UART2 |
887e2ec9 | 46 | #endif |
b2815f79 SR |
47 | #if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ |
48 | defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ | |
8ac41e3e | 49 | defined(CONFIG_460EX) || defined(CONFIG_460GT) |
d1c3b275 | 50 | #define UART3_SDR SDR0_UART3 |
887e2ec9 SR |
51 | #endif |
52 | #define MFREG(a, d) mfsdr(a, d) | |
53 | #define MTREG(a, d) mtsdr(a, d) | |
54 | #endif /* #if defined(CONFIG_440GP) */ | |
e01bd218 | 55 | #elif defined(CONFIG_405EP) || defined(CONFIG_405EZ) |
8749cfb4 SR |
56 | #define UCR0_MASK 0x0000007f |
57 | #define UCR1_MASK 0x00007f00 | |
58 | #define UCR0_UDIV_POS 0 | |
59 | #define UCR1_UDIV_POS 8 | |
60 | #define UDIV_MAX 127 | |
dbbd1257 | 61 | #elif defined(CONFIG_405EX) |
550650dd SR |
62 | #define MFREG(a, d) mfsdr(a, d) |
63 | #define MTREG(a, d) mtsdr(a, d) | |
dbbd1257 SR |
64 | #define CR0_MASK 0x000000ff |
65 | #define CR0_EXTCLK_ENA 0x00800000 | |
66 | #define CR0_UDIV_POS 0 | |
67 | #define UDIV_SUBTRACT 0 | |
d1c3b275 SR |
68 | #define UART0_SDR SDR0_UART0 |
69 | #define UART1_SDR SDR0_UART1 | |
3fb85889 | 70 | #else /* CONFIG_405GP */ |
fe8c2806 | 71 | #define CR0_MASK 0x00001fff |
d7787c6e | 72 | #define CR0_EXTCLK_ENA 0x000000c0 |
fe8c2806 | 73 | #define CR0_UDIV_POS 1 |
8749cfb4 SR |
74 | #define UDIV_MAX 32 |
75 | #endif | |
76 | ||
6d0f6bcf | 77 | #if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK) |
0c8721a4 | 78 | #error "External serial clock not supported on AMCC PPC405EP!" |
fe8c2806 WD |
79 | #endif |
80 | ||
550650dd SR |
81 | #if (defined(CONFIG_405EX) || defined(CONFIG_405EZ) || \ |
82 | defined(CONFIG_440)) && !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) | |
83 | /* | |
84 | * For some SoC's, the cpu clock is on divider chain A, UART on | |
85 | * divider chain B ... so cpu clock is irrelevant. Get the | |
86 | * "optimized" values that are subject to the 1/2 opb clock | |
87 | * constraint. | |
88 | */ | |
89 | static u16 serial_bdiv(int baudrate, u32 *udiv) | |
fe8c2806 | 90 | { |
e01bd218 | 91 | sys_info_t sysinfo; |
550650dd SR |
92 | u32 div; /* total divisor udiv * bdiv */ |
93 | u32 umin; /* minimum udiv */ | |
94 | u16 diff; /* smallest diff */ | |
95 | u16 idiff; /* current diff */ | |
96 | u16 ibdiv; /* current bdiv */ | |
97 | u32 i; | |
98 | u32 est; /* current estimate */ | |
99 | u32 max; | |
100 | #if defined(CONFIG_405EZ) | |
101 | u32 cpr_pllc; | |
102 | u32 plloutb; | |
103 | u32 reg; | |
104 | #endif | |
fe8c2806 | 105 | |
e01bd218 | 106 | get_sys_info(&sysinfo); |
fe8c2806 | 107 | |
550650dd | 108 | #if defined(CONFIG_405EZ) |
273db7e1 | 109 | /* check the pll feedback source */ |
d1c3b275 | 110 | mfcpr(CPR0_PLLC, cpr_pllc); |
429d9571 | 111 | plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ? |
3248f63a SR |
112 | sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) * |
113 | sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB); | |
e01bd218 SR |
114 | div = plloutb / (16 * baudrate); /* total divisor */ |
115 | umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */ | |
550650dd SR |
116 | max = 256; /* highest possible */ |
117 | #else /* 405EZ */ | |
118 | div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */ | |
119 | umin = sysinfo.pllOpbDiv << 1; /* 2 x OPB divisor */ | |
120 | max = 32; /* highest possible */ | |
121 | #endif /* 405EZ */ | |
122 | ||
123 | *udiv = diff = max; | |
e01bd218 | 124 | |
550650dd SR |
125 | /* |
126 | * i is the test udiv value -- start with the largest | |
127 | * possible (max) to minimize serial clock and constrain | |
e01bd218 SR |
128 | * search to umin. |
129 | */ | |
550650dd | 130 | for (i = max; i > umin; i--) { |
e01bd218 SR |
131 | ibdiv = div / i; |
132 | est = i * ibdiv; | |
550650dd | 133 | idiff = (est > div) ? (est - div) : (div - est); |
e01bd218 | 134 | if (idiff == 0) { |
550650dd SR |
135 | *udiv = i; |
136 | break; /* can't do better */ | |
e01bd218 | 137 | } else if (idiff < diff) { |
550650dd SR |
138 | *udiv = i; /* best so far */ |
139 | diff = idiff; /* update lowest diff*/ | |
e01bd218 SR |
140 | } |
141 | } | |
142 | ||
550650dd | 143 | #if defined(CONFIG_405EZ) |
afabb498 | 144 | mfcpr(CPR0_PERD0, reg); |
e01bd218 | 145 | reg &= ~0x0000ffff; |
550650dd | 146 | reg |= ((*udiv - 0) << 8) | (*udiv - 0); |
afabb498 | 147 | mtcpr(CPR0_PERD0, reg); |
550650dd SR |
148 | #endif |
149 | ||
150 | return div / *udiv; | |
fe8c2806 | 151 | } |
550650dd | 152 | #endif /* #if (defined(CONFIG_405EP) ... */ |
fe8c2806 | 153 | |
fe8c2806 | 154 | /* |
550650dd SR |
155 | * This function returns the UART clock used by the common |
156 | * NS16550 driver. Additionally the SoC internal divisors for | |
157 | * optimal UART baudrate are configured. | |
fe8c2806 | 158 | */ |
550650dd | 159 | int get_serial_clock(void) |
fe8c2806 | 160 | { |
550650dd SR |
161 | u32 clk; |
162 | u32 udiv; | |
550650dd SR |
163 | #if !defined(CONFIG_405EZ) |
164 | u32 reg; | |
ff36fd85 | 165 | #endif |
550650dd SR |
166 | #if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) |
167 | PPC4xx_SYS_INFO sys_info; | |
dbbd1257 | 168 | #endif |
dbbd1257 SR |
169 | |
170 | /* | |
550650dd SR |
171 | * Programming of the internal divisors is SoC specific. |
172 | * Let's handle this in some #ifdef's for the SoC's. | |
dbbd1257 | 173 | */ |
dbbd1257 | 174 | |
3fb85889 | 175 | #if defined(CONFIG_405GP) |
d1c3b275 | 176 | reg = mfdcr(CPC0_CR0) & ~CR0_MASK; |
6d0f6bcf JCPV |
177 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK |
178 | clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
fe8c2806 WD |
179 | udiv = 1; |
180 | reg |= CR0_EXTCLK_ENA; | |
550650dd | 181 | #else /* CONFIG_SYS_EXT_SERIAL_CLOCK */ |
fe8c2806 | 182 | clk = gd->cpu_clk; |
6d0f6bcf | 183 | #ifdef CONFIG_SYS_405_UART_ERRATA_59 |
fe8c2806 | 184 | udiv = 31; /* Errata 59: stuck at 31 */ |
550650dd | 185 | #else /* CONFIG_SYS_405_UART_ERRATA_59 */ |
32bb34a7 WD |
186 | { |
187 | u32 tmp = CONFIG_SYS_BASE_BAUD * 16; | |
188 | ||
189 | udiv = (clk + tmp / 2) / tmp; | |
190 | } | |
8749cfb4 SR |
191 | if (udiv > UDIV_MAX) /* max. n bits for udiv */ |
192 | udiv = UDIV_MAX; | |
550650dd SR |
193 | #endif /* CONFIG_SYS_405_UART_ERRATA_59 */ |
194 | #endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */ | |
fe8c2806 | 195 | reg |= (udiv - 1) << CR0_UDIV_POS; /* set the UART divisor */ |
d1c3b275 | 196 | mtdcr (CPC0_CR0, reg); |
550650dd SR |
197 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK |
198 | clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
fe8c2806 | 199 | #else |
550650dd | 200 | clk = CONFIG_SYS_BASE_BAUD * 16; |
fe8c2806 | 201 | #endif |
3fb85889 | 202 | #endif |
fe8c2806 | 203 | |
550650dd | 204 | #if defined(CONFIG_405EP) |
32bb34a7 WD |
205 | { |
206 | u32 tmp = CONFIG_SYS_BASE_BAUD * 16; | |
207 | ||
208 | reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK); | |
209 | clk = gd->cpu_clk; | |
210 | udiv = (clk + tmp / 2) / tmp; | |
211 | if (udiv > UDIV_MAX) /* max. n bits for udiv */ | |
212 | udiv = UDIV_MAX; | |
213 | } | |
550650dd SR |
214 | reg |= udiv << UCR0_UDIV_POS; /* set the UART divisor */ |
215 | reg |= udiv << UCR1_UDIV_POS; /* set the UART divisor */ | |
216 | mtdcr(CPC0_UCR, reg); | |
217 | clk = CONFIG_SYS_BASE_BAUD * 16; | |
218 | #endif /* CONFIG_405EP */ | |
fe8c2806 | 219 | |
550650dd SR |
220 | #if defined(CONFIG_405EX) || defined(CONFIG_440) |
221 | MFREG(UART0_SDR, reg); | |
222 | reg &= ~CR0_MASK; | |
fe8c2806 | 223 | |
550650dd SR |
224 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK |
225 | reg |= CR0_EXTCLK_ENA; | |
226 | udiv = 1; | |
227 | clk = CONFIG_SYS_EXT_SERIAL_CLOCK; | |
228 | #else /* CONFIG_SYS_EXT_SERIAL_CLOCK */ | |
229 | clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16; | |
230 | #endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */ | |
fe8c2806 | 231 | |
550650dd | 232 | reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS; /* set the UART divisor */ |
fe8c2806 | 233 | |
fe8c2806 | 234 | /* |
550650dd SR |
235 | * Configure input clock to baudrate generator for all |
236 | * available serial ports here | |
fe8c2806 | 237 | */ |
550650dd SR |
238 | MTREG(UART0_SDR, reg); |
239 | #if defined(UART1_SDR) | |
240 | MTREG(UART1_SDR, reg); | |
068b60a0 | 241 | #endif |
550650dd SR |
242 | #if defined(UART2_SDR) |
243 | MTREG(UART2_SDR, reg); | |
244 | #endif | |
245 | #if defined(UART3_SDR) | |
246 | MTREG(UART3_SDR, reg); | |
247 | #endif | |
248 | #endif /* CONFIG_405EX ... */ | |
fe8c2806 | 249 | |
550650dd SR |
250 | #if defined(CONFIG_405EZ) |
251 | clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16; | |
252 | #endif /* CONFIG_405EZ */ | |
ff36fd85 | 253 | |
550650dd SR |
254 | /* |
255 | * Correct UART frequency in bd-info struct now that | |
256 | * the UART divisor is available | |
257 | */ | |
258 | #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK | |
3a1dc8f1 | 259 | gd->arch.uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK; |
3248f63a | 260 | #else |
550650dd | 261 | get_sys_info(&sys_info); |
3a1dc8f1 | 262 | gd->arch.uart_clk = sys_info.freqUART / udiv; |
550650dd | 263 | #endif |
3248f63a | 264 | |
550650dd | 265 | return clk; |
3248f63a | 266 | } |
3fb85889 | 267 | #endif /* CONFIG_405GP */ |