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