Merge branch 'master' of git://git.denx.de/u-boot-mips
[platform/kernel/u-boot.git] / 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  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9 #include <fdtdec.h>
10 #include <watchdog.h>
11 #include <asm/io.h>
12 #include <linux/compiler.h>
13 #include <serial.h>
14 #include <asm/arch/clk.h>
15 #include <asm/arch/hardware.h>
16
17 DECLARE_GLOBAL_DATA_PTR;
18
19 #define ZYNQ_UART_SR_TXFULL     0x00000010 /* TX FIFO full */
20 #define ZYNQ_UART_SR_RXEMPTY    0x00000002 /* RX FIFO empty */
21
22 #define ZYNQ_UART_CR_TX_EN      0x00000010 /* TX enabled */
23 #define ZYNQ_UART_CR_RX_EN      0x00000004 /* RX enabled */
24 #define ZYNQ_UART_CR_TXRST      0x00000002 /* TX logic reset */
25 #define ZYNQ_UART_CR_RXRST      0x00000001 /* RX logic reset */
26
27 #define ZYNQ_UART_MR_PARITY_NONE        0x00000020  /* No parity mode */
28
29 struct uart_zynq {
30         u32 control; /* Control Register [8:0] */
31         u32 mode; /* Mode Register [10:0] */
32         u32 reserved1[4];
33         u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
34         u32 reserved2[4];
35         u32 channel_sts; /* Channel Status [11:0] */
36         u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
37         u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
38 };
39
40 static struct uart_zynq *uart_zynq_ports[2] = {
41         [0] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR0,
42         [1] = (struct uart_zynq *)ZYNQ_SERIAL_BASEADDR1,
43 };
44
45 #if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE0)
46 # define CONFIG_ZYNQ_SERIAL_BAUDRATE0   CONFIG_BAUDRATE
47 #endif
48 #if !defined(CONFIG_ZYNQ_SERIAL_BAUDRATE1)
49 # define CONFIG_ZYNQ_SERIAL_BAUDRATE1   CONFIG_BAUDRATE
50 #endif
51
52 struct uart_zynq_params {
53         u32 baudrate;
54 };
55
56 static struct uart_zynq_params uart_zynq_ports_param[2] = {
57         [0].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE0,
58         [1].baudrate = CONFIG_ZYNQ_SERIAL_BAUDRATE1,
59 };
60
61 /* Set up the baud rate in gd struct */
62 static void uart_zynq_serial_setbrg(const int port)
63 {
64         /* Calculation results. */
65         unsigned int calc_bauderror, bdiv, bgen;
66         unsigned long calc_baud = 0;
67         unsigned long baud = uart_zynq_ports_param[port].baudrate;
68         unsigned long clock = get_uart_clk(port);
69         struct uart_zynq *regs = uart_zynq_ports[port];
70
71         /*                master clock
72          * Baud rate = ------------------
73          *              bgen * (bdiv + 1)
74          *
75          * Find acceptable values for baud generation.
76          */
77         for (bdiv = 4; bdiv < 255; bdiv++) {
78                 bgen = clock / (baud * (bdiv + 1));
79                 if (bgen < 2 || bgen > 65535)
80                         continue;
81
82                 calc_baud = clock / (bgen * (bdiv + 1));
83
84                 /*
85                  * Use first calculated baudrate with
86                  * an acceptable (<3%) error
87                  */
88                 if (baud > calc_baud)
89                         calc_bauderror = baud - calc_baud;
90                 else
91                         calc_bauderror = calc_baud - baud;
92                 if (((calc_bauderror * 100) / baud) < 3)
93                         break;
94         }
95
96         writel(bdiv, &regs->baud_rate_divider);
97         writel(bgen, &regs->baud_rate_gen);
98 }
99
100 /* Initialize the UART, with...some settings. */
101 static int uart_zynq_serial_init(const int port)
102 {
103         struct uart_zynq *regs = uart_zynq_ports[port];
104
105         if (!regs)
106                 return -1;
107
108         /* RX/TX enabled & reset */
109         writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \
110                                         ZYNQ_UART_CR_RXRST, &regs->control);
111         writel(ZYNQ_UART_MR_PARITY_NONE, &regs->mode); /* 8 bit, no parity */
112         uart_zynq_serial_setbrg(port);
113
114         return 0;
115 }
116
117 static void uart_zynq_serial_putc(const char c, const int port)
118 {
119         struct uart_zynq *regs = uart_zynq_ports[port];
120
121         while ((readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
122                 WATCHDOG_RESET();
123
124         if (c == '\n') {
125                 writel('\r', &regs->tx_rx_fifo);
126                 while ((readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
127                         WATCHDOG_RESET();
128         }
129         writel(c, &regs->tx_rx_fifo);
130 }
131
132 static void uart_zynq_serial_puts(const char *s, const int port)
133 {
134         while (*s)
135                 uart_zynq_serial_putc(*s++, port);
136 }
137
138 static int uart_zynq_serial_tstc(const int port)
139 {
140         struct uart_zynq *regs = uart_zynq_ports[port];
141
142         return (readl(&regs->channel_sts) & ZYNQ_UART_SR_RXEMPTY) == 0;
143 }
144
145 static int uart_zynq_serial_getc(const int port)
146 {
147         struct uart_zynq *regs = uart_zynq_ports[port];
148
149         while (!uart_zynq_serial_tstc(port))
150                 WATCHDOG_RESET();
151         return readl(&regs->tx_rx_fifo);
152 }
153
154 /* Multi serial device functions */
155 #define DECLARE_PSSERIAL_FUNCTIONS(port) \
156         int uart_zynq##port##_init(void) \
157                                 { return uart_zynq_serial_init(port); } \
158         void uart_zynq##port##_setbrg(void) \
159                                 { return uart_zynq_serial_setbrg(port); } \
160         int uart_zynq##port##_getc(void) \
161                                 { return uart_zynq_serial_getc(port); } \
162         int uart_zynq##port##_tstc(void) \
163                                 { return uart_zynq_serial_tstc(port); } \
164         void uart_zynq##port##_putc(const char c) \
165                                 { uart_zynq_serial_putc(c, port); } \
166         void uart_zynq##port##_puts(const char *s) \
167                                 { uart_zynq_serial_puts(s, port); }
168
169 /* Serial device descriptor */
170 #define INIT_PSSERIAL_STRUCTURE(port, __name) { \
171           .name   = __name,                     \
172           .start  = uart_zynq##port##_init,     \
173           .stop   = NULL,                       \
174           .setbrg = uart_zynq##port##_setbrg,   \
175           .getc   = uart_zynq##port##_getc,     \
176           .tstc   = uart_zynq##port##_tstc,     \
177           .putc   = uart_zynq##port##_putc,     \
178           .puts   = uart_zynq##port##_puts,     \
179 }
180
181 DECLARE_PSSERIAL_FUNCTIONS(0);
182 struct serial_device uart_zynq_serial0_device =
183         INIT_PSSERIAL_STRUCTURE(0, "ttyPS0");
184 DECLARE_PSSERIAL_FUNCTIONS(1);
185 struct serial_device uart_zynq_serial1_device =
186         INIT_PSSERIAL_STRUCTURE(1, "ttyPS1");
187
188 #ifdef CONFIG_OF_CONTROL
189 __weak struct serial_device *default_serial_console(void)
190 {
191         const void *blob = gd->fdt_blob;
192         int node;
193         unsigned int base_addr;
194
195         node = fdt_path_offset(blob, "serial0");
196         if (node < 0)
197                 return NULL;
198
199         base_addr = fdtdec_get_addr(blob, node, "reg");
200         if (base_addr == FDT_ADDR_T_NONE)
201                 return NULL;
202
203         if (base_addr == ZYNQ_SERIAL_BASEADDR0)
204                 return &uart_zynq_serial0_device;
205
206         if (base_addr == ZYNQ_SERIAL_BASEADDR1)
207                 return &uart_zynq_serial1_device;
208
209         return NULL;
210 }
211 #else
212 __weak struct serial_device *default_serial_console(void)
213 {
214 #if defined(CONFIG_ZYNQ_SERIAL_UART0)
215         if (uart_zynq_ports[0])
216                 return &uart_zynq_serial0_device;
217 #endif
218 #if defined(CONFIG_ZYNQ_SERIAL_UART1)
219         if (uart_zynq_ports[1])
220                 return &uart_zynq_serial1_device;
221 #endif
222         return NULL;
223 }
224 #endif
225
226 void zynq_serial_initalize(void)
227 {
228         serial_register(&uart_zynq_serial0_device);
229         serial_register(&uart_zynq_serial1_device);
230 }