xref: /openbmc/u-boot/drivers/serial/serial_pl01x.c (revision 72d5e44c95062bf1e25d28c71abbc875d232d393)
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 
50*72d5e44cSRabin Vincent static struct pl01x_regs *pl01x_get_regs(int portnum)
51*72d5e44cSRabin Vincent {
52*72d5e44cSRabin Vincent 	return (struct pl01x_regs *) port[portnum];
53*72d5e44cSRabin Vincent }
54*72d5e44cSRabin Vincent 
5548d0192fSAndreas Engel #ifdef CONFIG_PL010_SERIAL
5620c9226cSAndreas Engel 
5720c9226cSAndreas Engel int serial_init (void)
5820c9226cSAndreas Engel {
59*72d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
6020c9226cSAndreas Engel 	unsigned int divisor;
6120c9226cSAndreas Engel 
62249d5219SMatt Waddel 	/* First, disable everything */
63*72d5e44cSRabin 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 
91*72d5e44cSRabin Vincent 	writel((divisor & 0xf00) >> 8, &regs->pl010_lcrm);
92*72d5e44cSRabin 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 */
95*72d5e44cSRabin Vincent 	writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, &regs->pl010_lcrh);
9620c9226cSAndreas Engel 
97249d5219SMatt Waddel 	/* Finally, enable the UART */
98*72d5e44cSRabin 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 {
109*72d5e44cSRabin 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;
11420c9226cSAndreas Engel 
115249d5219SMatt Waddel 	/* First, disable everything */
116*72d5e44cSRabin Vincent 	writel(0, &regs->pl011_cr);
11720c9226cSAndreas Engel 
11820c9226cSAndreas Engel 	/*
119249d5219SMatt Waddel 	 * Set baud rate
120249d5219SMatt Waddel 	 *
121249d5219SMatt Waddel 	 * IBRD = UART_CLK / (16 * BAUD_RATE)
122249d5219SMatt Waddel 	 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE))
12320c9226cSAndreas Engel 	 */
124249d5219SMatt Waddel 	temp = 16 * baudrate;
12520c9226cSAndreas Engel 	divider = CONFIG_PL011_CLOCK / temp;
12620c9226cSAndreas Engel 	remainder = CONFIG_PL011_CLOCK % temp;
127249d5219SMatt Waddel 	temp = (8 * remainder) / baudrate;
12820c9226cSAndreas Engel 	fraction = (temp >> 1) + (temp & 1);
12920c9226cSAndreas Engel 
130*72d5e44cSRabin Vincent 	writel(divider, &regs->pl011_ibrd);
131*72d5e44cSRabin Vincent 	writel(fraction, &regs->pl011_fbrd);
13220c9226cSAndreas Engel 
133249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
134*72d5e44cSRabin Vincent 	writel(UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN,
135*72d5e44cSRabin Vincent 	       &regs->pl011_lcrh);
13620c9226cSAndreas Engel 
137249d5219SMatt Waddel 	/* Finally, enable the UART */
138*72d5e44cSRabin Vincent 	writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE,
139*72d5e44cSRabin Vincent 	       &regs->pl011_cr);
14020c9226cSAndreas Engel 
14120c9226cSAndreas Engel 	return 0;
14220c9226cSAndreas Engel }
14320c9226cSAndreas Engel 
14448d0192fSAndreas Engel #endif /* CONFIG_PL011_SERIAL */
14520c9226cSAndreas Engel 
14620c9226cSAndreas Engel void serial_putc (const char c)
14720c9226cSAndreas Engel {
14820c9226cSAndreas Engel 	if (c == '\n')
14920c9226cSAndreas Engel 		pl01x_putc (CONSOLE_PORT, '\r');
15020c9226cSAndreas Engel 
15120c9226cSAndreas Engel 	pl01x_putc (CONSOLE_PORT, c);
15220c9226cSAndreas Engel }
15320c9226cSAndreas Engel 
15420c9226cSAndreas Engel void serial_puts (const char *s)
15520c9226cSAndreas Engel {
15620c9226cSAndreas Engel 	while (*s) {
15720c9226cSAndreas Engel 		serial_putc (*s++);
15820c9226cSAndreas Engel 	}
15920c9226cSAndreas Engel }
16020c9226cSAndreas Engel 
16120c9226cSAndreas Engel int serial_getc (void)
16220c9226cSAndreas Engel {
16320c9226cSAndreas Engel 	return pl01x_getc (CONSOLE_PORT);
16420c9226cSAndreas Engel }
16520c9226cSAndreas Engel 
16620c9226cSAndreas Engel int serial_tstc (void)
16720c9226cSAndreas Engel {
16820c9226cSAndreas Engel 	return pl01x_tstc (CONSOLE_PORT);
16920c9226cSAndreas Engel }
17020c9226cSAndreas Engel 
17120c9226cSAndreas Engel void serial_setbrg (void)
17220c9226cSAndreas Engel {
173249d5219SMatt Waddel 	baudrate = gd->baudrate;
174249d5219SMatt Waddel 	serial_init();
17520c9226cSAndreas Engel }
17620c9226cSAndreas Engel 
17720c9226cSAndreas Engel static void pl01x_putc (int portnum, char c)
17820c9226cSAndreas Engel {
179*72d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
180*72d5e44cSRabin Vincent 
18120c9226cSAndreas Engel 	/* Wait until there is space in the FIFO */
182*72d5e44cSRabin Vincent 	while (readl(&regs->fr) & UART_PL01x_FR_TXFF)
18320c9226cSAndreas Engel 		WATCHDOG_RESET();
18420c9226cSAndreas Engel 
18520c9226cSAndreas Engel 	/* Send the character */
186*72d5e44cSRabin Vincent 	writel(c, &regs->dr);
18720c9226cSAndreas Engel }
18820c9226cSAndreas Engel 
18920c9226cSAndreas Engel static int pl01x_getc (int portnum)
19020c9226cSAndreas Engel {
191*72d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
19220c9226cSAndreas Engel 	unsigned int data;
19320c9226cSAndreas Engel 
19420c9226cSAndreas Engel 	/* Wait until there is data in the FIFO */
195*72d5e44cSRabin Vincent 	while (readl(&regs->fr) & UART_PL01x_FR_RXFE)
19620c9226cSAndreas Engel 		WATCHDOG_RESET();
19720c9226cSAndreas Engel 
198*72d5e44cSRabin Vincent 	data = readl(&regs->dr);
19920c9226cSAndreas Engel 
20020c9226cSAndreas Engel 	/* Check for an error flag */
20120c9226cSAndreas Engel 	if (data & 0xFFFFFF00) {
20220c9226cSAndreas Engel 		/* Clear the error */
203*72d5e44cSRabin Vincent 		writel(0xFFFFFFFF, &regs->ecr);
20420c9226cSAndreas Engel 		return -1;
20520c9226cSAndreas Engel 	}
20620c9226cSAndreas Engel 
20720c9226cSAndreas Engel 	return (int) data;
20820c9226cSAndreas Engel }
20920c9226cSAndreas Engel 
21020c9226cSAndreas Engel static int pl01x_tstc (int portnum)
21120c9226cSAndreas Engel {
212*72d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
213*72d5e44cSRabin Vincent 
21420c9226cSAndreas Engel 	WATCHDOG_RESET();
215*72d5e44cSRabin Vincent 	return !(readl(&regs->fr) & UART_PL01x_FR_RXFE);
21620c9226cSAndreas Engel }
217