xref: /openbmc/u-boot/drivers/serial/serial_pl01x.c (revision aed2fbef5e9a0ab5a7cd01e742039a962f0b24ef)
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  *
91a459660SWolfgang 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>
15*aed2fbefSSimon Glass #include <errno.h>
1620c9226cSAndreas Engel #include <watchdog.h>
17249d5219SMatt Waddel #include <asm/io.h>
1839f61477SMarek Vasut #include <serial.h>
19*aed2fbefSSimon Glass #include <serial_pl01x.h>
2039f61477SMarek Vasut #include <linux/compiler.h>
21*aed2fbefSSimon Glass #include "serial_pl01x_internal.h"
2220c9226cSAndreas Engel 
2320c9226cSAndreas Engel static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS;
24*aed2fbefSSimon Glass static enum pl01x_type pl01x_type __attribute__ ((section(".data")));
25*aed2fbefSSimon Glass static struct pl01x_regs *base_regs __attribute__ ((section(".data")));
2620c9226cSAndreas Engel #define NUM_PORTS (sizeof(port)/sizeof(port[0]))
2720c9226cSAndreas Engel 
28249d5219SMatt Waddel DECLARE_GLOBAL_DATA_PTR;
2920c9226cSAndreas Engel 
30*aed2fbefSSimon Glass static int pl01x_putc(struct pl01x_regs *regs, char c)
3172d5e44cSRabin Vincent {
3220c9226cSAndreas Engel 	/* Wait until there is space in the FIFO */
33*aed2fbefSSimon Glass 	if (readl(&regs->fr) & UART_PL01x_FR_TXFF)
34*aed2fbefSSimon Glass 		return -EAGAIN;
3520c9226cSAndreas Engel 
3620c9226cSAndreas Engel 	/* Send the character */
3772d5e44cSRabin Vincent 	writel(c, &regs->dr);
38*aed2fbefSSimon Glass 
39*aed2fbefSSimon Glass 	return 0;
4020c9226cSAndreas Engel }
4120c9226cSAndreas Engel 
42*aed2fbefSSimon Glass static int pl01x_getc(struct pl01x_regs *regs)
4320c9226cSAndreas Engel {
4420c9226cSAndreas Engel 	unsigned int data;
4520c9226cSAndreas Engel 
4620c9226cSAndreas Engel 	/* Wait until there is data in the FIFO */
47*aed2fbefSSimon Glass 	if (readl(&regs->fr) & UART_PL01x_FR_RXFE)
48*aed2fbefSSimon Glass 		return -EAGAIN;
4920c9226cSAndreas Engel 
5072d5e44cSRabin Vincent 	data = readl(&regs->dr);
5120c9226cSAndreas Engel 
5220c9226cSAndreas Engel 	/* Check for an error flag */
5320c9226cSAndreas Engel 	if (data & 0xFFFFFF00) {
5420c9226cSAndreas Engel 		/* Clear the error */
5572d5e44cSRabin Vincent 		writel(0xFFFFFFFF, &regs->ecr);
5620c9226cSAndreas Engel 		return -1;
5720c9226cSAndreas Engel 	}
5820c9226cSAndreas Engel 
5920c9226cSAndreas Engel 	return (int) data;
6020c9226cSAndreas Engel }
6120c9226cSAndreas Engel 
62*aed2fbefSSimon Glass static int pl01x_tstc(struct pl01x_regs *regs)
6320c9226cSAndreas Engel {
6420c9226cSAndreas Engel 	WATCHDOG_RESET();
6572d5e44cSRabin Vincent 	return !(readl(&regs->fr) & UART_PL01x_FR_RXFE);
6620c9226cSAndreas Engel }
6739f61477SMarek Vasut 
68*aed2fbefSSimon Glass static int pl01x_generic_serial_init(struct pl01x_regs *regs,
69*aed2fbefSSimon Glass 				     enum pl01x_type type)
70*aed2fbefSSimon Glass {
71*aed2fbefSSimon Glass 	unsigned int lcr;
72*aed2fbefSSimon Glass 
73*aed2fbefSSimon Glass #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT
74*aed2fbefSSimon Glass 	if (type == TYPE_PL011) {
75*aed2fbefSSimon Glass 		/* Empty RX fifo if necessary */
76*aed2fbefSSimon Glass 		if (readl(&regs->pl011_cr) & UART_PL011_CR_UARTEN) {
77*aed2fbefSSimon Glass 			while (!(readl(&regs->fr) & UART_PL01x_FR_RXFE))
78*aed2fbefSSimon Glass 				readl(&regs->dr);
79*aed2fbefSSimon Glass 		}
80*aed2fbefSSimon Glass 	}
81*aed2fbefSSimon Glass #endif
82*aed2fbefSSimon Glass 
83*aed2fbefSSimon Glass 	/* First, disable everything */
84*aed2fbefSSimon Glass 	writel(0, &regs->pl010_cr);
85*aed2fbefSSimon Glass 
86*aed2fbefSSimon Glass 	/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
87*aed2fbefSSimon Glass 	lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN;
88*aed2fbefSSimon Glass 	writel(lcr, &regs->pl011_lcrh);
89*aed2fbefSSimon Glass 
90*aed2fbefSSimon Glass 	switch (type) {
91*aed2fbefSSimon Glass 	case TYPE_PL010:
92*aed2fbefSSimon Glass 		break;
93*aed2fbefSSimon Glass 	case TYPE_PL011: {
94*aed2fbefSSimon Glass #ifdef CONFIG_PL011_SERIAL_RLCR
95*aed2fbefSSimon Glass 		int i;
96*aed2fbefSSimon Glass 
97*aed2fbefSSimon Glass 		/*
98*aed2fbefSSimon Glass 		 * Program receive line control register after waiting
99*aed2fbefSSimon Glass 		 * 10 bus cycles.  Delay be writing to readonly register
100*aed2fbefSSimon Glass 		 * 10 times
101*aed2fbefSSimon Glass 		 */
102*aed2fbefSSimon Glass 		for (i = 0; i < 10; i++)
103*aed2fbefSSimon Glass 			writel(lcr, &regs->fr);
104*aed2fbefSSimon Glass 
105*aed2fbefSSimon Glass 		writel(lcr, &regs->pl011_rlcr);
106*aed2fbefSSimon Glass 		/* lcrh needs to be set again for change to be effective */
107*aed2fbefSSimon Glass 		writel(lcr, &regs->pl011_lcrh);
108*aed2fbefSSimon Glass #endif
109*aed2fbefSSimon Glass 		break;
110*aed2fbefSSimon Glass 	}
111*aed2fbefSSimon Glass 	default:
112*aed2fbefSSimon Glass 		return -EINVAL;
113*aed2fbefSSimon Glass 	}
114*aed2fbefSSimon Glass 
115*aed2fbefSSimon Glass 	return 0;
116*aed2fbefSSimon Glass }
117*aed2fbefSSimon Glass 
118*aed2fbefSSimon Glass static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type,
119*aed2fbefSSimon Glass 				int clock, int baudrate)
120*aed2fbefSSimon Glass {
121*aed2fbefSSimon Glass 	switch (type) {
122*aed2fbefSSimon Glass 	case TYPE_PL010: {
123*aed2fbefSSimon Glass 		unsigned int divisor;
124*aed2fbefSSimon Glass 
125*aed2fbefSSimon Glass 		switch (baudrate) {
126*aed2fbefSSimon Glass 		case 9600:
127*aed2fbefSSimon Glass 			divisor = UART_PL010_BAUD_9600;
128*aed2fbefSSimon Glass 			break;
129*aed2fbefSSimon Glass 		case 19200:
130*aed2fbefSSimon Glass 			divisor = UART_PL010_BAUD_9600;
131*aed2fbefSSimon Glass 			break;
132*aed2fbefSSimon Glass 		case 38400:
133*aed2fbefSSimon Glass 			divisor = UART_PL010_BAUD_38400;
134*aed2fbefSSimon Glass 			break;
135*aed2fbefSSimon Glass 		case 57600:
136*aed2fbefSSimon Glass 			divisor = UART_PL010_BAUD_57600;
137*aed2fbefSSimon Glass 			break;
138*aed2fbefSSimon Glass 		case 115200:
139*aed2fbefSSimon Glass 			divisor = UART_PL010_BAUD_115200;
140*aed2fbefSSimon Glass 			break;
141*aed2fbefSSimon Glass 		default:
142*aed2fbefSSimon Glass 			divisor = UART_PL010_BAUD_38400;
143*aed2fbefSSimon Glass 		}
144*aed2fbefSSimon Glass 
145*aed2fbefSSimon Glass 		writel((divisor & 0xf00) >> 8, &regs->pl010_lcrm);
146*aed2fbefSSimon Glass 		writel(divisor & 0xff, &regs->pl010_lcrl);
147*aed2fbefSSimon Glass 
148*aed2fbefSSimon Glass 		/* Finally, enable the UART */
149*aed2fbefSSimon Glass 		writel(UART_PL010_CR_UARTEN, &regs->pl010_cr);
150*aed2fbefSSimon Glass 		break;
151*aed2fbefSSimon Glass 	}
152*aed2fbefSSimon Glass 	case TYPE_PL011: {
153*aed2fbefSSimon Glass 		unsigned int temp;
154*aed2fbefSSimon Glass 		unsigned int divider;
155*aed2fbefSSimon Glass 		unsigned int remainder;
156*aed2fbefSSimon Glass 		unsigned int fraction;
157*aed2fbefSSimon Glass 
158*aed2fbefSSimon Glass 		/*
159*aed2fbefSSimon Glass 		* Set baud rate
160*aed2fbefSSimon Glass 		*
161*aed2fbefSSimon Glass 		* IBRD = UART_CLK / (16 * BAUD_RATE)
162*aed2fbefSSimon Glass 		* FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE)))
163*aed2fbefSSimon Glass 		*		/ (16 * BAUD_RATE))
164*aed2fbefSSimon Glass 		*/
165*aed2fbefSSimon Glass 		temp = 16 * baudrate;
166*aed2fbefSSimon Glass 		divider = clock / temp;
167*aed2fbefSSimon Glass 		remainder = clock % temp;
168*aed2fbefSSimon Glass 		temp = (8 * remainder) / baudrate;
169*aed2fbefSSimon Glass 		fraction = (temp >> 1) + (temp & 1);
170*aed2fbefSSimon Glass 
171*aed2fbefSSimon Glass 		writel(divider, &regs->pl011_ibrd);
172*aed2fbefSSimon Glass 		writel(fraction, &regs->pl011_fbrd);
173*aed2fbefSSimon Glass 
174*aed2fbefSSimon Glass 		/* Finally, enable the UART */
175*aed2fbefSSimon Glass 		writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE |
176*aed2fbefSSimon Glass 		       UART_PL011_CR_RXE | UART_PL011_CR_RTS, &regs->pl011_cr);
177*aed2fbefSSimon Glass 		break;
178*aed2fbefSSimon Glass 	}
179*aed2fbefSSimon Glass 	default:
180*aed2fbefSSimon Glass 		return -EINVAL;
181*aed2fbefSSimon Glass 	}
182*aed2fbefSSimon Glass 
183*aed2fbefSSimon Glass 	return 0;
184*aed2fbefSSimon Glass }
185*aed2fbefSSimon Glass 
186*aed2fbefSSimon Glass #ifndef CONFIG_DM_SERIAL
187*aed2fbefSSimon Glass static void pl01x_serial_init_baud(int baudrate)
188*aed2fbefSSimon Glass {
189*aed2fbefSSimon Glass 	int clock = 0;
190*aed2fbefSSimon Glass 
191*aed2fbefSSimon Glass #if defined(CONFIG_PL010_SERIAL)
192*aed2fbefSSimon Glass 	pl01x_type = TYPE_PL010;
193*aed2fbefSSimon Glass #elif defined(CONFIG_PL011_SERIAL)
194*aed2fbefSSimon Glass 	pl01x_type = TYPE_PL011;
195*aed2fbefSSimon Glass 	clock = CONFIG_PL011_CLOCK;
196*aed2fbefSSimon Glass #endif
197*aed2fbefSSimon Glass 	base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX];
198*aed2fbefSSimon Glass 
199*aed2fbefSSimon Glass 	pl01x_generic_serial_init(base_regs, pl01x_type);
200*aed2fbefSSimon Glass 	pl01x_generic_setbrg(base_regs, TYPE_PL010, clock, baudrate);
201*aed2fbefSSimon Glass }
202*aed2fbefSSimon Glass 
203*aed2fbefSSimon Glass /*
204*aed2fbefSSimon Glass  * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1
205*aed2fbefSSimon Glass  * Integrator CP has two UARTs, use the first one, at 38400-8-N-1
206*aed2fbefSSimon Glass  * Versatile PB has four UARTs.
207*aed2fbefSSimon Glass  */
208*aed2fbefSSimon Glass int pl01x_serial_init(void)
209*aed2fbefSSimon Glass {
210*aed2fbefSSimon Glass 	pl01x_serial_init_baud(CONFIG_BAUDRATE);
211*aed2fbefSSimon Glass 
212*aed2fbefSSimon Glass 	return 0;
213*aed2fbefSSimon Glass }
214*aed2fbefSSimon Glass 
215*aed2fbefSSimon Glass static void pl01x_serial_putc(const char c)
216*aed2fbefSSimon Glass {
217*aed2fbefSSimon Glass 	if (c == '\n')
218*aed2fbefSSimon Glass 		while (pl01x_putc(base_regs, '\r') == -EAGAIN);
219*aed2fbefSSimon Glass 
220*aed2fbefSSimon Glass 	while (pl01x_putc(base_regs, c) == -EAGAIN);
221*aed2fbefSSimon Glass }
222*aed2fbefSSimon Glass 
223*aed2fbefSSimon Glass static int pl01x_serial_getc(void)
224*aed2fbefSSimon Glass {
225*aed2fbefSSimon Glass 	while (1) {
226*aed2fbefSSimon Glass 		int ch = pl01x_getc(base_regs);
227*aed2fbefSSimon Glass 
228*aed2fbefSSimon Glass 		if (ch == -EAGAIN) {
229*aed2fbefSSimon Glass 			WATCHDOG_RESET();
230*aed2fbefSSimon Glass 			continue;
231*aed2fbefSSimon Glass 		}
232*aed2fbefSSimon Glass 
233*aed2fbefSSimon Glass 		return ch;
234*aed2fbefSSimon Glass 	}
235*aed2fbefSSimon Glass }
236*aed2fbefSSimon Glass 
237*aed2fbefSSimon Glass static int pl01x_serial_tstc(void)
238*aed2fbefSSimon Glass {
239*aed2fbefSSimon Glass 	return pl01x_tstc(base_regs);
240*aed2fbefSSimon Glass }
241*aed2fbefSSimon Glass 
242*aed2fbefSSimon Glass static void pl01x_serial_setbrg(void)
243*aed2fbefSSimon Glass {
244*aed2fbefSSimon Glass 	/*
245*aed2fbefSSimon Glass 	 * Flush FIFO and wait for non-busy before changing baudrate to avoid
246*aed2fbefSSimon Glass 	 * crap in console
247*aed2fbefSSimon Glass 	 */
248*aed2fbefSSimon Glass 	while (!(readl(&base_regs->fr) & UART_PL01x_FR_TXFE))
249*aed2fbefSSimon Glass 		WATCHDOG_RESET();
250*aed2fbefSSimon Glass 	while (readl(&base_regs->fr) & UART_PL01x_FR_BUSY)
251*aed2fbefSSimon Glass 		WATCHDOG_RESET();
252*aed2fbefSSimon Glass 	pl01x_serial_init_baud(gd->baudrate);
253*aed2fbefSSimon Glass }
254*aed2fbefSSimon Glass 
25539f61477SMarek Vasut static struct serial_device pl01x_serial_drv = {
25639f61477SMarek Vasut 	.name	= "pl01x_serial",
25739f61477SMarek Vasut 	.start	= pl01x_serial_init,
25839f61477SMarek Vasut 	.stop	= NULL,
25939f61477SMarek Vasut 	.setbrg	= pl01x_serial_setbrg,
26039f61477SMarek Vasut 	.putc	= pl01x_serial_putc,
261ec3fd689SMarek Vasut 	.puts	= default_serial_puts,
26239f61477SMarek Vasut 	.getc	= pl01x_serial_getc,
26339f61477SMarek Vasut 	.tstc	= pl01x_serial_tstc,
26439f61477SMarek Vasut };
26539f61477SMarek Vasut 
26639f61477SMarek Vasut void pl01x_serial_initialize(void)
26739f61477SMarek Vasut {
26839f61477SMarek Vasut 	serial_register(&pl01x_serial_drv);
26939f61477SMarek Vasut }
27039f61477SMarek Vasut 
27139f61477SMarek Vasut __weak struct serial_device *default_serial_console(void)
27239f61477SMarek Vasut {
27339f61477SMarek Vasut 	return &pl01x_serial_drv;
27439f61477SMarek Vasut }
275*aed2fbefSSimon Glass 
276*aed2fbefSSimon Glass #endif /* nCONFIG_DM_SERIAL */
277