tty/serial/ar933x_uart: fix baud rate calculation
The UART of the AR933x SoC implements a fractional divisor for generating the desired baud rate. The current code uses a fixed value for the fractional part of the divisor, and this leads to improperly calculated baud rates: baud scale step real baud diff 300 5207* 8192 17756 17456 5818.66% 600 2603* 8192 35511 34911 5818.50% 1200 1301* 8192 71023 69823 5818.58% 2400 650* 8192 11241 8841 368.37% 4800 324* 8192 22645 17845 371.77% 9600 161 8192 9645 45 0.46% 14400 107 8192 14468 68 0.47% 19200 80 8192 19290 90 0.46% 28800 53 8192 28935 135 0.46% 38400 39 8192 39063 663 1.72% 57600 26 8192 57870 270 0.46% 115200 12 8192 120192 4992 4.33% 230400 5 8192 260417 30017 13.02% 460800 2 8192 520833 60033 13.02% 921600 0 8192 1562500 640900 69.93% After the patch, the integer and fractional parts of the divisor will be calculated dynamically. This ensures that the UART will use correct baud rates: baud scale step real baud diff 300 6 11 300 0 0.00% 600 54 173 600 0 0.00% 1200 30 195 1200 0 0.00% 2400 30 390 2400 0 0.00% 4800 48 1233 4800 0 0.00% 9600 78 3976 9600 0 0.00% 14400 98 7474 14400 0 0.00% 19200 55 5637 19200 0 0.00% 28800 130 19780 28800 0 0.00% 38400 36 7449 38400 0 0.00% 57600 78 23857 57600 0 0.00% 115200 43 26575 115200 0 0.00% 230400 23 28991 230400 0 0.00% 460800 11 28991 460800 0 0.00% 921600 5 28991 921599 -1 0.00% Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
5318609519
commit
2dff8ad926
@@ -25,11 +25,19 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
|
||||||
|
#include <asm/div64.h>
|
||||||
|
|
||||||
#include <asm/mach-ath79/ar933x_uart.h>
|
#include <asm/mach-ath79/ar933x_uart.h>
|
||||||
#include <asm/mach-ath79/ar933x_uart_platform.h>
|
#include <asm/mach-ath79/ar933x_uart_platform.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "ar933x-uart"
|
#define DRIVER_NAME "ar933x-uart"
|
||||||
|
|
||||||
|
#define AR933X_UART_MAX_SCALE 0xff
|
||||||
|
#define AR933X_UART_MAX_STEP 0xffff
|
||||||
|
|
||||||
|
#define AR933X_UART_MIN_BAUD 300
|
||||||
|
#define AR933X_UART_MAX_BAUD 3000000
|
||||||
|
|
||||||
#define AR933X_DUMMY_STATUS_RD 0x01
|
#define AR933X_DUMMY_STATUS_RD 0x01
|
||||||
|
|
||||||
static struct uart_driver ar933x_uart_driver;
|
static struct uart_driver ar933x_uart_driver;
|
||||||
@@ -37,6 +45,8 @@ static struct uart_driver ar933x_uart_driver;
|
|||||||
struct ar933x_uart_port {
|
struct ar933x_uart_port {
|
||||||
struct uart_port port;
|
struct uart_port port;
|
||||||
unsigned int ier; /* shadow Interrupt Enable Register */
|
unsigned int ier; /* shadow Interrupt Enable Register */
|
||||||
|
unsigned int min_baud;
|
||||||
|
unsigned int max_baud;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
|
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
|
||||||
@@ -162,6 +172,57 @@ static void ar933x_uart_enable_ms(struct uart_port *port)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* baudrate = (clk / (scale + 1)) * (step * (1 / 2^17))
|
||||||
|
*/
|
||||||
|
static unsigned long ar933x_uart_get_baud(unsigned int clk,
|
||||||
|
unsigned int scale,
|
||||||
|
unsigned int step)
|
||||||
|
{
|
||||||
|
u64 t;
|
||||||
|
u32 div;
|
||||||
|
|
||||||
|
div = (2 << 16) * (scale + 1);
|
||||||
|
t = clk;
|
||||||
|
t *= step;
|
||||||
|
t += (div / 2);
|
||||||
|
do_div(t, div);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ar933x_uart_get_scale_step(unsigned int clk,
|
||||||
|
unsigned int baud,
|
||||||
|
unsigned int *scale,
|
||||||
|
unsigned int *step)
|
||||||
|
{
|
||||||
|
unsigned int tscale;
|
||||||
|
long min_diff;
|
||||||
|
|
||||||
|
*scale = 0;
|
||||||
|
*step = 0;
|
||||||
|
|
||||||
|
min_diff = baud;
|
||||||
|
for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) {
|
||||||
|
u64 tstep;
|
||||||
|
int diff;
|
||||||
|
|
||||||
|
tstep = baud * (tscale + 1);
|
||||||
|
tstep *= (2 << 16);
|
||||||
|
do_div(tstep, clk);
|
||||||
|
|
||||||
|
if (tstep > AR933X_UART_MAX_STEP)
|
||||||
|
break;
|
||||||
|
|
||||||
|
diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud);
|
||||||
|
if (diff < min_diff) {
|
||||||
|
min_diff = diff;
|
||||||
|
*scale = tscale;
|
||||||
|
*step = tstep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ar933x_uart_set_termios(struct uart_port *port,
|
static void ar933x_uart_set_termios(struct uart_port *port,
|
||||||
struct ktermios *new,
|
struct ktermios *new,
|
||||||
struct ktermios *old)
|
struct ktermios *old)
|
||||||
@@ -169,7 +230,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
|
|||||||
struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
|
struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
|
||||||
unsigned int cs;
|
unsigned int cs;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int baud, scale;
|
unsigned int baud, scale, step;
|
||||||
|
|
||||||
/* Only CS8 is supported */
|
/* Only CS8 is supported */
|
||||||
new->c_cflag &= ~CSIZE;
|
new->c_cflag &= ~CSIZE;
|
||||||
@@ -191,8 +252,8 @@ static void ar933x_uart_set_termios(struct uart_port *port,
|
|||||||
/* Mark/space parity is not supported */
|
/* Mark/space parity is not supported */
|
||||||
new->c_cflag &= ~CMSPAR;
|
new->c_cflag &= ~CMSPAR;
|
||||||
|
|
||||||
baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
|
baud = uart_get_baud_rate(port, new, old, up->min_baud, up->max_baud);
|
||||||
scale = (port->uartclk / (16 * baud)) - 1;
|
ar933x_uart_get_scale_step(port->uartclk, baud, &scale, &step);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ok, we're now changing the port state. Do it with
|
* Ok, we're now changing the port state. Do it with
|
||||||
@@ -200,6 +261,10 @@ static void ar933x_uart_set_termios(struct uart_port *port,
|
|||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&up->port.lock, flags);
|
spin_lock_irqsave(&up->port.lock, flags);
|
||||||
|
|
||||||
|
/* disable the UART */
|
||||||
|
ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
|
||||||
|
AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S);
|
||||||
|
|
||||||
/* Update the per-port timeout. */
|
/* Update the per-port timeout. */
|
||||||
uart_update_timeout(port, new->c_cflag, baud);
|
uart_update_timeout(port, new->c_cflag, baud);
|
||||||
|
|
||||||
@@ -210,7 +275,7 @@ static void ar933x_uart_set_termios(struct uart_port *port,
|
|||||||
up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD;
|
up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD;
|
||||||
|
|
||||||
ar933x_uart_write(up, AR933X_UART_CLOCK_REG,
|
ar933x_uart_write(up, AR933X_UART_CLOCK_REG,
|
||||||
scale << AR933X_UART_CLOCK_SCALE_S | 8192);
|
scale << AR933X_UART_CLOCK_SCALE_S | step);
|
||||||
|
|
||||||
/* setup configuration register */
|
/* setup configuration register */
|
||||||
ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs);
|
ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs);
|
||||||
@@ -219,6 +284,11 @@ static void ar933x_uart_set_termios(struct uart_port *port,
|
|||||||
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
|
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
|
||||||
AR933X_UART_CS_HOST_INT_EN);
|
AR933X_UART_CS_HOST_INT_EN);
|
||||||
|
|
||||||
|
/* reenable the UART */
|
||||||
|
ar933x_uart_rmw(up, AR933X_UART_CS_REG,
|
||||||
|
AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S,
|
||||||
|
AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||||
|
|
||||||
if (tty_termios_baud_rate(new))
|
if (tty_termios_baud_rate(new))
|
||||||
@@ -401,6 +471,8 @@ static void ar933x_uart_config_port(struct uart_port *port, int flags)
|
|||||||
static int ar933x_uart_verify_port(struct uart_port *port,
|
static int ar933x_uart_verify_port(struct uart_port *port,
|
||||||
struct serial_struct *ser)
|
struct serial_struct *ser)
|
||||||
{
|
{
|
||||||
|
struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
|
||||||
|
|
||||||
if (ser->type != PORT_UNKNOWN &&
|
if (ser->type != PORT_UNKNOWN &&
|
||||||
ser->type != PORT_AR933X)
|
ser->type != PORT_AR933X)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -408,7 +480,8 @@ static int ar933x_uart_verify_port(struct uart_port *port,
|
|||||||
if (ser->irq < 0 || ser->irq >= NR_IRQS)
|
if (ser->irq < 0 || ser->irq >= NR_IRQS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (ser->baud_base < 28800)
|
if (ser->baud_base < up->min_baud ||
|
||||||
|
ser->baud_base > up->max_baud)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -561,6 +634,7 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev)
|
|||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
struct resource *mem_res;
|
struct resource *mem_res;
|
||||||
struct resource *irq_res;
|
struct resource *irq_res;
|
||||||
|
unsigned int baud;
|
||||||
int id;
|
int id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -611,6 +685,12 @@ static int __devinit ar933x_uart_probe(struct platform_device *pdev)
|
|||||||
port->fifosize = AR933X_UART_FIFO_SIZE;
|
port->fifosize = AR933X_UART_FIFO_SIZE;
|
||||||
port->ops = &ar933x_uart_ops;
|
port->ops = &ar933x_uart_ops;
|
||||||
|
|
||||||
|
baud = ar933x_uart_get_baud(port->uartclk, AR933X_UART_MAX_SCALE, 1);
|
||||||
|
up->min_baud = max_t(unsigned int, baud, AR933X_UART_MIN_BAUD);
|
||||||
|
|
||||||
|
baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
|
||||||
|
up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
|
||||||
|
|
||||||
ar933x_uart_add_console_port(up);
|
ar933x_uart_add_console_port(up);
|
||||||
|
|
||||||
ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
|
ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
|
||||||
|
Reference in New Issue
Block a user