]> git.sur5r.net Git - u-boot/blob - post/cpu/ppc4xx/uart.c
POST: Add ppc4xx UART POST support without external uart clock (lwmon5)
[u-boot] / 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  * See file CREDITS for list of people who contributed to this
8  * project.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of
13  * the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  * MA 02111-1307 USA
24  */
25
26 #include <common.h>
27
28 /*
29  * UART test
30  *
31  * The controllers are configured to loopback mode and several
32  * characters are transmitted.
33  */
34
35 #ifdef CONFIG_POST
36
37 #include <post.h>
38
39 #if CONFIG_POST & CFG_POST_UART
40
41 #include <asm/processor.h>
42 #include <serial.h>
43
44 #define UART0_BASE  CFG_PERIPHERAL_BASE + 0x00000300
45 #define UART1_BASE  CFG_PERIPHERAL_BASE + 0x00000400
46 #define UART2_BASE  CFG_PERIPHERAL_BASE + 0x00000500
47 #define UART3_BASE  CFG_PERIPHERAL_BASE + 0x00000600
48
49 #define CR0_MASK        0xdfffffff
50 #define CR0_EXTCLK_ENA  0x00800000
51 #define CR0_UDIV_POS    0
52 #define UDIV_SUBTRACT   0
53 #define UART0_SDR       sdr_uart0
54 #define UART1_SDR       sdr_uart1
55 #define UART2_SDR       sdr_uart2
56 #define UART3_SDR       sdr_uart3
57 #define MFREG(a, d)     mfsdr(a, d)
58 #define MTREG(a, d)     mtsdr(a, d)
59
60 #define UART_RBR    0x00
61 #define UART_THR    0x00
62 #define UART_IER    0x01
63 #define UART_IIR    0x02
64 #define UART_FCR    0x02
65 #define UART_LCR    0x03
66 #define UART_MCR    0x04
67 #define UART_LSR    0x05
68 #define UART_MSR    0x06
69 #define UART_SCR    0x07
70 #define UART_DLL    0x00
71 #define UART_DLM    0x01
72
73 /*
74   Line Status Register.
75 */
76 #define asyncLSRDataReady1            0x01
77 #define asyncLSROverrunError1         0x02
78 #define asyncLSRParityError1          0x04
79 #define asyncLSRFramingError1         0x08
80 #define asyncLSRBreakInterrupt1       0x10
81 #define asyncLSRTxHoldEmpty1          0x20
82 #define asyncLSRTxShiftEmpty1         0x40
83 #define asyncLSRRxFifoError1          0x80
84
85 DECLARE_GLOBAL_DATA_PTR;
86
87 #if !defined(CFG_EXT_SERIAL_CLOCK)
88 static void serial_divs (int baudrate, unsigned long *pudiv,
89                          unsigned short *pbdiv)
90 {
91         sys_info_t sysinfo;
92         unsigned long div;              /* total divisor udiv * bdiv */
93         unsigned long umin;             /* minimum udiv */
94         unsigned short diff;            /* smallest diff */
95         unsigned long udiv;             /* best udiv */
96         unsigned short idiff;           /* current diff */
97         unsigned short ibdiv;           /* current bdiv */
98         unsigned long i;
99         unsigned long est;              /* current estimate */
100
101         get_sys_info(&sysinfo);
102
103         udiv = 32;                      /* Assume lowest possible serial clk */
104         div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */
105         umin = sysinfo.pllOpbDiv << 1;  /* 2 x OPB divisor */
106         diff = 32;                      /* highest possible */
107
108         /* i is the test udiv value -- start with the largest
109          * possible (32) to minimize serial clock and constrain
110          * search to umin.
111          */
112         for (i = 32; i > umin; i--) {
113                 ibdiv = div / i;
114                 est = i * ibdiv;
115                 idiff = (est > div) ? (est-div) : (div-est);
116                 if (idiff == 0) {
117                         udiv = i;
118                         break;  /* can't do better */
119                 } else if (idiff < diff) {
120                         udiv = i;       /* best so far */
121                         diff = idiff;   /* update lowest diff*/
122                 }
123         }
124
125         *pudiv = udiv;
126         *pbdiv = div / udiv;
127 }
128 #endif
129
130 static int uart_post_init (unsigned long dev_base)
131 {
132         unsigned long reg;
133         unsigned long udiv;
134         unsigned short bdiv;
135         volatile char val;
136 #ifdef CFG_EXT_SERIAL_CLOCK
137         unsigned long tmp;
138 #endif
139         int i;
140
141         for (i = 0; i < 3500; i++) {
142                 if (in8 (dev_base + UART_LSR) & asyncLSRTxHoldEmpty1)
143                         break;
144                 udelay (100);
145         }
146         MFREG(UART0_SDR, reg);
147         reg &= ~CR0_MASK;
148
149 #ifdef CFG_EXT_SERIAL_CLOCK
150         reg |= CR0_EXTCLK_ENA;
151         udiv = 1;
152         tmp  = gd->baudrate * 16;
153         bdiv = (CFG_EXT_SERIAL_CLOCK + tmp / 2) / tmp;
154 #else
155         /* For 440, the cpu clock is on divider chain A, UART on divider
156          * chain B ... so cpu clock is irrelevant. Get the "optimized"
157          * values that are subject to the 1/2 opb clock constraint
158          */
159         serial_divs (gd->baudrate, &udiv, &bdiv);
160 #endif
161
162         reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS;  /* set the UART divisor */
163
164         /*
165          * Configure input clock to baudrate generator for all
166          * available serial ports here
167          */
168         MTREG(UART0_SDR, reg);
169 #if defined(UART1_SDR)
170         MTREG(UART1_SDR, reg);
171 #endif
172 #if defined(UART2_SDR)
173         MTREG(UART2_SDR, reg);
174 #endif
175 #if defined(UART3_SDR)
176         MTREG(UART3_SDR, reg);
177 #endif
178
179         out8(dev_base + UART_LCR, 0x80);        /* set DLAB bit */
180         out8(dev_base + UART_DLL, bdiv);        /* set baudrate divisor */
181         out8(dev_base + UART_DLM, bdiv >> 8);   /* set baudrate divisor */
182         out8(dev_base + UART_LCR, 0x03);        /* clear DLAB; set 8 bits, no parity */
183         out8(dev_base + UART_FCR, 0x00);        /* disable FIFO */
184         out8(dev_base + UART_MCR, 0x10);        /* enable loopback mode */
185         val = in8(dev_base + UART_LSR);         /* clear line status */
186         val = in8(dev_base + UART_RBR);         /* read receive buffer */
187         out8(dev_base + UART_SCR, 0x00);        /* set scratchpad */
188         out8(dev_base + UART_IER, 0x00);        /* set interrupt enable reg */
189
190         return 0;
191 }
192
193 static void uart_post_putc (unsigned long dev_base, char c)
194 {
195         int i;
196
197         out8 (dev_base + UART_THR, c);  /* put character out */
198
199         /* Wait for transfer completion */
200         for (i = 0; i < 3500; i++) {
201                 if (in8 (dev_base + UART_LSR) & asyncLSRTxHoldEmpty1)
202                         break;
203                 udelay (100);
204         }
205 }
206
207 static int uart_post_getc (unsigned long dev_base)
208 {
209         int i;
210
211         /* Wait for character available */
212         for (i = 0; i < 3500; i++) {
213                 if (in8 (dev_base + UART_LSR) & asyncLSRDataReady1)
214                         break;
215                 udelay (100);
216         }
217         return 0xff & in8 (dev_base + UART_RBR);
218 }
219
220 static int test_ctlr (unsigned long dev_base, int index)
221 {
222         int res = -1;
223         char test_str[] = "*** UART Test String ***\r\n";
224         int i;
225
226         uart_post_init (dev_base);
227
228         for (i = 0; i < sizeof (test_str) - 1; i++) {
229                 uart_post_putc (dev_base, test_str[i]);
230                 if (uart_post_getc (dev_base) != test_str[i])
231                         goto done;
232         }
233         res = 0;
234 done:
235         if (res)
236                 post_log ("uart%d test failed\n", index);
237
238         return res;
239 }
240
241 int uart_post_test (int flags)
242 {
243         int i, res = 0;
244         static unsigned long base[] = {
245                 UART0_BASE, UART1_BASE, UART2_BASE, UART3_BASE
246         };
247
248         for (i = 0; i < sizeof (base) / sizeof (base[0]); i++) {
249                 if (test_ctlr (base[i], i))
250                         res = -1;
251         }
252         serial_reinit_all ();
253
254         return res;
255 }
256
257 #endif /* CONFIG_POST & CFG_POST_UART */
258 #endif /* CONFIG_POST */