summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorAlexey Charkov <alchark@gmail.com>2014-09-06 19:21:14 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-09-09 01:29:36 +0200
commit5aa387c152fce7ae5cb29f2b191a1570cd04db5f (patch)
tree50806259127c6f3bdb1f49fdce62fcd0710c6f97 /drivers/tty
parenttty: vt8500_serial: add polled console functions (diff)
downloadlinux-5aa387c152fce7ae5cb29f2b191a1570cd04db5f.tar.xz
linux-5aa387c152fce7ae5cb29f2b191a1570cd04db5f.zip
tty: vt8500_serial: explicitly calculate base baud rate
Current code relies on the UART clock pre-divisor to be already configured in the baud rate register. Calculate it in the driver and set explicitly instead, also return the "real" effective baud rate, which is generally slightly different from the requested value. While at this, also ensure that break signal timing is updated when baud rate changes. Signed-off-by: Alexey Charkov <alchark@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/vt8500_serial.c27
1 files changed, 21 insertions, 6 deletions
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 4203862ef6cb..b2bc9e8ba048 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -101,12 +101,15 @@
#define VT8500_HAS_SWRTSCTS_SWITCH (1 << 1)
+#define VT8500_RECOMMENDED_CLK 12000000
+#define VT8500_OVERSAMPLING_DIVISOR 13
#define VT8500_MAX_PORTS 6
struct vt8500_port {
struct uart_port uart;
char name[16];
struct clk *clk;
+ unsigned int clk_predivisor;
unsigned int ier;
unsigned int vt8500_uart_flags;
};
@@ -311,20 +314,25 @@ static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
{
+ struct vt8500_port *vt8500_port =
+ container_of(port, struct vt8500_port, uart);
unsigned long div;
unsigned int loops = 1000;
- div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
+ div = ((vt8500_port->clk_predivisor - 1) & 0xf) << 16;
+ div |= (uart_get_divisor(port, baud) - 1) & 0x3ff;
- if (unlikely((baud < 900) || (baud > 921600)))
- div |= 7;
- else
- div |= (921600 / baud) - 1;
+ /* Effective baud rate */
+ baud = port->uartclk / 16 / ((div & 0x3ff) + 1);
while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops)
cpu_relax();
+
vt8500_write(port, div, VT8500_URDIV);
+ /* Break signal timing depends on baud rate, update accordingly */
+ vt8500_write(port, mult_frac(baud, 4096, 1000000), VT8500_URBKR);
+
return baud;
}
@@ -691,6 +699,10 @@ static int vt8500_serial_probe(struct platform_device *pdev)
}
vt8500_port->vt8500_uart_flags = *flags;
+ vt8500_port->clk_predivisor = DIV_ROUND_CLOSEST(
+ clk_get_rate(vt8500_port->clk),
+ VT8500_RECOMMENDED_CLK
+ );
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
@@ -701,7 +713,10 @@ static int vt8500_serial_probe(struct platform_device *pdev)
vt8500_port->uart.dev = &pdev->dev;
vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
+ /* Serial core uses the magic "16" everywhere - adjust for it */
+ vt8500_port->uart.uartclk = 16 * clk_get_rate(vt8500_port->clk) /
+ vt8500_port->clk_predivisor /
+ VT8500_OVERSAMPLING_DIVISOR;
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"VT8500 UART%d", pdev->id);