uboot/drivers/serial/serial_mt7620.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * UART driver for MediaTek MT7620 and earlier SoCs
   4 *
   5 * Copyright (C) 2020 MediaTek Inc.
   6 * Author: Weijie Gao <weijie.gao@mediatek.com>
   7 */
   8
   9#include <clk.h>
  10#include <div64.h>
  11#include <dm.h>
  12#include <errno.h>
  13#include <log.h>
  14#include <reset.h>
  15#include <serial.h>
  16#include <watchdog.h>
  17#include <asm/io.h>
  18#include <asm/types.h>
  19#include <asm/addrspace.h>
  20#include <dm/device_compat.h>
  21#include <linux/err.h>
  22
  23#if CONFIG_IS_ENABLED(OF_PLATDATA)
  24#include <dt-structs.h>
  25#endif
  26
  27struct mt7620_serial_regs {
  28        u32 rbr;
  29        u32 thr;
  30        u32 ier;
  31        u32 iir;
  32        u32 fcr;
  33        u32 lcr;
  34        u32 mcr;
  35        u32 lsr;
  36        u32 msr;
  37        u32 scratch;
  38        u32 dl;
  39        u32 dll;
  40        u32 dlm;
  41        u32 ifctl;
  42};
  43
  44#define UART_LCR_WLS_8          0x03    /* 8 bit character length */
  45
  46#define UART_LSR_DR             0x01    /* Data ready */
  47#define UART_LSR_THRE           0x20    /* Xmit holding register empty */
  48#define UART_LSR_TEMT           0x40    /* Xmitter empty */
  49
  50#define UART_MCR_DTR            0x01    /* DTR */
  51#define UART_MCR_RTS            0x02    /* RTS */
  52
  53#define UART_FCR_FIFO_EN        0x01    /* Fifo enable */
  54#define UART_FCR_RXSR           0x02    /* Receiver soft reset */
  55#define UART_FCR_TXSR           0x04    /* Transmitter soft reset */
  56
  57#define UART_MCRVAL (UART_MCR_DTR | \
  58                     UART_MCR_RTS)
  59
  60/* Clear & enable FIFOs */
  61#define UART_FCRVAL (UART_FCR_FIFO_EN | \
  62                     UART_FCR_RXSR |    \
  63                     UART_FCR_TXSR)
  64
  65struct mt7620_serial_plat {
  66#if CONFIG_IS_ENABLED(OF_PLATDATA)
  67        struct dtd_serial_mt7620 dtplat;
  68#endif
  69
  70        struct mt7620_serial_regs __iomem *regs;
  71        u32 clock;
  72};
  73
  74static void _mt7620_serial_setbrg(struct mt7620_serial_plat *plat, int baud)
  75{
  76        u32 quot;
  77
  78        /* set divisor */
  79        quot = DIV_ROUND_CLOSEST(plat->clock, 16 * baud);
  80        writel(quot, &plat->regs->dl);
  81
  82        /* set character length and stop bits */
  83        writel(UART_LCR_WLS_8, &plat->regs->lcr);
  84}
  85
  86static int mt7620_serial_setbrg(struct udevice *dev, int baudrate)
  87{
  88        struct mt7620_serial_plat *plat = dev_get_plat(dev);
  89
  90        _mt7620_serial_setbrg(plat, baudrate);
  91
  92        return 0;
  93}
  94
  95static int mt7620_serial_putc(struct udevice *dev, const char ch)
  96{
  97        struct mt7620_serial_plat *plat = dev_get_plat(dev);
  98
  99        if (!(readl(&plat->regs->lsr) & UART_LSR_THRE))
 100                return -EAGAIN;
 101
 102        writel(ch, &plat->regs->thr);
 103
 104        if (ch == '\n')
 105                WATCHDOG_RESET();
 106
 107        return 0;
 108}
 109
 110static int mt7620_serial_getc(struct udevice *dev)
 111{
 112        struct mt7620_serial_plat *plat = dev_get_plat(dev);
 113
 114        if (!(readl(&plat->regs->lsr) & UART_LSR_DR))
 115                return -EAGAIN;
 116
 117        return readl(&plat->regs->rbr);
 118}
 119
 120static int mt7620_serial_pending(struct udevice *dev, bool input)
 121{
 122        struct mt7620_serial_plat *plat = dev_get_plat(dev);
 123
 124        if (input)
 125                return (readl(&plat->regs->lsr) & UART_LSR_DR) ? 1 : 0;
 126
 127        return (readl(&plat->regs->lsr) & UART_LSR_THRE) ? 0 : 1;
 128}
 129
 130static int mt7620_serial_probe(struct udevice *dev)
 131{
 132        struct mt7620_serial_plat *plat = dev_get_plat(dev);
 133
 134#if CONFIG_IS_ENABLED(OF_PLATDATA)
 135        plat->regs = (void __iomem *)KSEG1ADDR(plat->dtplat.reg[0]);
 136        plat->clock = plat->dtplat.clock_frequency;
 137#endif
 138
 139        /* Disable interrupt */
 140        writel(0, &plat->regs->ier);
 141
 142        writel(UART_MCRVAL, &plat->regs->mcr);
 143        writel(UART_FCRVAL, &plat->regs->fcr);
 144
 145        return 0;
 146}
 147
 148#if CONFIG_IS_ENABLED(OF_REAL)
 149static int mt7620_serial_of_to_plat(struct udevice *dev)
 150{
 151        struct mt7620_serial_plat *plat = dev_get_plat(dev);
 152        struct reset_ctl reset_uart;
 153        struct clk clk;
 154        int err;
 155
 156        err = reset_get_by_index(dev, 0, &reset_uart);
 157        if (!err)
 158                reset_deassert(&reset_uart);
 159
 160        plat->regs = dev_remap_addr_index(dev, 0);
 161        if (!plat->regs) {
 162                dev_err(dev, "mt7620_serial: unable to map UART registers\n");
 163                return -EINVAL;
 164        }
 165
 166        err = clk_get_by_index(dev, 0, &clk);
 167        if (!err) {
 168                err = clk_get_rate(&clk);
 169                if (!IS_ERR_VALUE(err))
 170                        plat->clock = err;
 171        } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
 172                dev_err(dev, "mt7620_serial: failed to get clock\n");
 173                return err;
 174        }
 175
 176        if (!plat->clock)
 177                plat->clock = dev_read_u32_default(dev, "clock-frequency", 0);
 178
 179        if (!plat->clock) {
 180                dev_err(dev, "mt7620_serial: clock not defined\n");
 181                return -EINVAL;
 182        }
 183
 184        return 0;
 185}
 186
 187static const struct udevice_id mt7620_serial_ids[] = {
 188        { .compatible = "mediatek,mt7620-uart" },
 189        { }
 190};
 191#endif
 192
 193static const struct dm_serial_ops mt7620_serial_ops = {
 194        .putc = mt7620_serial_putc,
 195        .pending = mt7620_serial_pending,
 196        .getc = mt7620_serial_getc,
 197        .setbrg = mt7620_serial_setbrg,
 198};
 199
 200U_BOOT_DRIVER(serial_mt7620) = {
 201        .name = "serial_mt7620",
 202        .id = UCLASS_SERIAL,
 203#if CONFIG_IS_ENABLED(OF_REAL)
 204        .of_match = mt7620_serial_ids,
 205        .of_to_plat = mt7620_serial_of_to_plat,
 206#endif
 207        .plat_auto = sizeof(struct mt7620_serial_plat),
 208        .probe = mt7620_serial_probe,
 209        .ops = &mt7620_serial_ops,
 210        .flags = DM_FLAG_PRE_RELOC,
 211};
 212
 213DM_DRIVER_ALIAS(serial_mt7620, mediatek_mt7620_uart);
 214
 215#ifdef CONFIG_DEBUG_UART_MT7620
 216
 217#include <debug_uart.h>
 218
 219static inline void _debug_uart_init(void)
 220{
 221        struct mt7620_serial_plat plat;
 222
 223        plat.regs = (void *)CONFIG_DEBUG_UART_BASE;
 224        plat.clock = CONFIG_DEBUG_UART_CLOCK;
 225
 226        writel(0, &plat.regs->ier);
 227        writel(UART_MCRVAL, &plat.regs->mcr);
 228        writel(UART_FCRVAL, &plat.regs->fcr);
 229
 230        _mt7620_serial_setbrg(&plat, CONFIG_BAUDRATE);
 231}
 232
 233static inline void _debug_uart_putc(int ch)
 234{
 235        struct mt7620_serial_regs __iomem *regs =
 236                (void *)CONFIG_DEBUG_UART_BASE;
 237
 238        while (!(readl(&regs->lsr) & UART_LSR_THRE))
 239                ;
 240
 241        writel(ch, &regs->thr);
 242}
 243
 244DEBUG_UART_FUNCS
 245
 246#endif
 247