Many SoCs from MediaTek have a high-speed uart. This UART is compatible
with the ns16550 in legacy mode. It has extra registers for high-speed
mode which can reach a maximum baudrate at 921600.
However this UART will no longer be compatible if it's in high-speed mode.
Some BootROM of MediaTek's SoCs will change the UART into high-speed mode
and the U-Boot must use this driver to initialize the UART.
Signed-off-by: Weijie Gao
Tested-by: Ryder Lee
---
Changes since v5: Add a specific driver for MTK UART
Changes since v4: None
---
drivers/serial/Kconfig | 20
drivers/serial/Makefile | 1 +
drivers/serial/serial_mtk.c | 268
3 files changed, 289 insertions(+)
create mode 100644 drivers/serial/serial_mtk.c
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 597db4b..7ab38b7 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -368,6 +368,16 @@ config DEBUG_UART_OMAP
You will need to provide parameters to make this work. The driver
will be available until the real driver model serial is running.
+config DEBUG_UART_MTK
+ bool "MediaTek High-speed UART"
+ depends on MTK_SERIAL
+ help
+ Select this to enable a debug UART using the MediaTek High-speed
+ UART driver.
+ You will need to provide parameters to make this work. The
+ driver will be available until the real driver model serial is
+ running.
+
endchoice
config DEBUG_UART_BASE
@@ -692,6 +702,16 @@ config ZYNQ_SERIAL
This driver supports the Cadence UART. It is found e.g. in Xilinx
Zynq/ZynqMP.
+config MTK_SERIAL
+ bool "MediaTek High-speed UART support"
+ depends on DM_SERIAL
+ help
+ Select this to enable UART support for MediaTek High-speed UART
+ devices. This driver uses driver model and requires a device
+ tree binding to operate.
+ The High-speed UART is compatible with the ns16550a UART and have
+ its own high-speed registers.
+
config MPC8XX_CONS
bool "Console driver for MPC8XX"
depends on MPC8xx
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 03dc29e..2f8d065 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_MPC8XX_CONS) += serial_mpc8xx.o
obj-$(CONFIG_NULLDEV_SERIAL) += serial_nulldev.o
obj-$(CONFIG_OWL_SERIAL) += serial_owl.o
obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o
+obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_USB_TTY) += usbtty.o
diff --git a/drivers/serial/serial_mtk.c b/drivers/serial/serial_mtk.c
new file mode 100644
index 000..bce1be8
--- /dev/null
+++ b/drivers/serial/serial_mtk.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek High-speed UART driver
+ *
+ * Copyright (C) 2018 MediaTek Inc.
+ * Author: Weijie Gao
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct mtk_serial_regs {
+ u32 rbr;
+ u32 ier;
+ u32 fcr;
+ u32 lcr;
+ u32 mcr;
+ u32 lsr;
+ u32 msr;
+ u32 spr;
+ u32 mdr1;
+ u32 highspeed;
+ u32 sample_count;
+ u32 sample_point;
+ u32 fracdiv_l;
+ u32 fracdiv_m;
+ u32 escape_en;
+ u32 guard;
+ u32 rx_sel;
+};
+
+#define thr rbr
+#define iir fcr
+#define dll rbr
+#define dlm ier
+
+#define UART_LCR_WLS_8 0x03/* 8 bit character length */
+#define UART_LCR_DLAB 0x80/* Divisor latch access bit */
+
+#define UART_LSR_DR0x01/* Data ready */
+#define UART_LSR_THRE 0x20/* Xmit holding register empty */
+
+/* the data is correct if the real baud is within 3%. */
+#define BAUD_ALLOW_MAX(baud) ((baud) + (baud) * 3 / 100)
+#define BAUD_ALLOW_MIX(baud) ((baud) - (baud) * 3 / 100)
+
+struct mtk_serial_priv {
+ struct mtk_serial_regs __iomem *regs;
+ u32 clock;
+};
+
+static void _mtk_serial_setbrg(struct mtk_serial_priv *priv, int baud)
+{
+ bool support_clk12m_baud115200;
+ u32 quot, samplecount, realbaud;
+
+ if ((baud <= 115200) && (priv->clock == 1200))
+ support_clk12m_baud115200 = true;
+ else
+ support_clk12m_baud115200 = false;
+
+ if (baud <= 115200) {
+ writel(0, >regs->highspeed);
+ quot = DIV_ROUND_CLOSEST(priv->clock, 16 * baud);
+
+ if (support_clk12m_baud115200) {
+ writel(3, >regs->highspeed);
+ quot = DIV_ROUND_CLOSEST(priv->clock, 256 * baud);
+ if (quot == 0)
+ quot = 1;
+
+ samplecount = DIV_ROUND_CLOSEST(priv->clock,
+ quot * baud);
+ if (samplecount != 0) {
+