ar71xx: add support for 3.7
[openwrt/staging/yousong.git] / target / linux / ar71xx / patches-3.7 / 004-tty-serial-ar933x_uart-fix-baud-rate-calculation.patch
1 From 2dff8ad92661b6c99e9ba8b5918e43b522551556 Mon Sep 17 00:00:00 2001
2 From: Gabor Juhos <juhosg@openwrt.org>
3 Date: Wed, 14 Nov 2012 10:38:13 +0100
4 Subject: [PATCH] tty/serial/ar933x_uart: fix baud rate calculation
5
6 commit 2dff8ad92661b6c99e9ba8b5918e43b522551556 upstream.
7
8 The UART of the AR933x SoC implements a fractional divisor
9 for generating the desired baud rate.
10
11 The current code uses a fixed value for the fractional
12 part of the divisor, and this leads to improperly
13 calculated baud rates:
14
15 baud scale step real baud diff
16 300 5207* 8192 17756 17456 5818.66%
17 600 2603* 8192 35511 34911 5818.50%
18 1200 1301* 8192 71023 69823 5818.58%
19 2400 650* 8192 11241 8841 368.37%
20 4800 324* 8192 22645 17845 371.77%
21 9600 161 8192 9645 45 0.46%
22 14400 107 8192 14468 68 0.47%
23 19200 80 8192 19290 90 0.46%
24 28800 53 8192 28935 135 0.46%
25 38400 39 8192 39063 663 1.72%
26 57600 26 8192 57870 270 0.46%
27 115200 12 8192 120192 4992 4.33%
28 230400 5 8192 260417 30017 13.02%
29 460800 2 8192 520833 60033 13.02%
30 921600 0 8192 1562500 640900 69.93%
31
32 After the patch, the integer and fractional parts of the
33 divisor will be calculated dynamically. This ensures that
34 the UART will use correct baud rates:
35
36 baud scale step real baud diff
37 300 6 11 300 0 0.00%
38 600 54 173 600 0 0.00%
39 1200 30 195 1200 0 0.00%
40 2400 30 390 2400 0 0.00%
41 4800 48 1233 4800 0 0.00%
42 9600 78 3976 9600 0 0.00%
43 14400 98 7474 14400 0 0.00%
44 19200 55 5637 19200 0 0.00%
45 28800 130 19780 28800 0 0.00%
46 38400 36 7449 38400 0 0.00%
47 57600 78 23857 57600 0 0.00%
48 115200 43 26575 115200 0 0.00%
49 230400 23 28991 230400 0 0.00%
50 460800 11 28991 460800 0 0.00%
51 921600 5 28991 921599 -1 0.00%
52
53 Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
54 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
55 ---
56 drivers/tty/serial/ar933x_uart.c | 90 +++++++++++++++++++++++++++++++++++---
57 1 file changed, 85 insertions(+), 5 deletions(-)
58
59 --- a/drivers/tty/serial/ar933x_uart.c
60 +++ b/drivers/tty/serial/ar933x_uart.c
61 @@ -25,11 +25,19 @@
62 #include <linux/io.h>
63 #include <linux/irq.h>
64
65 +#include <asm/div64.h>
66 +
67 #include <asm/mach-ath79/ar933x_uart.h>
68 #include <asm/mach-ath79/ar933x_uart_platform.h>
69
70 #define DRIVER_NAME "ar933x-uart"
71
72 +#define AR933X_UART_MAX_SCALE 0xff
73 +#define AR933X_UART_MAX_STEP 0xffff
74 +
75 +#define AR933X_UART_MIN_BAUD 300
76 +#define AR933X_UART_MAX_BAUD 3000000
77 +
78 #define AR933X_DUMMY_STATUS_RD 0x01
79
80 static struct uart_driver ar933x_uart_driver;
81 @@ -37,6 +45,8 @@ static struct uart_driver ar933x_uart_dr
82 struct ar933x_uart_port {
83 struct uart_port port;
84 unsigned int ier; /* shadow Interrupt Enable Register */
85 + unsigned int min_baud;
86 + unsigned int max_baud;
87 };
88
89 static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
90 @@ -162,6 +172,57 @@ static void ar933x_uart_enable_ms(struct
91 {
92 }
93
94 +/*
95 + * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17))
96 + */
97 +static unsigned long ar933x_uart_get_baud(unsigned int clk,
98 + unsigned int scale,
99 + unsigned int step)
100 +{
101 + u64 t;
102 + u32 div;
103 +
104 + div = (2 << 16) * (scale + 1);
105 + t = clk;
106 + t *= step;
107 + t += (div / 2);
108 + do_div(t, div);
109 +
110 + return t;
111 +}
112 +
113 +static void ar933x_uart_get_scale_step(unsigned int clk,
114 + unsigned int baud,
115 + unsigned int *scale,
116 + unsigned int *step)
117 +{
118 + unsigned int tscale;
119 + long min_diff;
120 +
121 + *scale = 0;
122 + *step = 0;
123 +
124 + min_diff = baud;
125 + for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) {
126 + u64 tstep;
127 + int diff;
128 +
129 + tstep = baud * (tscale + 1);
130 + tstep *= (2 << 16);
131 + do_div(tstep, clk);
132 +
133 + if (tstep > AR933X_UART_MAX_STEP)
134 + break;
135 +
136 + diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud);
137 + if (diff < min_diff) {
138 + min_diff = diff;
139 + *scale = tscale;
140 + *step = tstep;
141 + }
142 + }
143 +}
144 +
145 static void ar933x_uart_set_termios(struct uart_port *port,
146 struct ktermios *new,
147 struct ktermios *old)
148 @@ -169,7 +230,7 @@ static void ar933x_uart_set_termios(stru
149 struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
150 unsigned int cs;
151 unsigned long flags;
152 - unsigned int baud, scale;
153 + unsigned int baud, scale, step;
154
155 /* Only CS8 is supported */
156 new->c_cflag &= ~CSIZE;
157 @@ -191,8 +252,8 @@ static void ar933x_uart_set_termios(stru
158 /* Mark/space parity is not supported */
159 new->c_cflag &= ~CMSPAR;
160
161 - baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
162 - scale = (port->uartclk / (16 * baud)) - 1;
163 + baud = uart_get_baud_rate(port, new, old, up->min_baud, up->max_baud);
164 + ar933x_uart_get_scale_step(port->uartclk, baud, &scale, &step);
165
166 /*
167 * Ok, we're now changing the port state. Do it with
168 @@ -200,6 +261,10 @@ static void ar933x_uart_set_termios(stru
169 */
170 spin_lock_irqsave(&up->port.lock, flags);
171
172 + /* disable the UART */
173 + ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
174 + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S);
175 +
176 /* Update the per-port timeout. */
177 uart_update_timeout(port, new->c_cflag, baud);
178
179 @@ -210,7 +275,7 @@ static void ar933x_uart_set_termios(stru
180 up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD;
181
182 ar933x_uart_write(up, AR933X_UART_CLOCK_REG,
183 - scale << AR933X_UART_CLOCK_SCALE_S | 8192);
184 + scale << AR933X_UART_CLOCK_SCALE_S | step);
185
186 /* setup configuration register */
187 ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs);
188 @@ -219,6 +284,11 @@ static void ar933x_uart_set_termios(stru
189 ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
190 AR933X_UART_CS_HOST_INT_EN);
191
192 + /* reenable the UART */
193 + ar933x_uart_rmw(up, AR933X_UART_CS_REG,
194 + AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S,
195 + AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
196 +
197 spin_unlock_irqrestore(&up->port.lock, flags);
198
199 if (tty_termios_baud_rate(new))
200 @@ -401,6 +471,8 @@ static void ar933x_uart_config_port(stru
201 static int ar933x_uart_verify_port(struct uart_port *port,
202 struct serial_struct *ser)
203 {
204 + struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
205 +
206 if (ser->type != PORT_UNKNOWN &&
207 ser->type != PORT_AR933X)
208 return -EINVAL;
209 @@ -408,7 +480,8 @@ static int ar933x_uart_verify_port(struc
210 if (ser->irq < 0 || ser->irq >= NR_IRQS)
211 return -EINVAL;
212
213 - if (ser->baud_base < 28800)
214 + if (ser->baud_base < up->min_baud ||
215 + ser->baud_base > up->max_baud)
216 return -EINVAL;
217
218 return 0;
219 @@ -561,6 +634,7 @@ static int __devinit ar933x_uart_probe(s
220 struct uart_port *port;
221 struct resource *mem_res;
222 struct resource *irq_res;
223 + unsigned int baud;
224 int id;
225 int ret;
226
227 @@ -611,6 +685,12 @@ static int __devinit ar933x_uart_probe(s
228 port->fifosize = AR933X_UART_FIFO_SIZE;
229 port->ops = &ar933x_uart_ops;
230
231 + baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1);
232 + up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD);
233 +
234 + baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
235 + up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
236 +
237 ar933x_uart_add_console_port(up);
238
239 ret = uart_add_one_port(&ar933x_uart_driver, &up->port);