xref: /openbmc/u-boot/drivers/serial/serial_pl01x.c (revision 20c9226cb8cab08a111ee73db04e62d943ee0c97)
1*20c9226cSAndreas Engel /*
2*20c9226cSAndreas Engel  * (C) Copyright 2000
3*20c9226cSAndreas Engel  * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
4*20c9226cSAndreas Engel  *
5*20c9226cSAndreas Engel  * (C) Copyright 2004
6*20c9226cSAndreas Engel  * ARM Ltd.
7*20c9226cSAndreas Engel  * Philippe Robin, <philippe.robin@arm.com>
8*20c9226cSAndreas Engel  *
9*20c9226cSAndreas Engel  * See file CREDITS for list of people who contributed to this
10*20c9226cSAndreas Engel  * project.
11*20c9226cSAndreas Engel  *
12*20c9226cSAndreas Engel  * This program is free software; you can redistribute it and/or
13*20c9226cSAndreas Engel  * modify it under the terms of the GNU General Public License as
14*20c9226cSAndreas Engel  * published by the Free Software Foundation; either version 2 of
15*20c9226cSAndreas Engel  * the License, or (at your option) any later version.
16*20c9226cSAndreas Engel  *
17*20c9226cSAndreas Engel  * This program is distributed in the hope that it will be useful,
18*20c9226cSAndreas Engel  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19*20c9226cSAndreas Engel  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20*20c9226cSAndreas Engel  * GNU General Public License for more details.
21*20c9226cSAndreas Engel  *
22*20c9226cSAndreas Engel  * You should have received a copy of the GNU General Public License
23*20c9226cSAndreas Engel  * along with this program; if not, write to the Free Software
24*20c9226cSAndreas Engel  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25*20c9226cSAndreas Engel  * MA 02111-1307 USA
26*20c9226cSAndreas Engel  */
27*20c9226cSAndreas Engel 
28*20c9226cSAndreas Engel /* Simple U-Boot driver for the PrimeCell PL011 UARTs on the IntegratorCP */
29*20c9226cSAndreas Engel /* Should be fairly simple to make it work with the PL010 as well */
30*20c9226cSAndreas Engel 
31*20c9226cSAndreas Engel #include <common.h>
32*20c9226cSAndreas Engel #include <watchdog.h>
33*20c9226cSAndreas Engel 
34*20c9226cSAndreas Engel #if defined(CFG_PL010_SERIAL) || defined(CFG_PL011_SERIAL)
35*20c9226cSAndreas Engel 
36*20c9226cSAndreas Engel #include "serial_pl01x.h"
37*20c9226cSAndreas Engel 
38*20c9226cSAndreas Engel #define IO_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (val))
39*20c9226cSAndreas Engel #define IO_READ(addr) (*(volatile unsigned int *)(addr))
40*20c9226cSAndreas Engel 
41*20c9226cSAndreas Engel /*
42*20c9226cSAndreas Engel  * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1
43*20c9226cSAndreas Engel  * Integrator CP has two UARTs, use the first one, at 38400-8-N-1
44*20c9226cSAndreas Engel  * Versatile PB has four UARTs.
45*20c9226cSAndreas Engel  */
46*20c9226cSAndreas Engel #define CONSOLE_PORT CONFIG_CONS_INDEX
47*20c9226cSAndreas Engel #define baudRate CONFIG_BAUDRATE
48*20c9226cSAndreas Engel static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS;
49*20c9226cSAndreas Engel #define NUM_PORTS (sizeof(port)/sizeof(port[0]))
50*20c9226cSAndreas Engel 
51*20c9226cSAndreas Engel static void pl01x_putc (int portnum, char c);
52*20c9226cSAndreas Engel static int pl01x_getc (int portnum);
53*20c9226cSAndreas Engel static int pl01x_tstc (int portnum);
54*20c9226cSAndreas Engel 
55*20c9226cSAndreas Engel #ifdef CFG_PL010_SERIAL
56*20c9226cSAndreas Engel 
57*20c9226cSAndreas Engel int serial_init (void)
58*20c9226cSAndreas Engel {
59*20c9226cSAndreas Engel 	unsigned int divisor;
60*20c9226cSAndreas Engel 
61*20c9226cSAndreas Engel 	/*
62*20c9226cSAndreas Engel 	 ** First, disable everything.
63*20c9226cSAndreas Engel 	 */
64*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL010_CR, 0x0);
65*20c9226cSAndreas Engel 
66*20c9226cSAndreas Engel 	/*
67*20c9226cSAndreas Engel 	 ** Set baud rate
68*20c9226cSAndreas Engel 	 **
69*20c9226cSAndreas Engel 	 */
70*20c9226cSAndreas Engel 	switch (baudRate) {
71*20c9226cSAndreas Engel 	case 9600:
72*20c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
73*20c9226cSAndreas Engel 		break;
74*20c9226cSAndreas Engel 
75*20c9226cSAndreas Engel 	case 19200:
76*20c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
77*20c9226cSAndreas Engel 		break;
78*20c9226cSAndreas Engel 
79*20c9226cSAndreas Engel 	case 38400:
80*20c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
81*20c9226cSAndreas Engel 		break;
82*20c9226cSAndreas Engel 
83*20c9226cSAndreas Engel 	case 57600:
84*20c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_57600;
85*20c9226cSAndreas Engel 		break;
86*20c9226cSAndreas Engel 
87*20c9226cSAndreas Engel 	case 115200:
88*20c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_115200;
89*20c9226cSAndreas Engel 		break;
90*20c9226cSAndreas Engel 
91*20c9226cSAndreas Engel 	default:
92*20c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
93*20c9226cSAndreas Engel 	}
94*20c9226cSAndreas Engel 
95*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL010_LCRM,
96*20c9226cSAndreas Engel 		  ((divisor & 0xf00) >> 8));
97*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL010_LCRL, (divisor & 0xff));
98*20c9226cSAndreas Engel 
99*20c9226cSAndreas Engel 	/*
100*20c9226cSAndreas Engel 	 ** Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled.
101*20c9226cSAndreas Engel 	 */
102*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL010_LCRH,
103*20c9226cSAndreas Engel 		  (UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN));
104*20c9226cSAndreas Engel 
105*20c9226cSAndreas Engel 	/*
106*20c9226cSAndreas Engel 	 ** Finally, enable the UART
107*20c9226cSAndreas Engel 	 */
108*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL010_CR, (UART_PL010_CR_UARTEN));
109*20c9226cSAndreas Engel 
110*20c9226cSAndreas Engel 	return 0;
111*20c9226cSAndreas Engel }
112*20c9226cSAndreas Engel 
113*20c9226cSAndreas Engel #endif /* CFG_PL010_SERIAL */
114*20c9226cSAndreas Engel 
115*20c9226cSAndreas Engel #ifdef CFG_PL011_SERIAL
116*20c9226cSAndreas Engel 
117*20c9226cSAndreas Engel int serial_init (void)
118*20c9226cSAndreas Engel {
119*20c9226cSAndreas Engel 	unsigned int temp;
120*20c9226cSAndreas Engel 	unsigned int divider;
121*20c9226cSAndreas Engel 	unsigned int remainder;
122*20c9226cSAndreas Engel 	unsigned int fraction;
123*20c9226cSAndreas Engel 
124*20c9226cSAndreas Engel 	/*
125*20c9226cSAndreas Engel 	 ** First, disable everything.
126*20c9226cSAndreas Engel 	 */
127*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL011_CR, 0x0);
128*20c9226cSAndreas Engel 
129*20c9226cSAndreas Engel 	/*
130*20c9226cSAndreas Engel 	 ** Set baud rate
131*20c9226cSAndreas Engel 	 **
132*20c9226cSAndreas Engel 	 ** IBRD = UART_CLK / (16 * BAUD_RATE)
133*20c9226cSAndreas Engel 	 ** FBRD = ROUND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE))
134*20c9226cSAndreas Engel 	 */
135*20c9226cSAndreas Engel 	temp = 16 * baudRate;
136*20c9226cSAndreas Engel 	divider = CONFIG_PL011_CLOCK / temp;
137*20c9226cSAndreas Engel 	remainder = CONFIG_PL011_CLOCK % temp;
138*20c9226cSAndreas Engel 	temp = (8 * remainder) / baudRate;
139*20c9226cSAndreas Engel 	fraction = (temp >> 1) + (temp & 1);
140*20c9226cSAndreas Engel 
141*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL011_IBRD, divider);
142*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL011_FBRD, fraction);
143*20c9226cSAndreas Engel 
144*20c9226cSAndreas Engel 	/*
145*20c9226cSAndreas Engel 	 ** Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled.
146*20c9226cSAndreas Engel 	 */
147*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL011_LCRH,
148*20c9226cSAndreas Engel 		  (UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN));
149*20c9226cSAndreas Engel 
150*20c9226cSAndreas Engel 	/*
151*20c9226cSAndreas Engel 	 ** Finally, enable the UART
152*20c9226cSAndreas Engel 	 */
153*20c9226cSAndreas Engel 	IO_WRITE (port[CONSOLE_PORT] + UART_PL011_CR,
154*20c9226cSAndreas Engel 		  (UART_PL011_CR_UARTEN | UART_PL011_CR_TXE |
155*20c9226cSAndreas Engel 		   UART_PL011_CR_RXE));
156*20c9226cSAndreas Engel 
157*20c9226cSAndreas Engel 	return 0;
158*20c9226cSAndreas Engel }
159*20c9226cSAndreas Engel 
160*20c9226cSAndreas Engel #endif /* CFG_PL011_SERIAL */
161*20c9226cSAndreas Engel 
162*20c9226cSAndreas Engel void serial_putc (const char c)
163*20c9226cSAndreas Engel {
164*20c9226cSAndreas Engel 	if (c == '\n')
165*20c9226cSAndreas Engel 		pl01x_putc (CONSOLE_PORT, '\r');
166*20c9226cSAndreas Engel 
167*20c9226cSAndreas Engel 	pl01x_putc (CONSOLE_PORT, c);
168*20c9226cSAndreas Engel }
169*20c9226cSAndreas Engel 
170*20c9226cSAndreas Engel void serial_puts (const char *s)
171*20c9226cSAndreas Engel {
172*20c9226cSAndreas Engel 	while (*s) {
173*20c9226cSAndreas Engel 		serial_putc (*s++);
174*20c9226cSAndreas Engel 	}
175*20c9226cSAndreas Engel }
176*20c9226cSAndreas Engel 
177*20c9226cSAndreas Engel int serial_getc (void)
178*20c9226cSAndreas Engel {
179*20c9226cSAndreas Engel 	return pl01x_getc (CONSOLE_PORT);
180*20c9226cSAndreas Engel }
181*20c9226cSAndreas Engel 
182*20c9226cSAndreas Engel int serial_tstc (void)
183*20c9226cSAndreas Engel {
184*20c9226cSAndreas Engel 	return pl01x_tstc (CONSOLE_PORT);
185*20c9226cSAndreas Engel }
186*20c9226cSAndreas Engel 
187*20c9226cSAndreas Engel void serial_setbrg (void)
188*20c9226cSAndreas Engel {
189*20c9226cSAndreas Engel }
190*20c9226cSAndreas Engel 
191*20c9226cSAndreas Engel static void pl01x_putc (int portnum, char c)
192*20c9226cSAndreas Engel {
193*20c9226cSAndreas Engel 	/* Wait until there is space in the FIFO */
194*20c9226cSAndreas Engel 	while (IO_READ (port[portnum] + UART_PL01x_FR) & UART_PL01x_FR_TXFF)
195*20c9226cSAndreas Engel 		WATCHDOG_RESET();
196*20c9226cSAndreas Engel 
197*20c9226cSAndreas Engel 	/* Send the character */
198*20c9226cSAndreas Engel 	IO_WRITE (port[portnum] + UART_PL01x_DR, c);
199*20c9226cSAndreas Engel }
200*20c9226cSAndreas Engel 
201*20c9226cSAndreas Engel static int pl01x_getc (int portnum)
202*20c9226cSAndreas Engel {
203*20c9226cSAndreas Engel 	unsigned int data;
204*20c9226cSAndreas Engel 
205*20c9226cSAndreas Engel 	/* Wait until there is data in the FIFO */
206*20c9226cSAndreas Engel 	while (IO_READ (port[portnum] + UART_PL01x_FR) & UART_PL01x_FR_RXFE)
207*20c9226cSAndreas Engel 		WATCHDOG_RESET();
208*20c9226cSAndreas Engel 
209*20c9226cSAndreas Engel 	data = IO_READ (port[portnum] + UART_PL01x_DR);
210*20c9226cSAndreas Engel 
211*20c9226cSAndreas Engel 	/* Check for an error flag */
212*20c9226cSAndreas Engel 	if (data & 0xFFFFFF00) {
213*20c9226cSAndreas Engel 		/* Clear the error */
214*20c9226cSAndreas Engel 		IO_WRITE (port[portnum] + UART_PL01x_ECR, 0xFFFFFFFF);
215*20c9226cSAndreas Engel 		return -1;
216*20c9226cSAndreas Engel 	}
217*20c9226cSAndreas Engel 
218*20c9226cSAndreas Engel 	return (int) data;
219*20c9226cSAndreas Engel }
220*20c9226cSAndreas Engel 
221*20c9226cSAndreas Engel static int pl01x_tstc (int portnum)
222*20c9226cSAndreas Engel {
223*20c9226cSAndreas Engel 	WATCHDOG_RESET();
224*20c9226cSAndreas Engel 	return !(IO_READ (port[portnum] + UART_PL01x_FR) &
225*20c9226cSAndreas Engel 		 UART_PL01x_FR_RXFE);
226*20c9226cSAndreas Engel }
227*20c9226cSAndreas Engel 
228*20c9226cSAndreas Engel #endif
229