uboot/drivers/serial/serial_meson.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2016 Beniamino Galvani <b.galvani@gmail.com>
   4 */
   5
   6#include <common.h>
   7#include <dm.h>
   8#include <errno.h>
   9#include <fdtdec.h>
  10#include <linux/bitops.h>
  11#include <linux/compiler.h>
  12#include <serial.h>
  13
  14struct meson_uart {
  15        u32 wfifo;
  16        u32 rfifo;
  17        u32 control;
  18        u32 status;
  19        u32 misc;
  20};
  21
  22struct meson_serial_plat {
  23        struct meson_uart *reg;
  24};
  25
  26/* AML_UART_STATUS bits */
  27#define AML_UART_PARITY_ERR             BIT(16)
  28#define AML_UART_FRAME_ERR              BIT(17)
  29#define AML_UART_TX_FIFO_WERR           BIT(18)
  30#define AML_UART_RX_EMPTY               BIT(20)
  31#define AML_UART_TX_FULL                BIT(21)
  32#define AML_UART_TX_EMPTY               BIT(22)
  33#define AML_UART_XMIT_BUSY              BIT(25)
  34#define AML_UART_ERR                    (AML_UART_PARITY_ERR | \
  35                                         AML_UART_FRAME_ERR  | \
  36                                         AML_UART_TX_FIFO_WERR)
  37
  38/* AML_UART_CONTROL bits */
  39#define AML_UART_TX_EN                  BIT(12)
  40#define AML_UART_RX_EN                  BIT(13)
  41#define AML_UART_TX_RST                 BIT(22)
  42#define AML_UART_RX_RST                 BIT(23)
  43#define AML_UART_CLR_ERR                BIT(24)
  44
  45static void meson_serial_init(struct meson_uart *uart)
  46{
  47        u32 val;
  48
  49        val = readl(&uart->control);
  50        val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);
  51        writel(val, &uart->control);
  52        val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);
  53        writel(val, &uart->control);
  54        val |= (AML_UART_RX_EN | AML_UART_TX_EN);
  55        writel(val, &uart->control);
  56}
  57
  58static int meson_serial_probe(struct udevice *dev)
  59{
  60        struct meson_serial_plat *plat = dev_get_plat(dev);
  61        struct meson_uart *const uart = plat->reg;
  62
  63        meson_serial_init(uart);
  64
  65        return 0;
  66}
  67
  68static void meson_serial_rx_error(struct udevice *dev)
  69{
  70        struct meson_serial_plat *plat = dev_get_plat(dev);
  71        struct meson_uart *const uart = plat->reg;
  72        u32 val = readl(&uart->control);
  73
  74        /* Clear error */
  75        val |= AML_UART_CLR_ERR;
  76        writel(val, &uart->control);
  77        val &= ~AML_UART_CLR_ERR;
  78        writel(val, &uart->control);
  79
  80        /* Remove spurious byte from fifo */
  81        readl(&uart->rfifo);
  82}
  83
  84static int meson_serial_getc(struct udevice *dev)
  85{
  86        struct meson_serial_plat *plat = dev_get_plat(dev);
  87        struct meson_uart *const uart = plat->reg;
  88        uint32_t status = readl(&uart->status);
  89
  90        if (status & AML_UART_RX_EMPTY)
  91                return -EAGAIN;
  92
  93        if (status & AML_UART_ERR) {
  94                meson_serial_rx_error(dev);
  95                return -EIO;
  96        }
  97
  98        return readl(&uart->rfifo) & 0xff;
  99}
 100
 101static int meson_serial_putc(struct udevice *dev, const char ch)
 102{
 103        struct meson_serial_plat *plat = dev_get_plat(dev);
 104        struct meson_uart *const uart = plat->reg;
 105
 106        if (readl(&uart->status) & AML_UART_TX_FULL)
 107                return -EAGAIN;
 108
 109        writel(ch, &uart->wfifo);
 110
 111        return 0;
 112}
 113
 114static int meson_serial_pending(struct udevice *dev, bool input)
 115{
 116        struct meson_serial_plat *plat = dev_get_plat(dev);
 117        struct meson_uart *const uart = plat->reg;
 118        uint32_t status = readl(&uart->status);
 119
 120        if (input) {
 121                if (status & AML_UART_RX_EMPTY)
 122                        return false;
 123
 124                /*
 125                 * Handle and drop any RX error here to avoid
 126                 * returning true here when an error byte is in the FIFO
 127                 */
 128                if (status & AML_UART_ERR) {
 129                        meson_serial_rx_error(dev);
 130                        return false;
 131                }
 132
 133                return true;
 134        } else {
 135                return !(status & AML_UART_TX_FULL);
 136        }
 137}
 138
 139static int meson_serial_of_to_plat(struct udevice *dev)
 140{
 141        struct meson_serial_plat *plat = dev_get_plat(dev);
 142        fdt_addr_t addr;
 143
 144        addr = dev_read_addr(dev);
 145        if (addr == FDT_ADDR_T_NONE)
 146                return -EINVAL;
 147
 148        plat->reg = (struct meson_uart *)addr;
 149
 150        return 0;
 151}
 152
 153static const struct dm_serial_ops meson_serial_ops = {
 154        .putc = meson_serial_putc,
 155        .pending = meson_serial_pending,
 156        .getc = meson_serial_getc,
 157};
 158
 159static const struct udevice_id meson_serial_ids[] = {
 160        { .compatible = "amlogic,meson-uart" },
 161        { .compatible = "amlogic,meson-gx-uart" },
 162        { }
 163};
 164
 165U_BOOT_DRIVER(serial_meson) = {
 166        .name           = "serial_meson",
 167        .id             = UCLASS_SERIAL,
 168        .of_match       = meson_serial_ids,
 169        .probe          = meson_serial_probe,
 170        .ops            = &meson_serial_ops,
 171        .of_to_plat = meson_serial_of_to_plat,
 172        .plat_auto      = sizeof(struct meson_serial_plat),
 173};
 174
 175#ifdef CONFIG_DEBUG_UART_MESON
 176
 177#include <debug_uart.h>
 178
 179static inline void _debug_uart_init(void)
 180{
 181}
 182
 183static inline void _debug_uart_putc(int ch)
 184{
 185        struct meson_uart *regs = (struct meson_uart *)CONFIG_DEBUG_UART_BASE;
 186
 187        while (readl(&regs->status) & AML_UART_TX_FULL)
 188                ;
 189
 190        writel(ch, &regs->wfifo);
 191}
 192
 193DEBUG_UART_FUNCS
 194
 195#endif
 196