uboot/drivers/serial/serial_pic32.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (c) 2015 Paul Thacker <paul.thacker@microchip.com>
   4 *
   5 */
   6#include <common.h>
   7#include <clk.h>
   8#include <dm.h>
   9#include <malloc.h>
  10#include <serial.h>
  11#include <wait_bit.h>
  12#include <asm/global_data.h>
  13#include <linux/bitops.h>
  14#include <mach/pic32.h>
  15#include <dt-bindings/clock/microchip,clock.h>
  16
  17DECLARE_GLOBAL_DATA_PTR;
  18
  19/* UART Control Registers */
  20#define U_MOD           0x00
  21#define U_MODCLR        (U_MOD + _CLR_OFFSET)
  22#define U_MODSET        (U_MOD + _SET_OFFSET)
  23#define U_STA           0x10
  24#define U_STACLR        (U_STA + _CLR_OFFSET)
  25#define U_STASET        (U_STA + _SET_OFFSET)
  26#define U_TXR           0x20
  27#define U_RXR           0x30
  28#define U_BRG           0x40
  29
  30/* U_MOD bits */
  31#define UART_ENABLE             BIT(15)
  32
  33/* U_STA bits */
  34#define UART_RX_ENABLE          BIT(12)
  35#define UART_TX_BRK             BIT(11)
  36#define UART_TX_ENABLE          BIT(10)
  37#define UART_TX_FULL            BIT(9)
  38#define UART_TX_EMPTY           BIT(8)
  39#define UART_RX_OVER            BIT(1)
  40#define UART_RX_DATA_AVAIL      BIT(0)
  41
  42struct pic32_uart_priv {
  43        void __iomem *base;
  44        ulong uartclk;
  45};
  46
  47/*
  48 * Initialize the serial port with the given baudrate.
  49 * The settings are always 8 data bits, no parity, 1 stop bit, no start bits.
  50 */
  51static int pic32_serial_init(void __iomem *base, ulong clk, u32 baudrate)
  52{
  53        u32 div = DIV_ROUND_CLOSEST(clk, baudrate * 16);
  54
  55        /* wait for TX FIFO to empty */
  56        wait_for_bit_le32(base + U_STA, UART_TX_EMPTY,
  57                          true, CONFIG_SYS_HZ, false);
  58
  59        /* send break */
  60        writel(UART_TX_BRK, base + U_STASET);
  61
  62        /* disable and clear mode */
  63        writel(0, base + U_MOD);
  64        writel(0, base + U_STA);
  65
  66        /* set baud rate generator */
  67        writel(div - 1, base + U_BRG);
  68
  69        /* enable the UART for TX and RX */
  70        writel(UART_TX_ENABLE | UART_RX_ENABLE, base + U_STASET);
  71
  72        /* enable the UART */
  73        writel(UART_ENABLE, base + U_MODSET);
  74        return 0;
  75}
  76
  77/* Check whether any char pending in RX fifo */
  78static int pic32_uart_pending_input(void __iomem *base)
  79{
  80        /* check if rx buffer overrun error has occurred */
  81        if (readl(base + U_STA) & UART_RX_OVER) {
  82                readl(base + U_RXR);
  83
  84                /* clear overrun error to keep receiving */
  85                writel(UART_RX_OVER, base + U_STACLR);
  86        }
  87
  88        /* In PIC32 there is no way to know number of outstanding
  89         * chars in rx-fifo. Only it can be known whether there is any.
  90         */
  91        return readl(base + U_STA) & UART_RX_DATA_AVAIL;
  92}
  93
  94static int pic32_uart_pending(struct udevice *dev, bool input)
  95{
  96        struct pic32_uart_priv *priv = dev_get_priv(dev);
  97
  98        if (input)
  99                return pic32_uart_pending_input(priv->base);
 100
 101        return !(readl(priv->base + U_STA) & UART_TX_EMPTY);
 102}
 103
 104static int pic32_uart_setbrg(struct udevice *dev, int baudrate)
 105{
 106        struct pic32_uart_priv *priv = dev_get_priv(dev);
 107
 108        return pic32_serial_init(priv->base, priv->uartclk, baudrate);
 109}
 110
 111static int pic32_uart_putc(struct udevice *dev, const char ch)
 112{
 113        struct pic32_uart_priv *priv = dev_get_priv(dev);
 114
 115        /* Check if Tx FIFO is full */
 116        if (readl(priv->base + U_STA) & UART_TX_FULL)
 117                return -EAGAIN;
 118
 119        /* pump the char to tx buffer */
 120        writel(ch, priv->base + U_TXR);
 121
 122        return 0;
 123}
 124
 125static int pic32_uart_getc(struct udevice *dev)
 126{
 127        struct pic32_uart_priv *priv = dev_get_priv(dev);
 128
 129        /* return error if RX fifo is empty */
 130        if (!pic32_uart_pending_input(priv->base))
 131                return -EAGAIN;
 132
 133        /* read the character from rx buffer */
 134        return readl(priv->base + U_RXR) & 0xff;
 135}
 136
 137static int pic32_uart_probe(struct udevice *dev)
 138{
 139        struct pic32_uart_priv *priv = dev_get_priv(dev);
 140        struct clk clk;
 141        fdt_addr_t addr;
 142        fdt_size_t size;
 143        int ret;
 144
 145        /* get address */
 146        addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
 147                                    &size);
 148        if (addr == FDT_ADDR_T_NONE)
 149                return -EINVAL;
 150
 151        priv->base = ioremap(addr, size);
 152
 153        /* get clock rate */
 154        ret = clk_get_by_index(dev, 0, &clk);
 155        if (ret < 0)
 156                return ret;
 157        priv->uartclk = clk_get_rate(&clk);
 158        clk_free(&clk);
 159
 160        /* initialize serial */
 161        return pic32_serial_init(priv->base, priv->uartclk, CONFIG_BAUDRATE);
 162}
 163
 164static const struct dm_serial_ops pic32_uart_ops = {
 165        .putc           = pic32_uart_putc,
 166        .pending        = pic32_uart_pending,
 167        .getc           = pic32_uart_getc,
 168        .setbrg         = pic32_uart_setbrg,
 169};
 170
 171static const struct udevice_id pic32_uart_ids[] = {
 172        { .compatible = "microchip,pic32mzda-uart" },
 173        {}
 174};
 175
 176U_BOOT_DRIVER(pic32_serial) = {
 177        .name           = "pic32-uart",
 178        .id             = UCLASS_SERIAL,
 179        .of_match       = pic32_uart_ids,
 180        .probe          = pic32_uart_probe,
 181        .ops            = &pic32_uart_ops,
 182        .priv_auto      = sizeof(struct pic32_uart_priv),
 183};
 184
 185#ifdef CONFIG_DEBUG_UART_PIC32
 186#include <debug_uart.h>
 187
 188static inline void _debug_uart_init(void)
 189{
 190        void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;
 191
 192        pic32_serial_init(base, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
 193}
 194
 195static inline void _debug_uart_putc(int ch)
 196{
 197        writel(ch, CONFIG_DEBUG_UART_BASE + U_TXR);
 198}
 199
 200DEBUG_UART_FUNCS
 201#endif
 202