xref: /openbmc/u-boot/drivers/serial/serial_pl01x.c (revision 249d5219c4cf0a6ec423deb6e0eb8a77b3a405fa)
120c9226cSAndreas Engel /*
220c9226cSAndreas Engel  * (C) Copyright 2000
320c9226cSAndreas Engel  * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
420c9226cSAndreas Engel  *
520c9226cSAndreas Engel  * (C) Copyright 2004
620c9226cSAndreas Engel  * ARM Ltd.
720c9226cSAndreas Engel  * Philippe Robin, <philippe.robin@arm.com>
820c9226cSAndreas Engel  *
920c9226cSAndreas Engel  * See file CREDITS for list of people who contributed to this
1020c9226cSAndreas Engel  * project.
1120c9226cSAndreas Engel  *
1220c9226cSAndreas Engel  * This program is free software; you can redistribute it and/or
1320c9226cSAndreas Engel  * modify it under the terms of the GNU General Public License as
1420c9226cSAndreas Engel  * published by the Free Software Foundation; either version 2 of
1520c9226cSAndreas Engel  * the License, or (at your option) any later version.
1620c9226cSAndreas Engel  *
1720c9226cSAndreas Engel  * This program is distributed in the hope that it will be useful,
1820c9226cSAndreas Engel  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1920c9226cSAndreas Engel  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2020c9226cSAndreas Engel  * GNU General Public License for more details.
2120c9226cSAndreas Engel  *
2220c9226cSAndreas Engel  * You should have received a copy of the GNU General Public License
2320c9226cSAndreas Engel  * along with this program; if not, write to the Free Software
2420c9226cSAndreas Engel  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
2520c9226cSAndreas Engel  * MA 02111-1307 USA
2620c9226cSAndreas Engel  */
2720c9226cSAndreas Engel 
2848d0192fSAndreas Engel /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */
2920c9226cSAndreas Engel 
3020c9226cSAndreas Engel #include <common.h>
3120c9226cSAndreas Engel #include <watchdog.h>
32*249d5219SMatt Waddel #include <asm/io.h>
3320c9226cSAndreas Engel #include "serial_pl01x.h"
3420c9226cSAndreas Engel 
3520c9226cSAndreas Engel /*
3620c9226cSAndreas Engel  * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1
3720c9226cSAndreas Engel  * Integrator CP has two UARTs, use the first one, at 38400-8-N-1
3820c9226cSAndreas Engel  * Versatile PB has four UARTs.
3920c9226cSAndreas Engel  */
4020c9226cSAndreas Engel #define CONSOLE_PORT CONFIG_CONS_INDEX
4120c9226cSAndreas Engel static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS;
4220c9226cSAndreas Engel #define NUM_PORTS (sizeof(port)/sizeof(port[0]))
4320c9226cSAndreas Engel 
4420c9226cSAndreas Engel static void pl01x_putc (int portnum, char c);
4520c9226cSAndreas Engel static int pl01x_getc (int portnum);
4620c9226cSAndreas Engel static int pl01x_tstc (int portnum);
47*249d5219SMatt Waddel unsigned int baudrate = CONFIG_BAUDRATE;
48*249d5219SMatt Waddel DECLARE_GLOBAL_DATA_PTR;
4920c9226cSAndreas Engel 
5048d0192fSAndreas Engel #ifdef CONFIG_PL010_SERIAL
5120c9226cSAndreas Engel 
5220c9226cSAndreas Engel int serial_init (void)
5320c9226cSAndreas Engel {
5420c9226cSAndreas Engel 	unsigned int divisor;
5520c9226cSAndreas Engel 
56*249d5219SMatt Waddel 	/* First, disable everything */
57*249d5219SMatt Waddel 	writel(0x0, port[CONSOLE_PORT] + UART_PL010_CR);
5820c9226cSAndreas Engel 
59*249d5219SMatt Waddel 	/* Set baud rate */
60*249d5219SMatt Waddel 	switch (baudrate) {
6120c9226cSAndreas Engel 	case 9600:
6220c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
6320c9226cSAndreas Engel 		break;
6420c9226cSAndreas Engel 
6520c9226cSAndreas Engel 	case 19200:
6620c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
6720c9226cSAndreas Engel 		break;
6820c9226cSAndreas Engel 
6920c9226cSAndreas Engel 	case 38400:
7020c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
7120c9226cSAndreas Engel 		break;
7220c9226cSAndreas Engel 
7320c9226cSAndreas Engel 	case 57600:
7420c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_57600;
7520c9226cSAndreas Engel 		break;
7620c9226cSAndreas Engel 
7720c9226cSAndreas Engel 	case 115200:
7820c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_115200;
7920c9226cSAndreas Engel 		break;
8020c9226cSAndreas Engel 
8120c9226cSAndreas Engel 	default:
8220c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
8320c9226cSAndreas Engel 	}
8420c9226cSAndreas Engel 
85*249d5219SMatt Waddel 	writel(((divisor & 0xf00) >> 8), port[CONSOLE_PORT] + UART_PL010_LCRM);
86*249d5219SMatt Waddel 	writel((divisor & 0xff), port[CONSOLE_PORT] + UART_PL010_LCRL);
8720c9226cSAndreas Engel 
88*249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
89*249d5219SMatt Waddel 	writel((UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN),
90*249d5219SMatt Waddel 		port[CONSOLE_PORT] + UART_PL010_LCRH);
9120c9226cSAndreas Engel 
92*249d5219SMatt Waddel 	/* Finally, enable the UART */
93*249d5219SMatt Waddel 	writel((UART_PL010_CR_UARTEN), port[CONSOLE_PORT] + UART_PL010_CR);
9420c9226cSAndreas Engel 
9520c9226cSAndreas Engel 	return 0;
9620c9226cSAndreas Engel }
9720c9226cSAndreas Engel 
9848d0192fSAndreas Engel #endif /* CONFIG_PL010_SERIAL */
9920c9226cSAndreas Engel 
10048d0192fSAndreas Engel #ifdef CONFIG_PL011_SERIAL
10120c9226cSAndreas Engel 
10220c9226cSAndreas Engel int serial_init (void)
10320c9226cSAndreas Engel {
10420c9226cSAndreas Engel 	unsigned int temp;
10520c9226cSAndreas Engel 	unsigned int divider;
10620c9226cSAndreas Engel 	unsigned int remainder;
10720c9226cSAndreas Engel 	unsigned int fraction;
10820c9226cSAndreas Engel 
109*249d5219SMatt Waddel 	/* First, disable everything */
110*249d5219SMatt Waddel 	writel(0x0, port[CONSOLE_PORT] + UART_PL011_CR);
11120c9226cSAndreas Engel 
11220c9226cSAndreas Engel 	/*
113*249d5219SMatt Waddel 	 * Set baud rate
114*249d5219SMatt Waddel 	 *
115*249d5219SMatt Waddel 	 * IBRD = UART_CLK / (16 * BAUD_RATE)
116*249d5219SMatt Waddel 	 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE))
11720c9226cSAndreas Engel 	 */
118*249d5219SMatt Waddel 	temp = 16 * baudrate;
11920c9226cSAndreas Engel 	divider = CONFIG_PL011_CLOCK / temp;
12020c9226cSAndreas Engel 	remainder = CONFIG_PL011_CLOCK % temp;
121*249d5219SMatt Waddel 	temp = (8 * remainder) / baudrate;
12220c9226cSAndreas Engel 	fraction = (temp >> 1) + (temp & 1);
12320c9226cSAndreas Engel 
124*249d5219SMatt Waddel 	writel(divider, port[CONSOLE_PORT] + UART_PL011_IBRD);
125*249d5219SMatt Waddel 	writel(fraction, port[CONSOLE_PORT] + UART_PL011_FBRD);
12620c9226cSAndreas Engel 
127*249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
128*249d5219SMatt Waddel 	writel((UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN),
129*249d5219SMatt Waddel 		port[CONSOLE_PORT] + UART_PL011_LCRH);
13020c9226cSAndreas Engel 
131*249d5219SMatt Waddel 	/* Finally, enable the UART */
132*249d5219SMatt Waddel 	writel((UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE),
133*249d5219SMatt Waddel 		port[CONSOLE_PORT] + UART_PL011_CR);
13420c9226cSAndreas Engel 
13520c9226cSAndreas Engel 	return 0;
13620c9226cSAndreas Engel }
13720c9226cSAndreas Engel 
13848d0192fSAndreas Engel #endif /* CONFIG_PL011_SERIAL */
13920c9226cSAndreas Engel 
14020c9226cSAndreas Engel void serial_putc (const char c)
14120c9226cSAndreas Engel {
14220c9226cSAndreas Engel 	if (c == '\n')
14320c9226cSAndreas Engel 		pl01x_putc (CONSOLE_PORT, '\r');
14420c9226cSAndreas Engel 
14520c9226cSAndreas Engel 	pl01x_putc (CONSOLE_PORT, c);
14620c9226cSAndreas Engel }
14720c9226cSAndreas Engel 
14820c9226cSAndreas Engel void serial_puts (const char *s)
14920c9226cSAndreas Engel {
15020c9226cSAndreas Engel 	while (*s) {
15120c9226cSAndreas Engel 		serial_putc (*s++);
15220c9226cSAndreas Engel 	}
15320c9226cSAndreas Engel }
15420c9226cSAndreas Engel 
15520c9226cSAndreas Engel int serial_getc (void)
15620c9226cSAndreas Engel {
15720c9226cSAndreas Engel 	return pl01x_getc (CONSOLE_PORT);
15820c9226cSAndreas Engel }
15920c9226cSAndreas Engel 
16020c9226cSAndreas Engel int serial_tstc (void)
16120c9226cSAndreas Engel {
16220c9226cSAndreas Engel 	return pl01x_tstc (CONSOLE_PORT);
16320c9226cSAndreas Engel }
16420c9226cSAndreas Engel 
16520c9226cSAndreas Engel void serial_setbrg (void)
16620c9226cSAndreas Engel {
167*249d5219SMatt Waddel 	baudrate = gd->baudrate;
168*249d5219SMatt Waddel 	serial_init();
16920c9226cSAndreas Engel }
17020c9226cSAndreas Engel 
17120c9226cSAndreas Engel static void pl01x_putc (int portnum, char c)
17220c9226cSAndreas Engel {
17320c9226cSAndreas Engel 	/* Wait until there is space in the FIFO */
174*249d5219SMatt Waddel 	while (readl(port[portnum] + UART_PL01x_FR) & UART_PL01x_FR_TXFF)
17520c9226cSAndreas Engel 		WATCHDOG_RESET();
17620c9226cSAndreas Engel 
17720c9226cSAndreas Engel 	/* Send the character */
178*249d5219SMatt Waddel 	writel(c, port[portnum] + UART_PL01x_DR);
17920c9226cSAndreas Engel }
18020c9226cSAndreas Engel 
18120c9226cSAndreas Engel static int pl01x_getc (int portnum)
18220c9226cSAndreas Engel {
18320c9226cSAndreas Engel 	unsigned int data;
18420c9226cSAndreas Engel 
18520c9226cSAndreas Engel 	/* Wait until there is data in the FIFO */
186*249d5219SMatt Waddel 	while (readl(port[portnum] + UART_PL01x_FR) & UART_PL01x_FR_RXFE)
18720c9226cSAndreas Engel 		WATCHDOG_RESET();
18820c9226cSAndreas Engel 
189*249d5219SMatt Waddel 	data = readl(port[portnum] + UART_PL01x_DR);
19020c9226cSAndreas Engel 
19120c9226cSAndreas Engel 	/* Check for an error flag */
19220c9226cSAndreas Engel 	if (data & 0xFFFFFF00) {
19320c9226cSAndreas Engel 		/* Clear the error */
194*249d5219SMatt Waddel 		writel(0xFFFFFFFF, port[portnum] + UART_PL01x_ECR);
19520c9226cSAndreas Engel 		return -1;
19620c9226cSAndreas Engel 	}
19720c9226cSAndreas Engel 
19820c9226cSAndreas Engel 	return (int) data;
19920c9226cSAndreas Engel }
20020c9226cSAndreas Engel 
20120c9226cSAndreas Engel static int pl01x_tstc (int portnum)
20220c9226cSAndreas Engel {
20320c9226cSAndreas Engel 	WATCHDOG_RESET();
204*249d5219SMatt Waddel 	return !(readl(port[portnum] + UART_PL01x_FR) &
20520c9226cSAndreas Engel 		 UART_PL01x_FR_RXFE);
20620c9226cSAndreas Engel }
207