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 13:11:27 -0500 |
commit | 06a4c710673184f5c750bdb2f8579e0ae1cc252c (patch) | |
tree | 31a5984b7badc4d0c144ce22d8a67082c53d0cfd | |
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>
-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) | |||
2413 | serial8250_do_shutdown(port); | 2413 | serial8250_do_shutdown(port); |
2414 | } | 2414 | } |
2415 | 2415 | ||
2416 | static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned int baud) | 2416 | /* |
2417 | * XR17V35x UARTs have an extra fractional divisor register (DLD) | ||
2418 | * Calculate divisor with extra 4-bit fractional portion | ||
2419 | */ | ||
2420 | static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up, | ||
2421 | unsigned int baud, | ||
2422 | unsigned int *frac) | ||
2423 | { | ||
2424 | struct uart_port *port = &up->port; | ||
2425 | unsigned int quot_16; | ||
2426 | |||
2427 | quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud); | ||
2428 | *frac = quot_16 & 0x0f; | ||
2429 | |||
2430 | return quot_16 >> 4; | ||
2431 | } | ||
2432 | |||
2433 | static unsigned int serial8250_get_divisor(struct uart_8250_port *up, | ||
2434 | unsigned int baud, | ||
2435 | unsigned int *frac) | ||
2417 | { | 2436 | { |
2418 | struct uart_port *port = &up->port; | 2437 | struct uart_port *port = &up->port; |
2419 | unsigned int quot; | 2438 | unsigned int quot; |
@@ -2421,6 +2440,7 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i | |||
2421 | /* | 2440 | /* |
2422 | * Handle magic divisors for baud rates above baud_base on | 2441 | * Handle magic divisors for baud rates above baud_base on |
2423 | * SMSC SuperIO chips. | 2442 | * SMSC SuperIO chips. |
2443 | * | ||
2424 | */ | 2444 | */ |
2425 | if ((port->flags & UPF_MAGIC_MULTIPLIER) && | 2445 | if ((port->flags & UPF_MAGIC_MULTIPLIER) && |
2426 | baud == (port->uartclk/4)) | 2446 | baud == (port->uartclk/4)) |
@@ -2428,6 +2448,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up, unsigned i | |||
2428 | else if ((port->flags & UPF_MAGIC_MULTIPLIER) && | 2448 | else if ((port->flags & UPF_MAGIC_MULTIPLIER) && |
2429 | baud == (port->uartclk/8)) | 2449 | baud == (port->uartclk/8)) |
2430 | quot = 0x8002; | 2450 | quot = 0x8002; |
2451 | else if (up->port.type == PORT_XR17V35X) | ||
2452 | quot = xr17v35x_get_divisor(up, baud, frac); | ||
2431 | else | 2453 | else |
2432 | quot = uart_get_divisor(port, baud); | 2454 | quot = uart_get_divisor(port, baud); |
2433 | 2455 | ||
@@ -2479,7 +2501,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, | |||
2479 | } | 2501 | } |
2480 | 2502 | ||
2481 | void serial8250_set_divisor(struct uart_port *port, unsigned int baud, | 2503 | void serial8250_set_divisor(struct uart_port *port, unsigned int baud, |
2482 | unsigned int quot) | 2504 | unsigned int quot, unsigned int quot_frac) |
2483 | { | 2505 | { |
2484 | struct uart_8250_port *up = up_to_u8250p(port); | 2506 | struct uart_8250_port *up = up_to_u8250p(port); |
2485 | 2507 | ||
@@ -2503,23 +2525,9 @@ void serial8250_set_divisor(struct uart_port *port, unsigned int baud, | |||
2503 | 2525 | ||
2504 | serial_dl_write(up, quot); | 2526 | serial_dl_write(up, quot); |
2505 | 2527 | ||
2506 | /* | 2528 | /* XR17V35x UARTs have an extra fractional divisor register (DLD) */ |
2507 | * XR17V35x UARTs have an extra fractional divisor register (DLD) | 2529 | if (up->port.type == PORT_XR17V35X) |
2508 | * | 2530 | serial_port_out(port, 0x2, quot_frac); |
2509 | * We need to recalculate all of the registers, because DLM and DLL | ||
2510 | * are already rounded to a whole integer. | ||
2511 | * | ||
2512 | * When recalculating we use a 32x clock instead of a 16x clock to | ||
2513 | * allow 1-bit for rounding in the fractional part. | ||
2514 | */ | ||
2515 | if (up->port.type == PORT_XR17V35X) { | ||
2516 | unsigned int baud_x32 = (port->uartclk * 2) / baud; | ||
2517 | u16 quot = baud_x32 / 32; | ||
2518 | u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2); | ||
2519 | |||
2520 | serial_dl_write(up, quot); | ||
2521 | serial_port_out(port, 0x2, quot_frac & 0xf); | ||
2522 | } | ||
2523 | } | 2531 | } |
2524 | 2532 | ||
2525 | void | 2533 | void |
@@ -2529,7 +2537,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, | |||
2529 | struct uart_8250_port *up = up_to_u8250p(port); | 2537 | struct uart_8250_port *up = up_to_u8250p(port); |
2530 | unsigned char cval; | 2538 | unsigned char cval; |
2531 | unsigned long flags; | 2539 | unsigned long flags; |
2532 | unsigned int baud, quot; | 2540 | unsigned int baud, quot, frac = 0; |
2533 | 2541 | ||
2534 | cval = serial8250_compute_lcr(up, termios->c_cflag); | 2542 | cval = serial8250_compute_lcr(up, termios->c_cflag); |
2535 | 2543 | ||
@@ -2539,7 +2547,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, | |||
2539 | baud = uart_get_baud_rate(port, termios, old, | 2547 | baud = uart_get_baud_rate(port, termios, old, |
2540 | port->uartclk / 16 / 0xffff, | 2548 | port->uartclk / 16 / 0xffff, |
2541 | port->uartclk / 16); | 2549 | port->uartclk / 16); |
2542 | quot = serial8250_get_divisor(up, baud); | 2550 | quot = serial8250_get_divisor(up, baud, &frac); |
2543 | 2551 | ||
2544 | if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { | 2552 | if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { |
2545 | /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ | 2553 | /* 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, | |||
2636 | serial_port_out(port, UART_EFR, efr); | 2644 | serial_port_out(port, UART_EFR, efr); |
2637 | } | 2645 | } |
2638 | 2646 | ||
2639 | serial8250_set_divisor(port, baud, quot); | 2647 | serial8250_set_divisor(port, baud, quot, frac); |
2640 | 2648 | ||
2641 | /* | 2649 | /* |
2642 | * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR | 2650 | * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR |