123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * UART driver for MediaTek MT7620 and earlier SoCs
- *
- * Copyright (C) 2020 MediaTek Inc.
- * Author: Weijie Gao <weijie.gao@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_plat(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_plat(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_plat(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_plat(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_plat(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_of_to_plat(struct udevice *dev)
- {
- struct mt7620_serial_plat *plat = dev_get_plat(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,
- .of_to_plat = mt7620_serial_of_to_plat,
- #endif
- .plat_auto = sizeof(struct mt7620_serial_plat),
- .probe = mt7620_serial_probe,
- .ops = &mt7620_serial_ops,
- .flags = DM_FLAG_PRE_RELOC,
- };
- DM_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
|