[PATCH v3 10/23] serial: add uart driver for MediaTek MT7620 SoC
Stefan Roese
sr at denx.de
Mon Nov 9 09:41:39 CET 2020
On 09.11.20 09:25, Weijie Gao wrote:
> This patch adds uart support for MediaTek MT7620 and earlier SoCs.
>
> The UART used by MT7620 is incompatible with the ns16550a driver.
> All registers of this UART have different addresses. A special 16-bit
> register for Divisor Latch is used to set the baudrate instead of the
> original two 8-bit registers (DLL and DLM).
>
> The driver supports of-platdata which is useful for tiny SPL.
>
> Reviewed-by: Simon Glass <sjg at chromium.org>
> Signed-off-by: Weijie Gao <weijie.gao at mediatek.com>
Reviewed-by: Stefan Roese <sr at denx.de>
Thanks,
Stefan
> ---
> v3 changes: none
> v2 changes: replace non-DM code with of-platdata
> ---
> drivers/serial/Kconfig | 20 +++
> drivers/serial/Makefile | 1 +
> drivers/serial/serial.c | 2 +
> drivers/serial/serial_mt7620.c | 246 +++++++++++++++++++++++++++++++++
> 4 files changed, 269 insertions(+)
> create mode 100644 drivers/serial/serial_mt7620.c
>
> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index b4805a2e4e..44fff8a3cd 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -401,6 +401,16 @@ config DEBUG_UART_MTK
> driver will be available until the real driver model serial is
> running.
>
> +config DEBUG_UART_MT7620
> + bool "UART driver for MediaTek MT7620 and earlier SoCs"
> + depends on MT7620_SERIAL
> + help
> + Select this to enable a debug UART using the UART driver for
> + MediaTek MT7620 and earlier SoCs.
> + 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
> @@ -817,6 +827,16 @@ config MTK_SERIAL
> The High-speed UART is compatible with the ns16550a UART and have
> its own high-speed registers.
>
> +config MT7620_SERIAL
> + bool "UART driver for MediaTek MT7620 and earlier SoCs"
> + depends on DM_SERIAL
> + help
> + Select this to enable UART support for MediaTek MT7620 and earlier
> + SoCs. This driver uses driver model and requires a device tree
> + binding to operate.
> + The UART driver for MediaTek MT7620 and earlier SoCs is *NOT*
> + compatible with the ns16550a UART.
> +
> config MPC8XX_CONS
> bool "Console driver for MPC8XX"
> depends on MPC8xx
> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
> index 25f7f8d342..0c3810f5d5 100644
> --- a/drivers/serial/Makefile
> +++ b/drivers/serial/Makefile
> @@ -69,6 +69,7 @@ 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
> +obj-$(CONFIG_MT7620_SERIAL) += serial_mt7620.o
> obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
> obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
>
> diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
> index 355659ba05..1546f41e42 100644
> --- a/drivers/serial/serial.c
> +++ b/drivers/serial/serial.c
> @@ -127,6 +127,7 @@ serial_initfunc(pl01x_serial_initialize);
> serial_initfunc(pxa_serial_initialize);
> serial_initfunc(sh_serial_initialize);
> serial_initfunc(mtk_serial_initialize);
> +serial_initfunc(mt7620_serial_initialize);
>
> /**
> * serial_register() - Register serial driver with serial driver core
> @@ -181,6 +182,7 @@ int serial_initialize(void)
> pxa_serial_initialize();
> sh_serial_initialize();
> mtk_serial_initialize();
> + mt7620_serial_initialize();
>
> serial_assign(default_serial_console()->name);
>
> diff --git a/drivers/serial/serial_mt7620.c b/drivers/serial/serial_mt7620.c
> new file mode 100644
> index 0000000000..4f6b5de4cf
> --- /dev/null
> +++ b/drivers/serial/serial_mt7620.c
> @@ -0,0 +1,246 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * UART driver for MediaTek MT7620 and earlier SoCs
> + *
> + * Copyright (C) 2020 MediaTek Inc.
> + * Author: Weijie Gao <weijie.gao at mediatek.com>
> + */
> +
> +#include <clk.h>
> +#include <div64.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <log.h>
> +#include <reset.h>
> +#include <serial.h>
> +#include <watchdog.h>
> +#include <asm/io.h>
> +#include <asm/types.h>
> +#include <asm/addrspace.h>
> +#include <dm/device_compat.h>
> +#include <linux/err.h>
> +
> +#if CONFIG_IS_ENABLED(OF_PLATDATA)
> +#include <dt-structs.h>
> +#endif
> +
> +struct mt7620_serial_regs {
> + u32 rbr;
> + u32 thr;
> + u32 ier;
> + u32 iir;
> + u32 fcr;
> + u32 lcr;
> + u32 mcr;
> + u32 lsr;
> + u32 msr;
> + u32 scratch;
> + u32 dl;
> + u32 dll;
> + u32 dlm;
> + u32 ifctl;
> +};
> +
> +#define UART_LCR_WLS_8 0x03 /* 8 bit character length */
> +
> +#define UART_LSR_DR 0x01 /* Data ready */
> +#define UART_LSR_THRE 0x20 /* Xmit holding register empty */
> +#define UART_LSR_TEMT 0x40 /* Xmitter empty */
> +
> +#define UART_MCR_DTR 0x01 /* DTR */
> +#define UART_MCR_RTS 0x02 /* RTS */
> +
> +#define UART_FCR_FIFO_EN 0x01 /* Fifo enable */
> +#define UART_FCR_RXSR 0x02 /* Receiver soft reset */
> +#define UART_FCR_TXSR 0x04 /* Transmitter soft reset */
> +
> +#define UART_MCRVAL (UART_MCR_DTR | \
> + UART_MCR_RTS)
> +
> +/* Clear & enable FIFOs */
> +#define UART_FCRVAL (UART_FCR_FIFO_EN | \
> + UART_FCR_RXSR | \
> + UART_FCR_TXSR)
> +
> +struct mt7620_serial_plat {
> +#if CONFIG_IS_ENABLED(OF_PLATDATA)
> + struct dtd_serial_mt7620 dtplat;
> +#endif
> +
> + struct mt7620_serial_regs __iomem *regs;
> + u32 clock;
> +};
> +
> +static void _mt7620_serial_setbrg(struct mt7620_serial_plat *plat, int baud)
> +{
> + u32 quot;
> +
> + /* set divisor */
> + quot = DIV_ROUND_CLOSEST(plat->clock, 16 * baud);
> + writel(quot, &plat->regs->dl);
> +
> + /* set character length and stop bits */
> + writel(UART_LCR_WLS_8, &plat->regs->lcr);
> +}
> +
> +static int mt7620_serial_setbrg(struct udevice *dev, int baudrate)
> +{
> + struct mt7620_serial_plat *plat = dev_get_platdata(dev);
> +
> + _mt7620_serial_setbrg(plat, baudrate);
> +
> + return 0;
> +}
> +
> +static int mt7620_serial_putc(struct udevice *dev, const char ch)
> +{
> + struct mt7620_serial_plat *plat = dev_get_platdata(dev);
> +
> + if (!(readl(&plat->regs->lsr) & UART_LSR_THRE))
> + return -EAGAIN;
> +
> + writel(ch, &plat->regs->thr);
> +
> + if (ch == '\n')
> + WATCHDOG_RESET();
> +
> + return 0;
> +}
> +
> +static int mt7620_serial_getc(struct udevice *dev)
> +{
> + struct mt7620_serial_plat *plat = dev_get_platdata(dev);
> +
> + if (!(readl(&plat->regs->lsr) & UART_LSR_DR))
> + return -EAGAIN;
> +
> + return readl(&plat->regs->rbr);
> +}
> +
> +static int mt7620_serial_pending(struct udevice *dev, bool input)
> +{
> + struct mt7620_serial_plat *plat = dev_get_platdata(dev);
> +
> + if (input)
> + return (readl(&plat->regs->lsr) & UART_LSR_DR) ? 1 : 0;
> +
> + return (readl(&plat->regs->lsr) & UART_LSR_THRE) ? 0 : 1;
> +}
> +
> +static int mt7620_serial_probe(struct udevice *dev)
> +{
> + struct mt7620_serial_plat *plat = dev_get_platdata(dev);
> +
> +#if CONFIG_IS_ENABLED(OF_PLATDATA)
> + plat->regs = (void __iomem *)KSEG1ADDR(plat->dtplat.reg[0]);
> + plat->clock = plat->dtplat.clock_frequency;
> +#endif
> +
> + /* Disable interrupt */
> + writel(0, &plat->regs->ier);
> +
> + writel(UART_MCRVAL, &plat->regs->mcr);
> + writel(UART_FCRVAL, &plat->regs->fcr);
> +
> + return 0;
> +}
> +
> +#if !CONFIG_IS_ENABLED(OF_PLATDATA)
> +static int mt7620_serial_ofdata_to_platdata(struct udevice *dev)
> +{
> + struct mt7620_serial_plat *plat = dev_get_platdata(dev);
> + struct reset_ctl reset_uart;
> + struct clk clk;
> + int err;
> +
> + err = reset_get_by_index(dev, 0, &reset_uart);
> + if (!err)
> + reset_deassert(&reset_uart);
> +
> + plat->regs = dev_remap_addr_index(dev, 0);
> + if (!plat->regs) {
> + dev_err(dev, "mt7620_serial: unable to map UART registers\n");
> + return -EINVAL;
> + }
> +
> + err = clk_get_by_index(dev, 0, &clk);
> + if (!err) {
> + err = clk_get_rate(&clk);
> + if (!IS_ERR_VALUE(err))
> + plat->clock = err;
> + } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
> + dev_err(dev, "mt7620_serial: failed to get clock\n");
> + return err;
> + }
> +
> + if (!plat->clock)
> + plat->clock = dev_read_u32_default(dev, "clock-frequency", 0);
> +
> + if (!plat->clock) {
> + dev_err(dev, "mt7620_serial: clock not defined\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static const struct udevice_id mt7620_serial_ids[] = {
> + { .compatible = "mediatek,mt7620-uart" },
> + { }
> +};
> +#endif
> +
> +static const struct dm_serial_ops mt7620_serial_ops = {
> + .putc = mt7620_serial_putc,
> + .pending = mt7620_serial_pending,
> + .getc = mt7620_serial_getc,
> + .setbrg = mt7620_serial_setbrg,
> +};
> +
> +U_BOOT_DRIVER(serial_mt7620) = {
> + .name = "serial_mt7620",
> + .id = UCLASS_SERIAL,
> +#if !CONFIG_IS_ENABLED(OF_PLATDATA)
> + .of_match = mt7620_serial_ids,
> + .ofdata_to_platdata = mt7620_serial_ofdata_to_platdata,
> +#endif
> + .platdata_auto_alloc_size = sizeof(struct mt7620_serial_plat),
> + .probe = mt7620_serial_probe,
> + .ops = &mt7620_serial_ops,
> + .flags = DM_FLAG_PRE_RELOC,
> +};
> +
> +U_BOOT_DRIVER_ALIAS(serial_mt7620, mediatek_mt7620_uart);
> +
> +#ifdef CONFIG_DEBUG_UART_MT7620
> +
> +#include <debug_uart.h>
> +
> +static inline void _debug_uart_init(void)
> +{
> + struct mt7620_serial_plat plat;
> +
> + plat.regs = (void *)CONFIG_DEBUG_UART_BASE;
> + plat.clock = CONFIG_DEBUG_UART_CLOCK;
> +
> + writel(0, &plat.regs->ier);
> + writel(UART_MCRVAL, &plat.regs->mcr);
> + writel(UART_FCRVAL, &plat.regs->fcr);
> +
> + _mt7620_serial_setbrg(&plat, CONFIG_BAUDRATE);
> +}
> +
> +static inline void _debug_uart_putc(int ch)
> +{
> + struct mt7620_serial_regs __iomem *regs =
> + (void *)CONFIG_DEBUG_UART_BASE;
> +
> + while (!(readl(®s->lsr) & UART_LSR_THRE))
> + ;
> +
> + writel(ch, ®s->thr);
> +}
> +
> +DEBUG_UART_FUNCS
> +
> +#endif
>
Viele Grüße,
Stefan
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
More information about the U-Boot
mailing list