3 * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
7 * Philippe Robin, <philippe.robin@arm.com>
9 * SPDX-License-Identifier: GPL-2.0+
12 /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */
19 #include <serial_pl01x.h>
20 #include <linux/compiler.h>
21 #include "serial_pl01x_internal.h"
23 static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS;
24 static enum pl01x_type pl01x_type __attribute__ ((section(".data")));
25 static struct pl01x_regs *base_regs __attribute__ ((section(".data")));
26 #define NUM_PORTS (sizeof(port)/sizeof(port[0]))
28 DECLARE_GLOBAL_DATA_PTR;
30 static int pl01x_putc(struct pl01x_regs *regs, char c)
32 /* Wait until there is space in the FIFO */
33 if (readl(®s->fr) & UART_PL01x_FR_TXFF)
36 /* Send the character */
42 static int pl01x_getc(struct pl01x_regs *regs)
46 /* Wait until there is data in the FIFO */
47 if (readl(®s->fr) & UART_PL01x_FR_RXFE)
50 data = readl(®s->dr);
52 /* Check for an error flag */
53 if (data & 0xFFFFFF00) {
55 writel(0xFFFFFFFF, ®s->ecr);
62 static int pl01x_tstc(struct pl01x_regs *regs)
65 return !(readl(®s->fr) & UART_PL01x_FR_RXFE);
68 static int pl01x_generic_serial_init(struct pl01x_regs *regs,
73 #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT
74 if (type == TYPE_PL011) {
75 /* Empty RX fifo if necessary */
76 if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) {
77 while (!(readl(®s->fr) & UART_PL01x_FR_RXFE))
83 /* First, disable everything */
84 writel(0, ®s->pl010_cr);
86 /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */
87 lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN;
88 writel(lcr, ®s->pl011_lcrh);
94 #ifdef CONFIG_PL011_SERIAL_RLCR
98 * Program receive line control register after waiting
99 * 10 bus cycles. Delay be writing to readonly register
102 for (i = 0; i < 10; i++)
103 writel(lcr, ®s->fr);
105 writel(lcr, ®s->pl011_rlcr);
106 /* lcrh needs to be set again for change to be effective */
107 writel(lcr, ®s->pl011_lcrh);
118 static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type,
119 int clock, int baudrate)
123 unsigned int divisor;
127 divisor = UART_PL010_BAUD_9600;
130 divisor = UART_PL010_BAUD_9600;
133 divisor = UART_PL010_BAUD_38400;
136 divisor = UART_PL010_BAUD_57600;
139 divisor = UART_PL010_BAUD_115200;
142 divisor = UART_PL010_BAUD_38400;
145 writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm);
146 writel(divisor & 0xff, ®s->pl010_lcrl);
148 /* Finally, enable the UART */
149 writel(UART_PL010_CR_UARTEN, ®s->pl010_cr);
154 unsigned int divider;
155 unsigned int remainder;
156 unsigned int fraction;
161 * IBRD = UART_CLK / (16 * BAUD_RATE)
162 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE)))
163 * / (16 * BAUD_RATE))
165 temp = 16 * baudrate;
166 divider = clock / temp;
167 remainder = clock % temp;
168 temp = (8 * remainder) / baudrate;
169 fraction = (temp >> 1) + (temp & 1);
171 writel(divider, ®s->pl011_ibrd);
172 writel(fraction, ®s->pl011_fbrd);
174 /* Finally, enable the UART */
175 writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE |
176 UART_PL011_CR_RXE | UART_PL011_CR_RTS, ®s->pl011_cr);
186 #ifndef CONFIG_DM_SERIAL
187 static void pl01x_serial_init_baud(int baudrate)
191 #if defined(CONFIG_PL010_SERIAL)
192 pl01x_type = TYPE_PL010;
193 #elif defined(CONFIG_PL011_SERIAL)
194 pl01x_type = TYPE_PL011;
195 clock = CONFIG_PL011_CLOCK;
197 base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX];
199 pl01x_generic_serial_init(base_regs, pl01x_type);
200 pl01x_generic_setbrg(base_regs, TYPE_PL010, clock, baudrate);
204 * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1
205 * Integrator CP has two UARTs, use the first one, at 38400-8-N-1
206 * Versatile PB has four UARTs.
208 int pl01x_serial_init(void)
210 pl01x_serial_init_baud(CONFIG_BAUDRATE);
215 static void pl01x_serial_putc(const char c)
218 while (pl01x_putc(base_regs, '\r') == -EAGAIN);
220 while (pl01x_putc(base_regs, c) == -EAGAIN);
223 static int pl01x_serial_getc(void)
226 int ch = pl01x_getc(base_regs);
237 static int pl01x_serial_tstc(void)
239 return pl01x_tstc(base_regs);
242 static void pl01x_serial_setbrg(void)
245 * Flush FIFO and wait for non-busy before changing baudrate to avoid
248 while (!(readl(&base_regs->fr) & UART_PL01x_FR_TXFE))
250 while (readl(&base_regs->fr) & UART_PL01x_FR_BUSY)
252 pl01x_serial_init_baud(gd->baudrate);
255 static struct serial_device pl01x_serial_drv = {
256 .name = "pl01x_serial",
257 .start = pl01x_serial_init,
259 .setbrg = pl01x_serial_setbrg,
260 .putc = pl01x_serial_putc,
261 .puts = default_serial_puts,
262 .getc = pl01x_serial_getc,
263 .tstc = pl01x_serial_tstc,
266 void pl01x_serial_initialize(void)
268 serial_register(&pl01x_serial_drv);
271 __weak struct serial_device *default_serial_console(void)
273 return &pl01x_serial_drv;
276 #endif /* nCONFIG_DM_SERIAL */