The device has a highspeed register which influences the calcualtion of the
divisor. The chip lacks support for some baudrates. When requested, we set the
divisor to the next smaller baudrate and adjust the c_cflag accordingly.
Signed-off-by: Matthias Brugger
---
drivers/tty/serial/8250/8250_mtk.c | 294
drivers/tty/serial/8250/Kconfig|7 +
drivers/tty/serial/8250/Makefile |1 +
3 files changed, 302 insertions(+)
create mode 100644 drivers/tty/serial/8250/8250_mtk.c
diff --git a/drivers/tty/serial/8250/8250_mtk.c
b/drivers/tty/serial/8250/8250_mtk.c
new file mode 100644
index 000..1c4b4bf
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -0,0 +1,294 @@
+/*
+ * Mediatek 8250 driver.
+ *
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Matthias Brugger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "8250.h"
+
+#define UART_MTK_HIGHS 0x09/* Highspeed register */
+#define UART_MTK_SAMPLE_COUNT 0x0a/* Sample count register */
+#define UART_MTK_SAMPLE_POINT 0x0b/* Sample point register */
+#define MTK_UART_RATE_FIX 0x0d/* UART Rate Fix Register */
+
+struct mtk8250_data {
+ int line;
+ struct clk *clk;
+};
+
+static void
+mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned long flags;
+ unsigned int baud, quot;
+
+ struct uart_8250_port *up =
+ container_of(port, struct uart_8250_port, port);
+
+ serial8250_do_set_termios(port, termios, old);
+
+ /*
+* Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS)
+*
+* We need to recalcualte the quot register, as the claculation depends
+* on the vaule in the highspeed register.
+*
+* Some baudrates are not supported by the chip, so we use the next
+* lower rate supported and update termios c_flag.
+*
+* If highspeed register is set to 3, we need to specify sample count
+* and sample point to increase accuracy. If not, we reset the
+* registers to their default values.
+*/
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0x,
+ port->uartclk / 16);
+
+ if (baud <= 115200) {
+ serial_port_out(port, UART_MTK_HIGHS, 0x0);
+ quot = uart_get_divisor(port, baud);
+ } else if (baud <= 576000) {
+ serial_port_out(port, UART_MTK_HIGHS, 0x2);
+
+ /* Set to next lower baudrate supported */
+ if ((baud == 50) || (baud == 576000))
+ baud = 460800;
+ quot = DIV_ROUND_CLOSEST(port->uartclk, 4 * baud);
+ } else {
+ serial_port_out(port, UART_MTK_HIGHS, 0x3);
+
+ /* Set to highest baudrate supported */
+ if (baud >= 1152000)
+ baud = 921600;
+ quot = DIV_ROUND_CLOSEST(port->uartclk, 256 * baud);
+ }
+
+ /*
+* Ok, we're now changing the port state. Do it with
+* interrupts disabled.
+*/
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* set DLAB we have cval saved in up->lcr from the call to the core */
+ serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
+ serial_dl_write(up, quot);
+
+ /* reset DLAB */
+ serial_port_out(port, UART_LCR, up->lcr);
+
+ if (baud > 460800) {
+ unsigned int tmp;
+
+ tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud);
+ serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1);
+ serial_port_out(port, UART_MTK_SAMPLE_POINT,
+ (tmp - 2) >> 1);
+ } else {
+ serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00);
+ serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static void
+mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
+{
+ if (!state)
+ pm_runtime_get_sync(port-