xref: /openbmc/u-boot/drivers/serial/serial_pl01x.c (revision 1a4596601fd395f3afb8f82f3f840c5e00bdd57a)
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  *
9*1a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
1020c9226cSAndreas Engel  */
1120c9226cSAndreas Engel 
1248d0192fSAndreas Engel /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */
1320c9226cSAndreas Engel 
1420c9226cSAndreas Engel #include <common.h>
1520c9226cSAndreas Engel #include <watchdog.h>
16249d5219SMatt Waddel #include <asm/io.h>
1739f61477SMarek Vasut #include <serial.h>
1839f61477SMarek Vasut #include <linux/compiler.h>
1920c9226cSAndreas Engel #include "serial_pl01x.h"
2020c9226cSAndreas Engel 
2120c9226cSAndreas Engel /*
2220c9226cSAndreas Engel  * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1
2320c9226cSAndreas Engel  * Integrator CP has two UARTs, use the first one, at 38400-8-N-1
2420c9226cSAndreas Engel  * Versatile PB has four UARTs.
2520c9226cSAndreas Engel  */
2620c9226cSAndreas Engel #define CONSOLE_PORT CONFIG_CONS_INDEX
2720c9226cSAndreas Engel static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS;
2820c9226cSAndreas Engel #define NUM_PORTS (sizeof(port)/sizeof(port[0]))
2920c9226cSAndreas Engel 
3020c9226cSAndreas Engel static void pl01x_putc (int portnum, char c);
3120c9226cSAndreas Engel static int pl01x_getc (int portnum);
3220c9226cSAndreas Engel static int pl01x_tstc (int portnum);
33249d5219SMatt Waddel unsigned int baudrate = CONFIG_BAUDRATE;
34249d5219SMatt Waddel DECLARE_GLOBAL_DATA_PTR;
3520c9226cSAndreas Engel 
3672d5e44cSRabin Vincent static struct pl01x_regs *pl01x_get_regs(int portnum)
3772d5e44cSRabin Vincent {
3872d5e44cSRabin Vincent 	return (struct pl01x_regs *) port[portnum];
3972d5e44cSRabin Vincent }
4072d5e44cSRabin Vincent 
4148d0192fSAndreas Engel #ifdef CONFIG_PL010_SERIAL
4220c9226cSAndreas Engel 
4339f61477SMarek Vasut static int pl01x_serial_init(void)
4420c9226cSAndreas Engel {
4572d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
4620c9226cSAndreas Engel 	unsigned int divisor;
4720c9226cSAndreas Engel 
48249d5219SMatt Waddel 	/* First, disable everything */
4972d5e44cSRabin Vincent 	writel(0, &regs->pl010_cr);
5020c9226cSAndreas Engel 
51249d5219SMatt Waddel 	/* Set baud rate */
52249d5219SMatt Waddel 	switch (baudrate) {
5320c9226cSAndreas Engel 	case 9600:
5420c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
5520c9226cSAndreas Engel 		break;
5620c9226cSAndreas Engel 
5720c9226cSAndreas Engel 	case 19200:
5820c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_9600;
5920c9226cSAndreas Engel 		break;
6020c9226cSAndreas Engel 
6120c9226cSAndreas Engel 	case 38400:
6220c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
6320c9226cSAndreas Engel 		break;
6420c9226cSAndreas Engel 
6520c9226cSAndreas Engel 	case 57600:
6620c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_57600;
6720c9226cSAndreas Engel 		break;
6820c9226cSAndreas Engel 
6920c9226cSAndreas Engel 	case 115200:
7020c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_115200;
7120c9226cSAndreas Engel 		break;
7220c9226cSAndreas Engel 
7320c9226cSAndreas Engel 	default:
7420c9226cSAndreas Engel 		divisor = UART_PL010_BAUD_38400;
7520c9226cSAndreas Engel 	}
7620c9226cSAndreas Engel 
7772d5e44cSRabin Vincent 	writel((divisor & 0xf00) >> 8, &regs->pl010_lcrm);
7872d5e44cSRabin Vincent 	writel(divisor & 0xff, &regs->pl010_lcrl);
7920c9226cSAndreas Engel 
80249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
8172d5e44cSRabin Vincent 	writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, &regs->pl010_lcrh);
8220c9226cSAndreas Engel 
83249d5219SMatt Waddel 	/* Finally, enable the UART */
8472d5e44cSRabin Vincent 	writel(UART_PL010_CR_UARTEN, &regs->pl010_cr);
8520c9226cSAndreas Engel 
8620c9226cSAndreas Engel 	return 0;
8720c9226cSAndreas Engel }
8820c9226cSAndreas Engel 
8948d0192fSAndreas Engel #endif /* CONFIG_PL010_SERIAL */
9020c9226cSAndreas Engel 
9148d0192fSAndreas Engel #ifdef CONFIG_PL011_SERIAL
9220c9226cSAndreas Engel 
9339f61477SMarek Vasut static int pl01x_serial_init(void)
9420c9226cSAndreas Engel {
9572d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
9620c9226cSAndreas Engel 	unsigned int temp;
9720c9226cSAndreas Engel 	unsigned int divider;
9820c9226cSAndreas Engel 	unsigned int remainder;
9920c9226cSAndreas Engel 	unsigned int fraction;
100910f1ae3SJohn Rigby 	unsigned int lcr;
101910f1ae3SJohn Rigby 
102910f1ae3SJohn Rigby #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT
103910f1ae3SJohn Rigby 	/* Empty RX fifo if necessary */
104910f1ae3SJohn Rigby 	if (readl(&regs->pl011_cr) & UART_PL011_CR_UARTEN) {
105910f1ae3SJohn Rigby 		while (!(readl(&regs->fr) & UART_PL01x_FR_RXFE))
106910f1ae3SJohn Rigby 			readl(&regs->dr);
107910f1ae3SJohn Rigby 	}
108910f1ae3SJohn Rigby #endif
10920c9226cSAndreas Engel 
110249d5219SMatt Waddel 	/* First, disable everything */
11172d5e44cSRabin Vincent 	writel(0, &regs->pl011_cr);
11220c9226cSAndreas Engel 
11320c9226cSAndreas Engel 	/*
114249d5219SMatt Waddel 	 * Set baud rate
115249d5219SMatt Waddel 	 *
116249d5219SMatt Waddel 	 * IBRD = UART_CLK / (16 * BAUD_RATE)
117249d5219SMatt Waddel 	 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) / (16 * BAUD_RATE))
11820c9226cSAndreas Engel 	 */
119249d5219SMatt Waddel 	temp = 16 * baudrate;
12020c9226cSAndreas Engel 	divider = CONFIG_PL011_CLOCK / temp;
12120c9226cSAndreas Engel 	remainder = CONFIG_PL011_CLOCK % temp;
122249d5219SMatt Waddel 	temp = (8 * remainder) / baudrate;
12320c9226cSAndreas Engel 	fraction = (temp >> 1) + (temp & 1);
12420c9226cSAndreas Engel 
12572d5e44cSRabin Vincent 	writel(divider, &regs->pl011_ibrd);
12672d5e44cSRabin Vincent 	writel(fraction, &regs->pl011_fbrd);
12720c9226cSAndreas Engel 
128249d5219SMatt Waddel 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
129910f1ae3SJohn Rigby 	lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN;
130910f1ae3SJohn Rigby 	writel(lcr, &regs->pl011_lcrh);
13120c9226cSAndreas Engel 
132910f1ae3SJohn Rigby #ifdef CONFIG_PL011_SERIAL_RLCR
133910f1ae3SJohn Rigby 	{
134910f1ae3SJohn Rigby 		int i;
135910f1ae3SJohn Rigby 
136910f1ae3SJohn Rigby 		/*
137910f1ae3SJohn Rigby 		 * Program receive line control register after waiting
138910f1ae3SJohn Rigby 		 * 10 bus cycles.  Delay be writing to readonly register
139910f1ae3SJohn Rigby 		 * 10 times
140910f1ae3SJohn Rigby 		 */
141910f1ae3SJohn Rigby 		for (i = 0; i < 10; i++)
142910f1ae3SJohn Rigby 			writel(lcr, &regs->fr);
143910f1ae3SJohn Rigby 
144910f1ae3SJohn Rigby 		writel(lcr, &regs->pl011_rlcr);
14584dee301SMathieu J. Poirier 		/* lcrh needs to be set again for change to be effective */
14684dee301SMathieu J. Poirier 		writel(lcr, &regs->pl011_lcrh);
147910f1ae3SJohn Rigby 	}
148910f1ae3SJohn Rigby #endif
149249d5219SMatt Waddel 	/* Finally, enable the UART */
15010501df0SJoshua Housh 	writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE |
15110501df0SJoshua Housh 	       UART_PL011_CR_RTS, &regs->pl011_cr);
15220c9226cSAndreas Engel 
15320c9226cSAndreas Engel 	return 0;
15420c9226cSAndreas Engel }
15520c9226cSAndreas Engel 
15648d0192fSAndreas Engel #endif /* CONFIG_PL011_SERIAL */
15720c9226cSAndreas Engel 
15839f61477SMarek Vasut static void pl01x_serial_putc(const char c)
15920c9226cSAndreas Engel {
16020c9226cSAndreas Engel 	if (c == '\n')
16120c9226cSAndreas Engel 		pl01x_putc (CONSOLE_PORT, '\r');
16220c9226cSAndreas Engel 
16320c9226cSAndreas Engel 	pl01x_putc (CONSOLE_PORT, c);
16420c9226cSAndreas Engel }
16520c9226cSAndreas Engel 
16639f61477SMarek Vasut static int pl01x_serial_getc(void)
16720c9226cSAndreas Engel {
16820c9226cSAndreas Engel 	return pl01x_getc (CONSOLE_PORT);
16920c9226cSAndreas Engel }
17020c9226cSAndreas Engel 
17139f61477SMarek Vasut static int pl01x_serial_tstc(void)
17220c9226cSAndreas Engel {
17320c9226cSAndreas Engel 	return pl01x_tstc (CONSOLE_PORT);
17420c9226cSAndreas Engel }
17520c9226cSAndreas Engel 
17639f61477SMarek Vasut static void pl01x_serial_setbrg(void)
17720c9226cSAndreas Engel {
17896baa4c3SLinus Walleij 	struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT);
17996baa4c3SLinus Walleij 
180249d5219SMatt Waddel 	baudrate = gd->baudrate;
18196baa4c3SLinus Walleij 	/*
18296baa4c3SLinus Walleij 	 * Flush FIFO and wait for non-busy before changing baudrate to avoid
18396baa4c3SLinus Walleij 	 * crap in console
18496baa4c3SLinus Walleij 	 */
18596baa4c3SLinus Walleij 	while (!(readl(&regs->fr) & UART_PL01x_FR_TXFE))
18696baa4c3SLinus Walleij 		WATCHDOG_RESET();
18796baa4c3SLinus Walleij 	while (readl(&regs->fr) & UART_PL01x_FR_BUSY)
18896baa4c3SLinus Walleij 		WATCHDOG_RESET();
189249d5219SMatt Waddel 	serial_init();
19020c9226cSAndreas Engel }
19120c9226cSAndreas Engel 
19220c9226cSAndreas Engel static void pl01x_putc (int portnum, char c)
19320c9226cSAndreas Engel {
19472d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
19572d5e44cSRabin Vincent 
19620c9226cSAndreas Engel 	/* Wait until there is space in the FIFO */
19772d5e44cSRabin Vincent 	while (readl(&regs->fr) & UART_PL01x_FR_TXFF)
19820c9226cSAndreas Engel 		WATCHDOG_RESET();
19920c9226cSAndreas Engel 
20020c9226cSAndreas Engel 	/* Send the character */
20172d5e44cSRabin Vincent 	writel(c, &regs->dr);
20220c9226cSAndreas Engel }
20320c9226cSAndreas Engel 
20420c9226cSAndreas Engel static int pl01x_getc (int portnum)
20520c9226cSAndreas Engel {
20672d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
20720c9226cSAndreas Engel 	unsigned int data;
20820c9226cSAndreas Engel 
20920c9226cSAndreas Engel 	/* Wait until there is data in the FIFO */
21072d5e44cSRabin Vincent 	while (readl(&regs->fr) & UART_PL01x_FR_RXFE)
21120c9226cSAndreas Engel 		WATCHDOG_RESET();
21220c9226cSAndreas Engel 
21372d5e44cSRabin Vincent 	data = readl(&regs->dr);
21420c9226cSAndreas Engel 
21520c9226cSAndreas Engel 	/* Check for an error flag */
21620c9226cSAndreas Engel 	if (data & 0xFFFFFF00) {
21720c9226cSAndreas Engel 		/* Clear the error */
21872d5e44cSRabin Vincent 		writel(0xFFFFFFFF, &regs->ecr);
21920c9226cSAndreas Engel 		return -1;
22020c9226cSAndreas Engel 	}
22120c9226cSAndreas Engel 
22220c9226cSAndreas Engel 	return (int) data;
22320c9226cSAndreas Engel }
22420c9226cSAndreas Engel 
22520c9226cSAndreas Engel static int pl01x_tstc (int portnum)
22620c9226cSAndreas Engel {
22772d5e44cSRabin Vincent 	struct pl01x_regs *regs = pl01x_get_regs(portnum);
22872d5e44cSRabin Vincent 
22920c9226cSAndreas Engel 	WATCHDOG_RESET();
23072d5e44cSRabin Vincent 	return !(readl(&regs->fr) & UART_PL01x_FR_RXFE);
23120c9226cSAndreas Engel }
23239f61477SMarek Vasut 
23339f61477SMarek Vasut static struct serial_device pl01x_serial_drv = {
23439f61477SMarek Vasut 	.name	= "pl01x_serial",
23539f61477SMarek Vasut 	.start	= pl01x_serial_init,
23639f61477SMarek Vasut 	.stop	= NULL,
23739f61477SMarek Vasut 	.setbrg	= pl01x_serial_setbrg,
23839f61477SMarek Vasut 	.putc	= pl01x_serial_putc,
239ec3fd689SMarek Vasut 	.puts	= default_serial_puts,
24039f61477SMarek Vasut 	.getc	= pl01x_serial_getc,
24139f61477SMarek Vasut 	.tstc	= pl01x_serial_tstc,
24239f61477SMarek Vasut };
24339f61477SMarek Vasut 
24439f61477SMarek Vasut void pl01x_serial_initialize(void)
24539f61477SMarek Vasut {
24639f61477SMarek Vasut 	serial_register(&pl01x_serial_drv);
24739f61477SMarek Vasut }
24839f61477SMarek Vasut 
24939f61477SMarek Vasut __weak struct serial_device *default_serial_console(void)
25039f61477SMarek Vasut {
25139f61477SMarek Vasut 	return &pl01x_serial_drv;
25239f61477SMarek Vasut }
253