]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - post/cpu/ppc4xx/uart.c
Blackfin: otp: fix build after constification of args[]
[karo-tx-uboot.git] / post / cpu / ppc4xx / uart.c
1 /*
2  * (C) Copyright 2007
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * Author: Igor Lisitsin <igor@emcraft.com>
6  *
7  * Copyright 2010, Stefan Roese, DENX Software Engineering, sr@denx.de
8  *
9  * See file CREDITS for list of people who contributed to this
10  * project.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of
15  * the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25  * MA 02111-1307 USA
26  */
27
28 #include <common.h>
29 #include <ppc4xx.h>
30 #include <ns16550.h>
31 #include <asm/io.h>
32
33 /*
34  * UART test
35  *
36  * The controllers are configured to loopback mode and several
37  * characters are transmitted.
38  */
39
40 #include <post.h>
41
42 #if CONFIG_POST & CONFIG_SYS_POST_UART
43
44 /*
45  * This table defines the UART's that should be tested and can
46  * be overridden in the board config file
47  */
48 #ifndef CONFIG_SYS_POST_UART_TABLE
49 #define CONFIG_SYS_POST_UART_TABLE      {UART0_BASE, UART1_BASE, UART2_BASE, UART3_BASE}
50 #endif
51
52 #include <asm/processor.h>
53 #include <serial.h>
54
55 #if defined(CONFIG_440)
56 #if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \
57     defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
58 #define UART0_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000300
59 #define UART1_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000400
60 #define UART2_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000500
61 #define UART3_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000600
62 #else
63 #define UART0_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000200
64 #define UART1_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000300
65 #endif
66
67 #if defined(CONFIG_440SP) || defined(CONFIG_440SPE)
68 #define UART2_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000600
69 #endif
70
71 #if defined(CONFIG_440GP)
72 #define CR0_MASK        0x3fff0000
73 #define CR0_EXTCLK_ENA  0x00600000
74 #define CR0_UDIV_POS    16
75 #define UDIV_SUBTRACT   1
76 #define UART0_SDR       CPC0_CR0
77 #define MFREG(a, d)     d = mfdcr(a)
78 #define MTREG(a, d)     mtdcr(a, d)
79 #else /* #if defined(CONFIG_440GP) */
80 /* all other 440 PPC's access clock divider via sdr register */
81 #define CR0_MASK        0xdfffffff
82 #define CR0_EXTCLK_ENA  0x00800000
83 #define CR0_UDIV_POS    0
84 #define UDIV_SUBTRACT   0
85 #define UART0_SDR       SDR0_UART0
86 #define UART1_SDR       SDR0_UART1
87 #if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
88     defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
89     defined(CONFIG_440SP) || defined(CONFIG_440SPE)
90 #define UART2_SDR       SDR0_UART2
91 #endif
92 #if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
93     defined(CONFIG_440GR) || defined(CONFIG_440GRX)
94 #define UART3_SDR       SDR0_UART3
95 #endif
96 #define MFREG(a, d)     mfsdr(a, d)
97 #define MTREG(a, d)     mtsdr(a, d)
98 #endif /* #if defined(CONFIG_440GP) */
99 #elif defined(CONFIG_405EP) || defined(CONFIG_405EZ)
100 #define UART0_BASE      0xef600300
101 #define UART1_BASE      0xef600400
102 #define UCR0_MASK       0x0000007f
103 #define UCR1_MASK       0x00007f00
104 #define UCR0_UDIV_POS   0
105 #define UCR1_UDIV_POS   8
106 #define UDIV_MAX        127
107 #elif defined(CONFIG_405EX)
108 #define UART0_BASE      0xef600200
109 #define UART1_BASE      0xef600300
110 #define CR0_MASK        0x000000ff
111 #define CR0_EXTCLK_ENA  0x00800000
112 #define CR0_UDIV_POS    0
113 #define UDIV_SUBTRACT   0
114 #define UART0_SDR       SDR0_UART0
115 #define UART1_SDR       SDR0_UART1
116 #define MFREG(a, d)     mfsdr(a, d)
117 #define MTREG(a, d)     mtsdr(a, d)
118 #else /* CONFIG_405GP || CONFIG_405CR */
119 #define UART0_BASE      0xef600300
120 #define UART1_BASE      0xef600400
121 #define CR0_MASK        0x00001fff
122 #define CR0_EXTCLK_ENA  0x000000c0
123 #define CR0_UDIV_POS    1
124 #define UDIV_MAX        32
125 #endif
126
127 DECLARE_GLOBAL_DATA_PTR;
128
129 static void uart_post_init_common(struct NS16550 *com_port, unsigned short bdiv)
130 {
131         volatile char val;
132
133         out_8(&com_port->lcr, 0x80);    /* set DLAB bit */
134         out_8(&com_port->dll, bdiv);    /* set baudrate divisor */
135         out_8(&com_port->dlm, bdiv >> 8); /* set baudrate divisor */
136         out_8(&com_port->lcr, 0x03);    /* clear DLAB; set 8 bits, no parity */
137         out_8(&com_port->fcr, 0x00);    /* disable FIFO */
138         out_8(&com_port->mcr, 0x10);    /* enable loopback mode */
139         val = in_8(&com_port->lsr);     /* clear line status */
140         val = in_8(&com_port->rbr);     /* read receive buffer */
141         out_8(&com_port->scr, 0x00);    /* set scratchpad */
142         out_8(&com_port->ier, 0x00);    /* set interrupt enable reg */
143 }
144
145 #if defined(CONFIG_440) || defined(CONFIG_405EX)
146 #if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
147 static void serial_divs (int baudrate, unsigned long *pudiv,
148                          unsigned short *pbdiv)
149 {
150         sys_info_t sysinfo;
151         unsigned long div;              /* total divisor udiv * bdiv */
152         unsigned long umin;             /* minimum udiv */
153         unsigned short diff;            /* smallest diff */
154         unsigned long udiv;             /* best udiv */
155         unsigned short idiff;           /* current diff */
156         unsigned short ibdiv;           /* current bdiv */
157         unsigned long i;
158         unsigned long est;              /* current estimate */
159
160         get_sys_info(&sysinfo);
161
162         udiv = 32;                      /* Assume lowest possible serial clk */
163         div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */
164         umin = sysinfo.pllOpbDiv << 1;  /* 2 x OPB divisor */
165         diff = 32;                      /* highest possible */
166
167         /* i is the test udiv value -- start with the largest
168          * possible (32) to minimize serial clock and constrain
169          * search to umin.
170          */
171         for (i = 32; i > umin; i--) {
172                 ibdiv = div / i;
173                 est = i * ibdiv;
174                 idiff = (est > div) ? (est-div) : (div-est);
175                 if (idiff == 0) {
176                         udiv = i;
177                         break;  /* can't do better */
178                 } else if (idiff < diff) {
179                         udiv = i;       /* best so far */
180                         diff = idiff;   /* update lowest diff*/
181                 }
182         }
183
184         *pudiv = udiv;
185         *pbdiv = div / udiv;
186 }
187 #endif
188
189 static int uart_post_init (struct NS16550 *com_port)
190 {
191         unsigned long reg = 0;
192         unsigned long udiv;
193         unsigned short bdiv;
194 #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
195         unsigned long tmp;
196 #endif
197         int i;
198
199         for (i = 0; i < 3500; i++) {
200                 if (in_8(&com_port->lsr) & UART_LSR_THRE)
201                         break;
202                 udelay (100);
203         }
204         MFREG(UART0_SDR, reg);
205         reg &= ~CR0_MASK;
206
207 #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
208         reg |= CR0_EXTCLK_ENA;
209         udiv = 1;
210         tmp  = gd->baudrate * 16;
211         bdiv = (CONFIG_SYS_EXT_SERIAL_CLOCK + tmp / 2) / tmp;
212 #else
213         /* For 440, the cpu clock is on divider chain A, UART on divider
214          * chain B ... so cpu clock is irrelevant. Get the "optimized"
215          * values that are subject to the 1/2 opb clock constraint
216          */
217         serial_divs (gd->baudrate, &udiv, &bdiv);
218 #endif
219
220         reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS;  /* set the UART divisor */
221
222         /*
223          * Configure input clock to baudrate generator for all
224          * available serial ports here
225          */
226         MTREG(UART0_SDR, reg);
227 #if defined(UART1_SDR)
228         MTREG(UART1_SDR, reg);
229 #endif
230 #if defined(UART2_SDR)
231         MTREG(UART2_SDR, reg);
232 #endif
233 #if defined(UART3_SDR)
234         MTREG(UART3_SDR, reg);
235 #endif
236
237         uart_post_init_common(com_port, bdiv);
238
239         return 0;
240 }
241
242 #else /* CONFIG_440 */
243
244 static int uart_post_init (struct NS16550 *com_port)
245 {
246         unsigned long reg;
247         unsigned long tmp;
248         unsigned long clk;
249         unsigned long udiv;
250         unsigned short bdiv;
251         int i;
252
253         for (i = 0; i < 3500; i++) {
254                 if (in_8(&com_port->lsr) & UART_LSR_THRE)
255                         break;
256                 udelay (100);
257         }
258
259 #if defined(CONFIG_405EZ)
260         serial_divs(gd->baudrate, &udiv, &bdiv);
261         clk = tmp = reg = 0;
262 #else
263 #ifdef CONFIG_405EP
264         reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK);
265         clk = gd->cpu_clk;
266         tmp = CONFIG_SYS_BASE_BAUD * 16;
267         udiv = (clk + tmp / 2) / tmp;
268         if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
269                 udiv = UDIV_MAX;
270         reg |= (udiv) << UCR0_UDIV_POS;         /* set the UART divisor */
271         reg |= (udiv) << UCR1_UDIV_POS;         /* set the UART divisor */
272         mtdcr (CPC0_UCR, reg);
273 #else /* CONFIG_405EP */
274         reg = mfdcr(CPC0_CR0) & ~CR0_MASK;
275 #ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
276         clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
277         udiv = 1;
278         reg |= CR0_EXTCLK_ENA;
279 #else
280         clk = gd->cpu_clk;
281 #ifdef CONFIG_SYS_405_UART_ERRATA_59
282         udiv = 31;                      /* Errata 59: stuck at 31 */
283 #else
284         tmp = CONFIG_SYS_BASE_BAUD * 16;
285         udiv = (clk + tmp / 2) / tmp;
286         if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
287                 udiv = UDIV_MAX;
288 #endif
289 #endif
290         reg |= (udiv - 1) << CR0_UDIV_POS;      /* set the UART divisor */
291         mtdcr (CPC0_CR0, reg);
292 #endif /* CONFIG_405EP */
293         tmp = gd->baudrate * udiv * 16;
294         bdiv = (clk + tmp / 2) / tmp;
295 #endif /* CONFIG_405EZ */
296
297         uart_post_init_common(com_port, bdiv);
298
299         return 0;
300 }
301 #endif /* CONFIG_440 */
302
303 static void uart_post_putc (struct NS16550 *com_port, char c)
304 {
305         int i;
306
307         out_8(&com_port->thr, c);       /* put character out */
308
309         /* Wait for transfer completion */
310         for (i = 0; i < 3500; i++) {
311                 if (in_8(&com_port->lsr) & UART_LSR_THRE)
312                         break;
313                 udelay (100);
314         }
315 }
316
317 static int uart_post_getc (struct NS16550 *com_port)
318 {
319         int i;
320
321         /* Wait for character available */
322         for (i = 0; i < 3500; i++) {
323                 if (in_8(&com_port->lsr) & UART_LSR_DR)
324                         break;
325                 udelay (100);
326         }
327
328         return 0xff & in_8(&com_port->rbr);
329 }
330
331 static int test_ctlr (struct NS16550 *com_port, int index)
332 {
333         int res = -1;
334         char test_str[] = "*** UART Test String ***\r\n";
335         int i;
336
337         uart_post_init (com_port);
338
339         for (i = 0; i < sizeof (test_str) - 1; i++) {
340                 uart_post_putc (com_port, test_str[i]);
341                 if (uart_post_getc (com_port) != test_str[i])
342                         goto done;
343         }
344         res = 0;
345 done:
346         if (res)
347                 post_log ("uart%d test failed\n", index);
348
349         return res;
350 }
351
352 int uart_post_test (int flags)
353 {
354         int i, res = 0;
355         static unsigned long base[] = CONFIG_SYS_POST_UART_TABLE;
356
357         for (i = 0; i < ARRAY_SIZE(base); i++) {
358                 if (test_ctlr((struct NS16550 *)base[i], i))
359                         res = -1;
360         }
361         serial_reinit_all ();
362
363         return res;
364 }
365
366 #endif /* CONFIG_POST & CONFIG_SYS_POST_UART */