diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2015-01-22 12:24:29 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-02-02 10:11:27 -0800 |
commit | 06a4c710673184f5c750bdb2f8579e0ae1cc252c (patch) | |
tree | 31a5984b7badc4d0c144ce22d8a67082c53d0cfd /drivers/tty/serial | |
parent | e72abd5da0730dfc4c8f566f7896c15c2d1bb88f (diff) |
serial: 8250: Refactor XR17V35X divisor calculation
Exar XR17V35X PCIe uarts support a 4-bit fractional divisor register.
Refactor the divisor calculation from the divisor programming.
Allow a fractional result from serial8250_get_divisor() and pass this
result to serial8250_dl_write().
Simplify the calculation for quot and quot_frac. This was verified
to be identical to the results of the original calculation with a test
jig.
NB: The results were also compared with the divisor value chart
on pg 33 of the Exar XR17V352 datasheet, rev 1.0.3, here:
http://www.exar.com/common/content/document.ashx?id=1585
which differs from the calculated values by 1 in the fractional result.
This is because the calculated values are still rounded in the
fractional result, whereas the table values are truncated. Note
that the data error rate % values in the datasheet are for
rounded fractional results, as the truncated fractional results
have more error.
Cc: Joe Schultz <jschultz@xes-inc.com>
Cc: Aaron Sierra <asierra@xes-inc.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 52 |
1 files changed, 30 insertions, 22 deletions
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index fd9e723926cd..c8ecfaf49eb4 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -2413,7 +2413,26 @@ static void serial8250_shutdown(struct uart_port *port) serial8250_do_shutdown(port); } -static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud) +/* + * XR17V35x UARTs have an extra fractional divisor register (DLD) + * Calculate divisor with extra 4-bit fractional portion + */ +static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, + unsigned int baud, + unsigned int *frac) +{ + struct uart_port *port = &up->port; + unsigned int quot_16; + + quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud); + *frac = quot_16 & 0x0f; + + return quot_16 >> 4; +} + +static unsigned int serial8250_get_divisor(struct uart_8250_port *up, + unsigned int baud, + unsigned int *frac) { struct uart_port *port = &up->port; unsigned int quot; @@ -2421,6 +2440,7 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i /* * Handle magic divisors for baud rates above baud_base on * SMSC SuperIO chips. + * */ if ((port->flags & UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/4)) @@ -2428,6 +2448,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i else if ((port->flags & UPF_MAGIC_MULTIPLIER) && baud == (port->uartclk/8)) quot = 0x8002; + else if (up->port.type == PORT_XR17V35X) + quot = xr17v35x_get_divisor(up, baud, frac); else quot = uart_get_divisor(port, baud); @@ -2479,7 +2501,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, } void serial8250_set_divisor(struct uart_port *port, unsigned int baud, - unsigned int quot) + unsigned int quot, unsigned int quot_frac) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2503,23 +2525,9 @@ void serial8250_set_divisor(struct uart_port *port, unsigned int baud, serial_dl_write(up, quot); - /* - * XR17V35x UARTs have an extra fractional divisor register (DLD) - * - * We need to recalculate all of the registers, because DLM and DLL - * are already rounded to a whole integer. - * - * When recalculating we use a 32x clock instead of a 16x clock to - * allow 1-bit for rounding in the fractional part. - */ - if (up->port.type == PORT_XR17V35X) { - unsigned int baud_x32 = (port->uartclk * 2) / baud; - u16 quot = baud_x32 / 32; - u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2); - - serial_dl_write(up, quot); - serial_port_out(port, 0x2, quot_frac & 0xf); - } + /* XR17V35x UARTs have an extra fractional divisor register (DLD) */ + if (up->port.type == PORT_XR17V35X) + serial_port_out(port, 0x2, quot_frac); } void @@ -2529,7 +2537,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct uart_8250_port *up = up_to_u8250p(port); unsigned char cval; unsigned long flags; - unsigned int baud, quot; + unsigned int baud, quot, frac = 0; cval = serial8250_compute_lcr(up, termios->c_cflag); @@ -2539,7 +2547,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, port->uartclk / 16 / 0xffff, port->uartclk / 16); - quot = serial8250_get_divisor(up, baud); + quot = serial8250_get_divisor(up, baud, &frac); if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ @@ -2636,7 +2644,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_EFR, efr); } - serial8250_set_divisor(port, baud, quot); + serial8250_set_divisor(port, baud, quot, frac); /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR |