uboot/drivers/serial/serial_npcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2021 Nuvoton Technology Corp.
   4 */
   5
   6#include <common.h>
   7#include <clk.h>
   8#include <dm.h>
   9#include <serial.h>
  10
  11struct npcm_uart {
  12        union {
  13                u32     rbr;    /* Receive Buffer Register */
  14                u32     thr;    /* Transmit Holding Register */
  15                u32     dll;    /* Divisor Latch (Low Byte) Register */
  16        };
  17        union {
  18                u32     ier;    /* Interrupt Enable Register */
  19                u32     dlm;    /* Divisor Latch (Low Byte) Register */
  20        };
  21        union {
  22                u32     iir;    /* Interrupt Identification Register */
  23                u32     fcr;    /* FIFO Control Register */
  24        };
  25        u32     lcr;            /* Line Control Register */
  26        u32     mcr;            /* Modem Control Register */
  27        u32     lsr;            /* Line Status Control Register */
  28        u32     msr;            /* Modem Status Register */
  29        u32     tor;            /* Timeout Register */
  30};
  31
  32#define LCR_WLS_8BITS   3       /* 8-bit word length select */
  33#define FCR_TFR         BIT(2)  /* TxFIFO reset */
  34#define FCR_RFR         BIT(1)  /* RxFIFO reset */
  35#define FCR_FME         BIT(0)  /* FIFO mode enable */
  36#define LSR_THRE        BIT(5)  /* Status of TxFIFO empty */
  37#define LSR_RFDR        BIT(0)  /* Status of RxFIFO data ready */
  38#define LCR_DLAB        BIT(7)  /* Divisor latch access bit */
  39
  40struct npcm_serial_plat {
  41        struct npcm_uart *reg;
  42        u32 uart_clk;           /* frequency of uart clock source */
  43};
  44
  45static int npcm_serial_pending(struct udevice *dev, bool input)
  46{
  47        struct npcm_serial_plat *plat = dev_get_plat(dev);
  48        struct npcm_uart *uart = plat->reg;
  49
  50        if (input)
  51                return readb(&uart->lsr) & LSR_RFDR ? 1 : 0;
  52        else
  53                return readb(&uart->lsr) & LSR_THRE ? 0 : 1;
  54}
  55
  56static int npcm_serial_putc(struct udevice *dev, const char ch)
  57{
  58        struct npcm_serial_plat *plat = dev_get_plat(dev);
  59        struct npcm_uart *uart = plat->reg;
  60
  61        if (!(readb(&uart->lsr) & LSR_THRE))
  62                return -EAGAIN;
  63
  64        writeb(ch, &uart->thr);
  65
  66        return 0;
  67}
  68
  69static int npcm_serial_getc(struct udevice *dev)
  70{
  71        struct npcm_serial_plat *plat = dev_get_plat(dev);
  72        struct npcm_uart *uart = plat->reg;
  73
  74        if (!(readb(&uart->lsr) & LSR_RFDR))
  75                return -EAGAIN;
  76
  77        return readb(&uart->rbr);
  78}
  79
  80static int npcm_serial_setbrg(struct udevice *dev, int baudrate)
  81{
  82        struct npcm_serial_plat *plat = dev_get_plat(dev);
  83        struct npcm_uart *uart = plat->reg;
  84        u16 divisor;
  85
  86        /* BaudOut = UART Clock / (16 * [Divisor + 2]) */
  87        divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2;
  88
  89        setbits_8(&uart->lcr, LCR_DLAB);
  90        writeb(divisor & 0xff, &uart->dll);
  91        writeb(divisor >> 8, &uart->dlm);
  92        clrbits_8(&uart->lcr, LCR_DLAB);
  93
  94        return 0;
  95}
  96
  97static int npcm_serial_probe(struct udevice *dev)
  98{
  99        struct npcm_serial_plat *plat = dev_get_plat(dev);
 100        struct npcm_uart *uart = plat->reg;
 101        struct clk clk, parent;
 102        u32 freq;
 103        int ret;
 104
 105        plat->reg = dev_read_addr_ptr(dev);
 106        freq = dev_read_u32_default(dev, "clock-frequency", 0);
 107
 108        ret = clk_get_by_index(dev, 0, &clk);
 109        if (ret < 0)
 110                return ret;
 111
 112        ret = clk_get_by_index(dev, 1, &parent);
 113        if (!ret) {
 114                ret = clk_set_parent(&clk, &parent);
 115                if (ret)
 116                        return ret;
 117        }
 118
 119        ret = clk_set_rate(&clk, freq);
 120        if (ret < 0)
 121                return ret;
 122        plat->uart_clk = ret;
 123
 124        /* Disable all interrupt */
 125        writeb(0, &uart->ier);
 126
 127        /* Set 8 bit, 1 stop, no parity */
 128        writeb(LCR_WLS_8BITS, &uart->lcr);
 129
 130        /* Reset RX/TX FIFO */
 131        writeb(FCR_FME | FCR_RFR | FCR_TFR, &uart->fcr);
 132
 133        return 0;
 134}
 135
 136static const struct dm_serial_ops npcm_serial_ops = {
 137        .getc = npcm_serial_getc,
 138        .setbrg = npcm_serial_setbrg,
 139        .putc = npcm_serial_putc,
 140        .pending = npcm_serial_pending,
 141};
 142
 143static const struct udevice_id npcm_serial_ids[] = {
 144        { .compatible = "nuvoton,npcm750-uart" },
 145        { .compatible = "nuvoton,npcm845-uart" },
 146        { }
 147};
 148
 149U_BOOT_DRIVER(serial_npcm) = {
 150        .name   = "serial_npcm",
 151        .id     = UCLASS_SERIAL,
 152        .of_match = npcm_serial_ids,
 153        .plat_auto  = sizeof(struct npcm_serial_plat),
 154        .probe = npcm_serial_probe,
 155        .ops    = &npcm_serial_ops,
 156        .flags = DM_FLAG_PRE_RELOC,
 157};
 158