summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-01-22 12:24:29 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-02-02 10:11:27 -0800
commit06a4c710673184f5c750bdb2f8579e0ae1cc252c (patch)
tree31a5984b7badc4d0c144ce22d8a67082c53d0cfd
parente72abd5da0730dfc4c8f566f7896c15c2d1bb88f (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>
-rw-r--r--drivers/tty/serial/8250/8250_core.c52
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