xref: /openbmc/u-boot/drivers/serial/serial_pl01x.c (revision 96baa4c37695b4dac2bde981f59c38e7af19f2eb)
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>
32249d5219SMatt 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);
47249d5219SMatt Waddel unsigned int baudrate = CONFIG_BAUDRATE;
48249d5219SMatt Waddel DECLARE_GLOBAL_DATA_PTR;
4920c9226cSAndreas Engel 
5072d5e44cSRabin Vincent static struct pl01x_regs *pl01x_get_regs(int portnum)
5172d5e44cSRabin Vincent {
5272d5e44cSRabin Vincent 	return (struct pl01x_regs *) port[portnum];
5372d5e44cSRabin Vincent }
5472d5e44cSRabin Vincent 
5548d0192fSAndreas Engel #ifdef CONFIG_PL010_SERIAL
5620c9226cSAndreas Engel 
5720c9226cSAndreas Engel int serial_init (void)
5820c9226cSAndreas Engel {
5972d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
6020c9226cSAndreas Engel 	unsigned int divisor;
6120c9226cSAndreas Engel 
62249d5219SMatt Waddel 	/* First, disable everything */
6372d5e44cSRabin Vincent 	writel(0, &regs->pl010_cr);
6420c9226cSAndreas Engel 
65249d5219SMatt Waddel 	/* Set baud rate */
66249d5219SMatt Waddel 	switch (baudrate) {
6720c9226cSAndreas Engel 	case 9600:
6820c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
6920c9226cSAndreas Engel 		break;
7020c9226cSAndreas Engel 
7120c9226cSAndreas Engel 	case 19200:
7220c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
7320c9226cSAndreas Engel 		break;
7420c9226cSAndreas Engel 
7520c9226cSAndreas Engel 	case 38400:
7620c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
7720c9226cSAndreas Engel 		break;
7820c9226cSAndreas Engel 
7920c9226cSAndreas Engel 	case 57600:
8020c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_57600;
8120c9226cSAndreas Engel 		break;
8220c9226cSAndreas Engel 
8320c9226cSAndreas Engel 	case 115200:
8420c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_115200;
8520c9226cSAndreas Engel 		break;
8620c9226cSAndreas Engel 
8720c9226cSAndreas Engel 	default:
8820c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
8920c9226cSAndreas Engel 	}
9020c9226cSAndreas Engel 
9172d5e44cSRabin Vincent 	writel((divisor & 0xf00) >> 8, &regs->pl010_lcrm);
9272d5e44cSRabin Vincent 	writel(divisor & 0xff, &regs->pl010_lcrl);
9320c9226cSAndreas Engel 
94249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
9572d5e44cSRabin Vincent 	writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, &regs->pl010_lcrh);
9620c9226cSAndreas Engel 
97249d5219SMatt Waddel 	/* Finally, enable the UART */
9872d5e44cSRabin Vincent 	writel(UART_PL010_CR_UARTEN, &regs->pl010_cr);
9920c9226cSAndreas Engel 
10020c9226cSAndreas Engel 	return 0;
10120c9226cSAndreas Engel }
10220c9226cSAndreas Engel 
10348d0192fSAndreas Engel #endif /* CONFIG_PL010_SERIAL */
10420c9226cSAndreas Engel 
10548d0192fSAndreas Engel #ifdef CONFIG_PL011_SERIAL
10620c9226cSAndreas Engel 
10720c9226cSAndreas Engel int serial_init (void)
10820c9226cSAndreas Engel {
10972d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
11020c9226cSAndreas Engel 	unsigned int temp;
11120c9226cSAndreas Engel 	unsigned int divider;
11220c9226cSAndreas Engel 	unsigned int remainder;
11320c9226cSAndreas Engel 	unsigned int fraction;
114910f1ae3SJohn Rigby 	unsigned int lcr;
115910f1ae3SJohn Rigby 
116910f1ae3SJohn Rigby #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT
117910f1ae3SJohn Rigby 	/* Empty RX fifo if necessary */
118910f1ae3SJohn Rigby 	if (readl(&regs->pl011_cr) & UART_PL011_CR_UARTEN) {
119910f1ae3SJohn Rigby 		while (!(readl(&regs->fr) & UART_PL01x_FR_RXFE))
120910f1ae3SJohn Rigby 			readl(&regs->dr);
121910f1ae3SJohn Rigby 	}
122910f1ae3SJohn Rigby #endif
12320c9226cSAndreas Engel 
124249d5219SMatt Waddel 	/* First, disable everything */
12572d5e44cSRabin Vincent 	writel(0, &regs->pl011_cr);
12620c9226cSAndreas Engel 
12720c9226cSAndreas Engel 	/*
128249d5219SMatt Waddel 	 * Set baud rate
129249d5219SMatt Waddel 	 *
130249d5219SMatt Waddel 	 * IBRD = UART_CLK / (16 * BAUD_RATE)
131249d5219SMatt Waddel 	 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE))
13220c9226cSAndreas Engel 	 */
133249d5219SMatt Waddel 	temp = 16 * baudrate;
13420c9226cSAndreas Engel 	divider = CONFIG_PL011_CLOCK / temp;
13520c9226cSAndreas Engel 	remainder = CONFIG_PL011_CLOCK % temp;
136249d5219SMatt Waddel 	temp = (8 * remainder) / baudrate;
13720c9226cSAndreas Engel 	fraction = (temp >> 1) + (temp & 1);
13820c9226cSAndreas Engel 
13972d5e44cSRabin Vincent 	writel(divider, &regs->pl011_ibrd);
14072d5e44cSRabin Vincent 	writel(fraction, &regs->pl011_fbrd);
14120c9226cSAndreas Engel 
142249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
143910f1ae3SJohn Rigby 	lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN;
144910f1ae3SJohn Rigby 	writel(lcr, &regs->pl011_lcrh);
14520c9226cSAndreas Engel 
146910f1ae3SJohn Rigby #ifdef CONFIG_PL011_SERIAL_RLCR
147910f1ae3SJohn Rigby 	{
148910f1ae3SJohn Rigby 		int i;
149910f1ae3SJohn Rigby 
150910f1ae3SJohn Rigby 		/*
151910f1ae3SJohn Rigby 		 * Program receive line control register after waiting
152910f1ae3SJohn Rigby 		 * 10 bus cycles.  Delay be writing to readonly register
153910f1ae3SJohn Rigby 		 * 10 times
154910f1ae3SJohn Rigby 		 */
155910f1ae3SJohn Rigby 		for (i = 0; i < 10; i++)
156910f1ae3SJohn Rigby 			writel(lcr, &regs->fr);
157910f1ae3SJohn Rigby 
158910f1ae3SJohn Rigby 		writel(lcr, &regs->pl011_rlcr);
159910f1ae3SJohn Rigby 	}
160910f1ae3SJohn Rigby #endif
161249d5219SMatt Waddel 	/* Finally, enable the UART */
16272d5e44cSRabin Vincent 	writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE,
16372d5e44cSRabin Vincent 	       &regs->pl011_cr);
16420c9226cSAndreas Engel 
16520c9226cSAndreas Engel 	return 0;
16620c9226cSAndreas Engel }
16720c9226cSAndreas Engel 
16848d0192fSAndreas Engel #endif /* CONFIG_PL011_SERIAL */
16920c9226cSAndreas Engel 
17020c9226cSAndreas Engel void serial_putc (const char c)
17120c9226cSAndreas Engel {
17220c9226cSAndreas Engel 	if (c == '\n')
17320c9226cSAndreas Engel 		pl01x_putc (CONSOLE_PORT, '\r');
17420c9226cSAndreas Engel 
17520c9226cSAndreas Engel 	pl01x_putc (CONSOLE_PORT, c);
17620c9226cSAndreas Engel }
17720c9226cSAndreas Engel 
17820c9226cSAndreas Engel void serial_puts (const char *s)
17920c9226cSAndreas Engel {
18020c9226cSAndreas Engel 	while (*s) {
18120c9226cSAndreas Engel 		serial_putc (*s++);
18220c9226cSAndreas Engel 	}
18320c9226cSAndreas Engel }
18420c9226cSAndreas Engel 
18520c9226cSAndreas Engel int serial_getc (void)
18620c9226cSAndreas Engel {
18720c9226cSAndreas Engel 	return pl01x_getc (CONSOLE_PORT);
18820c9226cSAndreas Engel }
18920c9226cSAndreas Engel 
19020c9226cSAndreas Engel int serial_tstc (void)
19120c9226cSAndreas Engel {
19220c9226cSAndreas Engel 	return pl01x_tstc (CONSOLE_PORT);
19320c9226cSAndreas Engel }
19420c9226cSAndreas Engel 
19520c9226cSAndreas Engel void serial_setbrg (void)
19620c9226cSAndreas Engel {
197*96baa4c3SLinus Walleij 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
198*96baa4c3SLinus Walleij 
199249d5219SMatt Waddel 	baudrate = gd->baudrate;
200*96baa4c3SLinus Walleij 	/*
201*96baa4c3SLinus Walleij 	 * Flush FIFO and wait for non-busy before changing baudrate to avoid
202*96baa4c3SLinus Walleij 	 * crap in console
203*96baa4c3SLinus Walleij 	 */
204*96baa4c3SLinus Walleij 	while (!(readl(&regs->fr) & UART_PL01x_FR_TXFE))
205*96baa4c3SLinus Walleij 		WATCHDOG_RESET();
206*96baa4c3SLinus Walleij 	while (readl(&regs->fr) & UART_PL01x_FR_BUSY)
207*96baa4c3SLinus Walleij 		WATCHDOG_RESET();
208249d5219SMatt Waddel 	serial_init();
20920c9226cSAndreas Engel }
21020c9226cSAndreas Engel 
21120c9226cSAndreas Engel static void pl01x_putc (int portnum, char c)
21220c9226cSAndreas Engel {
21372d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
21472d5e44cSRabin Vincent 
21520c9226cSAndreas Engel 	/* Wait until there is space in the FIFO */
21672d5e44cSRabin Vincent 	while (readl(&regs->fr) & UART_PL01x_FR_TXFF)
21720c9226cSAndreas Engel 		WATCHDOG_RESET();
21820c9226cSAndreas Engel 
21920c9226cSAndreas Engel 	/* Send the character */
22072d5e44cSRabin Vincent 	writel(c, &regs->dr);
22120c9226cSAndreas Engel }
22220c9226cSAndreas Engel 
22320c9226cSAndreas Engel static int pl01x_getc (int portnum)
22420c9226cSAndreas Engel {
22572d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
22620c9226cSAndreas Engel 	unsigned int data;
22720c9226cSAndreas Engel 
22820c9226cSAndreas Engel 	/* Wait until there is data in the FIFO */
22972d5e44cSRabin Vincent 	while (readl(&regs->fr) & UART_PL01x_FR_RXFE)
23020c9226cSAndreas Engel 		WATCHDOG_RESET();
23120c9226cSAndreas Engel 
23272d5e44cSRabin Vincent 	data = readl(&regs->dr);
23320c9226cSAndreas Engel 
23420c9226cSAndreas Engel 	/* Check for an error flag */
23520c9226cSAndreas Engel 	if (data & 0xFFFFFF00) {
23620c9226cSAndreas Engel 		/* Clear the error */
23772d5e44cSRabin Vincent 		writel(0xFFFFFFFF, &regs->ecr);
23820c9226cSAndreas Engel 		return -1;
23920c9226cSAndreas Engel 	}
24020c9226cSAndreas Engel 
24120c9226cSAndreas Engel 	return (int) data;
24220c9226cSAndreas Engel }
24320c9226cSAndreas Engel 
24420c9226cSAndreas Engel static int pl01x_tstc (int portnum)
24520c9226cSAndreas Engel {
24672d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
24772d5e44cSRabin Vincent 
24820c9226cSAndreas Engel 	WATCHDOG_RESET();
24972d5e44cSRabin Vincent 	return !(readl(&regs->fr) & UART_PL01x_FR_RXFE);
25020c9226cSAndreas Engel }
251