]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/serial/serial_pl01x.c
665a0e48b1d6396f9ae0d674fa62977608aa6d99
[karo-tx-uboot.git] / drivers / serial / serial_pl01x.c
1 /*
2  * (C) Copyright 2000
3  * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
4  *
5  * (C) Copyright 2004
6  * ARM Ltd.
7  * Philippe Robin, <philippe.robin@arm.com>
8  *
9  * SPDX-License-Identifier:     GPL-2.0+
10  */
11
12 /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */
13
14 #include <common.h>
15 #include <errno.h>
16 #include <watchdog.h>
17 #include <asm/io.h>
18 #include <serial.h>
19 #include <serial_pl01x.h>
20 #include <linux/compiler.h>
21 #include "serial_pl01x_internal.h"
22
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]))
27
28 DECLARE_GLOBAL_DATA_PTR;
29
30 static int pl01x_putc(struct pl01x_regs *regs, char c)
31 {
32         /* Wait until there is space in the FIFO */
33         if (readl(&regs->fr) & UART_PL01x_FR_TXFF)
34                 return -EAGAIN;
35
36         /* Send the character */
37         writel(c, &regs->dr);
38
39         return 0;
40 }
41
42 static int pl01x_getc(struct pl01x_regs *regs)
43 {
44         unsigned int data;
45
46         /* Wait until there is data in the FIFO */
47         if (readl(&regs->fr) & UART_PL01x_FR_RXFE)
48                 return -EAGAIN;
49
50         data = readl(&regs->dr);
51
52         /* Check for an error flag */
53         if (data & 0xFFFFFF00) {
54                 /* Clear the error */
55                 writel(0xFFFFFFFF, &regs->ecr);
56                 return -1;
57         }
58
59         return (int) data;
60 }
61
62 static int pl01x_tstc(struct pl01x_regs *regs)
63 {
64         WATCHDOG_RESET();
65         return !(readl(&regs->fr) & UART_PL01x_FR_RXFE);
66 }
67
68 static int pl01x_generic_serial_init(struct pl01x_regs *regs,
69                                      enum pl01x_type type)
70 {
71         unsigned int lcr;
72
73 #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT
74         if (type == TYPE_PL011) {
75                 /* Empty RX fifo if necessary */
76                 if (readl(&regs->pl011_cr) & UART_PL011_CR_UARTEN) {
77                         while (!(readl(&regs->fr) & UART_PL01x_FR_RXFE))
78                                 readl(&regs->dr);
79                 }
80         }
81 #endif
82
83         /* First, disable everything */
84         writel(0, &regs->pl010_cr);
85
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, &regs->pl011_lcrh);
89
90         switch (type) {
91         case TYPE_PL010:
92                 break;
93         case TYPE_PL011: {
94 #ifdef CONFIG_PL011_SERIAL_RLCR
95                 int i;
96
97                 /*
98                  * Program receive line control register after waiting
99                  * 10 bus cycles.  Delay be writing to readonly register
100                  * 10 times
101                  */
102                 for (i = 0; i < 10; i++)
103                         writel(lcr, &regs->fr);
104
105                 writel(lcr, &regs->pl011_rlcr);
106                 /* lcrh needs to be set again for change to be effective */
107                 writel(lcr, &regs->pl011_lcrh);
108 #endif
109                 break;
110         }
111         default:
112                 return -EINVAL;
113         }
114
115         return 0;
116 }
117
118 static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type,
119                                 int clock, int baudrate)
120 {
121         switch (type) {
122         case TYPE_PL010: {
123                 unsigned int divisor;
124
125                 switch (baudrate) {
126                 case 9600:
127                         divisor = UART_PL010_BAUD_9600;
128                         break;
129                 case 19200:
130                         divisor = UART_PL010_BAUD_9600;
131                         break;
132                 case 38400:
133                         divisor = UART_PL010_BAUD_38400;
134                         break;
135                 case 57600:
136                         divisor = UART_PL010_BAUD_57600;
137                         break;
138                 case 115200:
139                         divisor = UART_PL010_BAUD_115200;
140                         break;
141                 default:
142                         divisor = UART_PL010_BAUD_38400;
143                 }
144
145                 writel((divisor & 0xf00) >> 8, &regs->pl010_lcrm);
146                 writel(divisor & 0xff, &regs->pl010_lcrl);
147
148                 /* Finally, enable the UART */
149                 writel(UART_PL010_CR_UARTEN, &regs->pl010_cr);
150                 break;
151         }
152         case TYPE_PL011: {
153                 unsigned int temp;
154                 unsigned int divider;
155                 unsigned int remainder;
156                 unsigned int fraction;
157
158                 /*
159                 * Set baud rate
160                 *
161                 * IBRD = UART_CLK / (16 * BAUD_RATE)
162                 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE)))
163                 *               / (16 * BAUD_RATE))
164                 */
165                 temp = 16 * baudrate;
166                 divider = clock / temp;
167                 remainder = clock % temp;
168                 temp = (8 * remainder) / baudrate;
169                 fraction = (temp >> 1) + (temp & 1);
170
171                 writel(divider, &regs->pl011_ibrd);
172                 writel(fraction, &regs->pl011_fbrd);
173
174                 /* Finally, enable the UART */
175                 writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE |
176                        UART_PL011_CR_RXE | UART_PL011_CR_RTS, &regs->pl011_cr);
177                 break;
178         }
179         default:
180                 return -EINVAL;
181         }
182
183         return 0;
184 }
185
186 #ifndef CONFIG_DM_SERIAL
187 static void pl01x_serial_init_baud(int baudrate)
188 {
189         int clock = 0;
190
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;
196 #endif
197         base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX];
198
199         pl01x_generic_serial_init(base_regs, pl01x_type);
200         pl01x_generic_setbrg(base_regs, TYPE_PL010, clock, baudrate);
201 }
202
203 /*
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.
207  */
208 int pl01x_serial_init(void)
209 {
210         pl01x_serial_init_baud(CONFIG_BAUDRATE);
211
212         return 0;
213 }
214
215 static void pl01x_serial_putc(const char c)
216 {
217         if (c == '\n')
218                 while (pl01x_putc(base_regs, '\r') == -EAGAIN);
219
220         while (pl01x_putc(base_regs, c) == -EAGAIN);
221 }
222
223 static int pl01x_serial_getc(void)
224 {
225         while (1) {
226                 int ch = pl01x_getc(base_regs);
227
228                 if (ch == -EAGAIN) {
229                         WATCHDOG_RESET();
230                         continue;
231                 }
232
233                 return ch;
234         }
235 }
236
237 static int pl01x_serial_tstc(void)
238 {
239         return pl01x_tstc(base_regs);
240 }
241
242 static void pl01x_serial_setbrg(void)
243 {
244         /*
245          * Flush FIFO and wait for non-busy before changing baudrate to avoid
246          * crap in console
247          */
248         while (!(readl(&base_regs->fr) & UART_PL01x_FR_TXFE))
249                 WATCHDOG_RESET();
250         while (readl(&base_regs->fr) & UART_PL01x_FR_BUSY)
251                 WATCHDOG_RESET();
252         pl01x_serial_init_baud(gd->baudrate);
253 }
254
255 static struct serial_device pl01x_serial_drv = {
256         .name   = "pl01x_serial",
257         .start  = pl01x_serial_init,
258         .stop   = NULL,
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,
264 };
265
266 void pl01x_serial_initialize(void)
267 {
268         serial_register(&pl01x_serial_drv);
269 }
270
271 __weak struct serial_device *default_serial_console(void)
272 {
273         return &pl01x_serial_drv;
274 }
275
276 #endif /* nCONFIG_DM_SERIAL */