Commit 194846f398ef38d5e24c239d264a3f637c685e53
Committed by
Albert ARIBAUD
1 parent
76abfa5781
Exists in
master
and in
54 other branches
serial: Add Zynq serial driver
The driver is used on Xilinx Zynq platform. Signed-off-by: Michal Simek <monstr@monstr.eu> CC: Joe Hershberger <joe.hershberger@gmail.com> CC: Marek Vasut <marex@denx.de> Acked-by: Marek Vasut <marex@denx.de>
Showing 4 changed files with 261 additions and 0 deletions Side-by-side Diff
common/serial.c
... | ... | @@ -122,6 +122,14 @@ |
122 | 122 | serial_register(&uartlite_serial3_device); |
123 | 123 | # endif /* XILINX_UARTLITE_BASEADDR3 */ |
124 | 124 | #endif /* CONFIG_XILINX_UARTLITE */ |
125 | +#if defined(CONFIG_ZYNQ_SERIAL) | |
126 | +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0 | |
127 | + serial_register(&uart_zynq_serial0_device); | |
128 | +# endif | |
129 | +# ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1 | |
130 | + serial_register(&uart_zynq_serial1_device); | |
131 | +# endif | |
132 | +#endif | |
125 | 133 | serial_assign(default_serial_console()->name); |
126 | 134 | } |
127 | 135 |
drivers/serial/Makefile
... | ... | @@ -56,6 +56,7 @@ |
56 | 56 | COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o |
57 | 57 | COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o |
58 | 58 | COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o |
59 | +COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o | |
59 | 60 | |
60 | 61 | ifndef CONFIG_SPL_BUILD |
61 | 62 | COBJS-$(CONFIG_USB_TTY) += usbtty.o |
drivers/serial/serial_zynq.c
1 | +/* | |
2 | + * Copyright (C) 2012 Michal Simek <monstr@monstr.eu> | |
3 | + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. | |
4 | + * | |
5 | + * See file CREDITS for list of people who contributed to this | |
6 | + * project. | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or | |
9 | + * modify it under the terms of the GNU General Public License as | |
10 | + * published by the Free Software Foundation; either version 2 of | |
11 | + * the License, or (at your option) any later version. | |
12 | + * | |
13 | + * This program is distributed in the hope that it will be useful, | |
14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + * GNU General Public License for more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License | |
19 | + * along with this program; if not, write to the Free Software | |
20 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | + * MA 02111-1307 USA | |
22 | + */ | |
23 | + | |
24 | +#include <common.h> | |
25 | +#include <watchdog.h> | |
26 | +#include <asm/io.h> | |
27 | +#include <linux/compiler.h> | |
28 | +#include <serial.h> | |
29 | + | |
30 | +#define ZYNQ_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ | |
31 | +#define ZYNQ_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ | |
32 | + | |
33 | +#define ZYNQ_UART_CR_TX_EN 0x00000010 /* TX enabled */ | |
34 | +#define ZYNQ_UART_CR_RX_EN 0x00000004 /* RX enabled */ | |
35 | +#define ZYNQ_UART_CR_TXRST 0x00000002 /* TX logic reset */ | |
36 | +#define ZYNQ_UART_CR_RXRST 0x00000001 /* RX logic reset */ | |
37 | + | |
38 | +#define ZYNQ_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ | |
39 | + | |
40 | +/* Some clock/baud constants */ | |
41 | +#define ZYNQ_UART_BDIV 15 /* Default/reset BDIV value */ | |
42 | +#define ZYNQ_UART_BASECLK 3125000L /* master / (bdiv + 1) */ | |
43 | + | |
44 | +struct uart_zynq { | |
45 | + u32 control; /* Control Register [8:0] */ | |
46 | + u32 mode; /* Mode Register [10:0] */ | |
47 | + u32 reserved1[4]; | |
48 | + u32 baud_rate_gen; /* Baud Rate Generator [15:0] */ | |
49 | + u32 reserved2[4]; | |
50 | + u32 channel_sts; /* Channel Status [11:0] */ | |
51 | + u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */ | |
52 | + u32 baud_rate_divider; /* Baud Rate Divider [7:0] */ | |
53 | +}; | |
54 | + | |
55 | +static struct uart_zynq *uart_zynq_ports[2] = { | |
56 | +#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR0 | |
57 | + [0] = (struct uart_zynq *)CONFIG_ZYNQ_SERIAL_BASEADDR0, | |
58 | +#endif | |
59 | +#ifdef CONFIG_ZYNQ_SERIAL_BASEADDR1 | |
60 | + [1] = (struct uart_zynq *)CONFIG_ZYNQ_SERIAL_BASEADDR1, | |
61 | +#endif | |
62 | +}; | |
63 | + | |
64 | +struct uart_zynq_params { | |
65 | + u32 baudrate; | |
66 | + u32 clock; | |
67 | +}; | |
68 | + | |
69 | +static struct uart_zynq_params uart_zynq_ports_param[2] = { | |
70 | +#if defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0) && defined(CONFIG_ZYNQ_SERIAL_CLOCK0) | |
71 | + [0].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE0, | |
72 | + [0].clock = CONFIG_ZYNQ_SERIAL_CLOCK0, | |
73 | +#endif | |
74 | +#if defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1) && defined(CONFIG_ZYNQ_SERIAL_CLOCK1) | |
75 | + [1].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE1, | |
76 | + [1].clock = CONFIG_ZYNQ_SERIAL_CLOCK1, | |
77 | +#endif | |
78 | +}; | |
79 | + | |
80 | +/* Set up the baud rate in gd struct */ | |
81 | +static void uart_zynq_serial_setbrg(const int port) | |
82 | +{ | |
83 | + /* Calculation results. */ | |
84 | + unsigned int calc_bauderror, bdiv, bgen; | |
85 | + unsigned long calc_baud = 0; | |
86 | + unsigned long baud = uart_zynq_ports_param[port].baudrate; | |
87 | + unsigned long clock = uart_zynq_ports_param[port].clock; | |
88 | + struct uart_zynq *regs = uart_zynq_ports[port]; | |
89 | + | |
90 | + /* master clock | |
91 | + * Baud rate = ------------------ | |
92 | + * bgen * (bdiv + 1) | |
93 | + * | |
94 | + * Find acceptable values for baud generation. | |
95 | + */ | |
96 | + for (bdiv = 4; bdiv < 255; bdiv++) { | |
97 | + bgen = clock / (baud * (bdiv + 1)); | |
98 | + if (bgen < 2 || bgen > 65535) | |
99 | + continue; | |
100 | + | |
101 | + calc_baud = clock / (bgen * (bdiv + 1)); | |
102 | + | |
103 | + /* | |
104 | + * Use first calculated baudrate with | |
105 | + * an acceptable (<3%) error | |
106 | + */ | |
107 | + if (baud > calc_baud) | |
108 | + calc_bauderror = baud - calc_baud; | |
109 | + else | |
110 | + calc_bauderror = calc_baud - baud; | |
111 | + if (((calc_bauderror * 100) / baud) < 3) | |
112 | + break; | |
113 | + } | |
114 | + | |
115 | + writel(bdiv, ®s->baud_rate_divider); | |
116 | + writel(bgen, ®s->baud_rate_gen); | |
117 | +} | |
118 | + | |
119 | +/* Initialize the UART, with...some settings. */ | |
120 | +static int uart_zynq_serial_init(const int port) | |
121 | +{ | |
122 | + struct uart_zynq *regs = uart_zynq_ports[port]; | |
123 | + | |
124 | + if (!regs) | |
125 | + return -1; | |
126 | + | |
127 | + /* RX/TX enabled & reset */ | |
128 | + writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \ | |
129 | + ZYNQ_UART_CR_RXRST, ®s->control); | |
130 | + writel(ZYNQ_UART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */ | |
131 | + uart_zynq_serial_setbrg(port); | |
132 | + | |
133 | + return 0; | |
134 | +} | |
135 | + | |
136 | +static void uart_zynq_serial_putc(const char c, const int port) | |
137 | +{ | |
138 | + struct uart_zynq *regs = uart_zynq_ports[port]; | |
139 | + | |
140 | + while ((readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0) | |
141 | + WATCHDOG_RESET(); | |
142 | + | |
143 | + if (c == '\n') { | |
144 | + writel('\r', ®s->tx_rx_fifo); | |
145 | + while ((readl(®s->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0) | |
146 | + WATCHDOG_RESET(); | |
147 | + } | |
148 | + writel(c, ®s->tx_rx_fifo); | |
149 | +} | |
150 | + | |
151 | +static void uart_zynq_serial_puts(const char *s, const int port) | |
152 | +{ | |
153 | + while (*s) | |
154 | + uart_zynq_serial_putc(*s++, port); | |
155 | +} | |
156 | + | |
157 | +static int uart_zynq_serial_tstc(const int port) | |
158 | +{ | |
159 | + struct uart_zynq *regs = uart_zynq_ports[port]; | |
160 | + | |
161 | + return (readl(®s->channel_sts) & ZYNQ_UART_SR_RXEMPTY) == 0; | |
162 | +} | |
163 | + | |
164 | +static int uart_zynq_serial_getc(const int port) | |
165 | +{ | |
166 | + struct uart_zynq *regs = uart_zynq_ports[port]; | |
167 | + | |
168 | + while (!uart_zynq_serial_tstc(port)) | |
169 | + WATCHDOG_RESET(); | |
170 | + return readl(®s->tx_rx_fifo); | |
171 | +} | |
172 | + | |
173 | +#if !defined(CONFIG_SERIAL_MULTI) | |
174 | +int serial_init(void) | |
175 | +{ | |
176 | + return uart_zynq_serial_init(0); | |
177 | +} | |
178 | + | |
179 | +void serial_setbrg(void) | |
180 | +{ | |
181 | + uart_zynq_serial_setbrg(0); | |
182 | +} | |
183 | + | |
184 | +void serial_putc(const char c) | |
185 | +{ | |
186 | + uart_zynq_serial_putc(c, 0); | |
187 | +} | |
188 | + | |
189 | +void serial_puts(const char *s) | |
190 | +{ | |
191 | + uart_zynq_serial_puts(s, 0); | |
192 | +} | |
193 | + | |
194 | +int serial_getc(void) | |
195 | +{ | |
196 | + return uart_zynq_serial_getc(0); | |
197 | +} | |
198 | + | |
199 | +int serial_tstc(void) | |
200 | +{ | |
201 | + return uart_zynq_serial_tstc(0); | |
202 | +} | |
203 | +#else | |
204 | +/* Multi serial device functions */ | |
205 | +#define DECLARE_PSSERIAL_FUNCTIONS(port) \ | |
206 | + int uart_zynq##port##_init(void) \ | |
207 | + { return uart_zynq_serial_init(port); } \ | |
208 | + void uart_zynq##port##_setbrg(void) \ | |
209 | + { return uart_zynq_serial_setbrg(port); } \ | |
210 | + int uart_zynq##port##_getc(void) \ | |
211 | + { return uart_zynq_serial_getc(port); } \ | |
212 | + int uart_zynq##port##_tstc(void) \ | |
213 | + { return uart_zynq_serial_tstc(port); } \ | |
214 | + void uart_zynq##port##_putc(const char c) \ | |
215 | + { uart_zynq_serial_putc(c, port); } \ | |
216 | + void uart_zynq##port##_puts(const char *s) \ | |
217 | + { uart_zynq_serial_puts(s, port); } | |
218 | + | |
219 | +/* Serial device descriptor */ | |
220 | +#define INIT_PSSERIAL_STRUCTURE(port, __name) { \ | |
221 | + .name = __name, \ | |
222 | + .init = uart_zynq##port##_init, \ | |
223 | + .uninit = NULL, \ | |
224 | + .setbrg = uart_zynq##port##_setbrg, \ | |
225 | + .getc = uart_zynq##port##_getc, \ | |
226 | + .tstc = uart_zynq##port##_tstc, \ | |
227 | + .putc = uart_zynq##port##_putc, \ | |
228 | + .puts = uart_zynq##port##_puts, \ | |
229 | +} | |
230 | + | |
231 | +DECLARE_PSSERIAL_FUNCTIONS(0); | |
232 | +struct serial_device uart_zynq_serial0_device = | |
233 | + INIT_PSSERIAL_STRUCTURE(0, "ttyPS0"); | |
234 | +DECLARE_PSSERIAL_FUNCTIONS(1); | |
235 | +struct serial_device uart_zynq_serial1_device = | |
236 | + INIT_PSSERIAL_STRUCTURE(1, "ttyPS1"); | |
237 | + | |
238 | +__weak struct serial_device *default_serial_console(void) | |
239 | +{ | |
240 | + if (uart_zynq_ports[0]) | |
241 | + return &uart_zynq_serial0_device; | |
242 | + if (uart_zynq_ports[1]) | |
243 | + return &uart_zynq_serial1_device; | |
244 | + | |
245 | + return NULL; | |
246 | +} | |
247 | +#endif |
include/serial.h
... | ... | @@ -90,6 +90,11 @@ |
90 | 90 | extern struct serial_device bfin_serial3_device; |
91 | 91 | #endif |
92 | 92 | |
93 | +#if defined(CONFIG_ZYNQ_SERIAL) | |
94 | +extern struct serial_device uart_zynq_serial0_device; | |
95 | +extern struct serial_device uart_zynq_serial1_device; | |
96 | +#endif | |
97 | + | |
93 | 98 | extern void serial_register(struct serial_device *); |
94 | 99 | extern void serial_initialize(void); |
95 | 100 | extern void serial_stdio_init(void); |