uboot/drivers/serial/serial_lpuart.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2013 Freescale Semiconductor, Inc.
   4 */
   5
   6#include <common.h>
   7#include <dm.h>
   8#include <fsl_lpuart.h>
   9#include <watchdog.h>
  10#include <asm/io.h>
  11#include <serial.h>
  12#include <linux/compiler.h>
  13#include <asm/arch/imx-regs.h>
  14#include <asm/arch/clock.h>
  15
  16#define US1_TDRE        (1 << 7)
  17#define US1_RDRF        (1 << 5)
  18#define US1_OR          (1 << 3)
  19#define UC2_TE          (1 << 3)
  20#define UC2_RE          (1 << 2)
  21#define CFIFO_TXFLUSH   (1 << 7)
  22#define CFIFO_RXFLUSH   (1 << 6)
  23#define SFIFO_RXOF      (1 << 2)
  24#define SFIFO_RXUF      (1 << 0)
  25
  26#define STAT_LBKDIF     (1 << 31)
  27#define STAT_RXEDGIF    (1 << 30)
  28#define STAT_TDRE       (1 << 23)
  29#define STAT_RDRF       (1 << 21)
  30#define STAT_IDLE       (1 << 20)
  31#define STAT_OR         (1 << 19)
  32#define STAT_NF         (1 << 18)
  33#define STAT_FE         (1 << 17)
  34#define STAT_PF         (1 << 16)
  35#define STAT_MA1F       (1 << 15)
  36#define STAT_MA2F       (1 << 14)
  37#define STAT_FLAGS      (STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
  38                         STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
  39
  40#define CTRL_TE         (1 << 19)
  41#define CTRL_RE         (1 << 18)
  42
  43#define FIFO_TXFE               0x80
  44#define FIFO_RXFE               0x40
  45
  46#define WATER_TXWATER_OFF       1
  47#define WATER_RXWATER_OFF       16
  48
  49DECLARE_GLOBAL_DATA_PTR;
  50
  51#define LPUART_FLAG_REGMAP_32BIT_REG    BIT(0)
  52#define LPUART_FLAG_REGMAP_ENDIAN_BIG   BIT(1)
  53
  54enum lpuart_devtype {
  55        DEV_VF610 = 1,
  56        DEV_LS1021A,
  57        DEV_MX7ULP
  58};
  59
  60struct lpuart_serial_platdata {
  61        void *reg;
  62        enum lpuart_devtype devtype;
  63        ulong flags;
  64};
  65
  66static void lpuart_read32(u32 flags, u32 *addr, u32 *val)
  67{
  68        if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
  69                if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
  70                        *(u32 *)val = in_be32(addr);
  71                else
  72                        *(u32 *)val = in_le32(addr);
  73        }
  74}
  75
  76static void lpuart_write32(u32 flags, u32 *addr, u32 val)
  77{
  78        if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
  79                if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
  80                        out_be32(addr, val);
  81                else
  82                        out_le32(addr, val);
  83        }
  84}
  85
  86
  87#ifndef CONFIG_SYS_CLK_FREQ
  88#define CONFIG_SYS_CLK_FREQ     0
  89#endif
  90
  91u32 __weak get_lpuart_clk(void)
  92{
  93        return CONFIG_SYS_CLK_FREQ;
  94}
  95
  96static bool is_lpuart32(struct udevice *dev)
  97{
  98        struct lpuart_serial_platdata *plat = dev->platdata;
  99
 100        return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG;
 101}
 102
 103static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat,
 104                                  int baudrate)
 105{
 106        struct lpuart_fsl *base = plat->reg;
 107        u32 clk = get_lpuart_clk();
 108        u16 sbr;
 109
 110        sbr = (u16)(clk / (16 * baudrate));
 111
 112        /* place adjustment later - n/32 BRFA */
 113        __raw_writeb(sbr >> 8, &base->ubdh);
 114        __raw_writeb(sbr & 0xff, &base->ubdl);
 115}
 116
 117static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat)
 118{
 119        struct lpuart_fsl *base = plat->reg;
 120        while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
 121                WATCHDOG_RESET();
 122
 123        barrier();
 124
 125        return __raw_readb(&base->ud);
 126}
 127
 128static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat,
 129                                const char c)
 130{
 131        struct lpuart_fsl *base = plat->reg;
 132
 133        while (!(__raw_readb(&base->us1) & US1_TDRE))
 134                WATCHDOG_RESET();
 135
 136        __raw_writeb(c, &base->ud);
 137}
 138
 139/* Test whether a character is in the RX buffer */
 140static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat)
 141{
 142        struct lpuart_fsl *base = plat->reg;
 143
 144        if (__raw_readb(&base->urcfifo) == 0)
 145                return 0;
 146
 147        return 1;
 148}
 149
 150/*
 151 * Initialise the serial port with the given baudrate. The settings
 152 * are always 8 data bits, no parity, 1 stop bit, no start bits.
 153 */
 154static int _lpuart_serial_init(struct lpuart_serial_platdata *plat)
 155{
 156        struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg;
 157        u8 ctrl;
 158
 159        ctrl = __raw_readb(&base->uc2);
 160        ctrl &= ~UC2_RE;
 161        ctrl &= ~UC2_TE;
 162        __raw_writeb(ctrl, &base->uc2);
 163
 164        __raw_writeb(0, &base->umodem);
 165        __raw_writeb(0, &base->uc1);
 166
 167        /* Disable FIFO and flush buffer */
 168        __raw_writeb(0x0, &base->upfifo);
 169        __raw_writeb(0x0, &base->utwfifo);
 170        __raw_writeb(0x1, &base->urwfifo);
 171        __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
 172
 173        /* provide data bits, parity, stop bit, etc */
 174        _lpuart_serial_setbrg(plat, gd->baudrate);
 175
 176        __raw_writeb(UC2_RE | UC2_TE, &base->uc2);
 177
 178        return 0;
 179}
 180
 181static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat,
 182                                         int baudrate)
 183{
 184        struct lpuart_fsl_reg32 *base = plat->reg;
 185        u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
 186        u32 clk = get_lpuart_clk();
 187
 188        baud_diff = baudrate;
 189        osr = 0;
 190        sbr = 0;
 191
 192        for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
 193                tmp_sbr = (clk / (baudrate * tmp_osr));
 194
 195                if (tmp_sbr == 0)
 196                        tmp_sbr = 1;
 197
 198                /*calculate difference in actual buad w/ current values */
 199                tmp_diff = (clk / (tmp_osr * tmp_sbr));
 200                tmp_diff = tmp_diff - baudrate;
 201
 202                /* select best values between sbr and sbr+1 */
 203                if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) {
 204                        tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1)));
 205                        tmp_sbr++;
 206                }
 207
 208                if (tmp_diff <= baud_diff) {
 209                        baud_diff = tmp_diff;
 210                        osr = tmp_osr;
 211                        sbr = tmp_sbr;
 212                }
 213        }
 214
 215        /*
 216         * TODO: handle buadrate outside acceptable rate
 217         * if (baudDiff > ((config->baudRate_Bps / 100) * 3))
 218         * {
 219         *   Unacceptable baud rate difference of more than 3%
 220         *   return kStatus_LPUART_BaudrateNotSupport;
 221         * }
 222         */
 223        tmp = in_le32(&base->baud);
 224
 225        if ((osr > 3) && (osr < 8))
 226                tmp |= LPUART_BAUD_BOTHEDGE_MASK;
 227
 228        tmp &= ~LPUART_BAUD_OSR_MASK;
 229        tmp |= LPUART_BAUD_OSR(osr-1);
 230
 231        tmp &= ~LPUART_BAUD_SBR_MASK;
 232        tmp |= LPUART_BAUD_SBR(sbr);
 233
 234        /* explicitly disable 10 bit mode & set 1 stop bit */
 235        tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK);
 236
 237        out_le32(&base->baud, tmp);
 238}
 239
 240static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat,
 241                                    int baudrate)
 242{
 243        struct lpuart_fsl_reg32 *base = plat->reg;
 244        u32 clk = get_lpuart_clk();
 245        u32 sbr;
 246
 247        sbr = (clk / (16 * baudrate));
 248
 249        /* place adjustment later - n/32 BRFA */
 250        lpuart_write32(plat->flags, &base->baud, sbr);
 251}
 252
 253static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat)
 254{
 255        struct lpuart_fsl_reg32 *base = plat->reg;
 256        u32 stat, val;
 257
 258        lpuart_read32(plat->flags, &base->stat, &stat);
 259        while ((stat & STAT_RDRF) == 0) {
 260                lpuart_write32(plat->flags, &base->stat, STAT_FLAGS);
 261                WATCHDOG_RESET();
 262                lpuart_read32(plat->flags, &base->stat, &stat);
 263        }
 264
 265        lpuart_read32(plat->flags, &base->data, &val);
 266
 267        lpuart_read32(plat->flags, &base->stat, &stat);
 268        if (stat & STAT_OR)
 269                lpuart_write32(plat->flags, &base->stat, STAT_OR);
 270
 271        return val & 0x3ff;
 272}
 273
 274static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
 275                                  const char c)
 276{
 277        struct lpuart_fsl_reg32 *base = plat->reg;
 278        u32 stat;
 279
 280        if (c == '\n')
 281                serial_putc('\r');
 282
 283        while (true) {
 284                lpuart_read32(plat->flags, &base->stat, &stat);
 285
 286                if ((stat & STAT_TDRE))
 287                        break;
 288
 289                WATCHDOG_RESET();
 290        }
 291
 292        lpuart_write32(plat->flags, &base->data, c);
 293}
 294
 295/* Test whether a character is in the RX buffer */
 296static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat)
 297{
 298        struct lpuart_fsl_reg32 *base = plat->reg;
 299        u32 water;
 300
 301        lpuart_read32(plat->flags, &base->water, &water);
 302
 303        if ((water >> 24) == 0)
 304                return 0;
 305
 306        return 1;
 307}
 308
 309/*
 310 * Initialise the serial port with the given baudrate. The settings
 311 * are always 8 data bits, no parity, 1 stop bit, no start bits.
 312 */
 313static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat)
 314{
 315        struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg;
 316        u32 ctrl;
 317
 318        lpuart_read32(plat->flags, &base->ctrl, &ctrl);
 319        ctrl &= ~CTRL_RE;
 320        ctrl &= ~CTRL_TE;
 321        lpuart_write32(plat->flags, &base->ctrl, ctrl);
 322
 323        lpuart_write32(plat->flags, &base->modir, 0);
 324        lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
 325
 326        lpuart_write32(plat->flags, &base->match, 0);
 327
 328        if (plat->devtype == DEV_MX7ULP) {
 329                _lpuart32_serial_setbrg_7ulp(plat, gd->baudrate);
 330        } else {
 331                /* provide data bits, parity, stop bit, etc */
 332                _lpuart32_serial_setbrg(plat, gd->baudrate);
 333        }
 334
 335        lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE);
 336
 337        return 0;
 338}
 339
 340static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
 341{
 342        struct lpuart_serial_platdata *plat = dev->platdata;
 343
 344        if (is_lpuart32(dev)) {
 345                if (plat->devtype == DEV_MX7ULP)
 346                        _lpuart32_serial_setbrg_7ulp(plat, baudrate);
 347                else
 348                        _lpuart32_serial_setbrg(plat, baudrate);
 349        } else {
 350                _lpuart_serial_setbrg(plat, baudrate);
 351        }
 352
 353        return 0;
 354}
 355
 356static int lpuart_serial_getc(struct udevice *dev)
 357{
 358        struct lpuart_serial_platdata *plat = dev->platdata;
 359
 360        if (is_lpuart32(dev))
 361                return _lpuart32_serial_getc(plat);
 362
 363        return _lpuart_serial_getc(plat);
 364}
 365
 366static int lpuart_serial_putc(struct udevice *dev, const char c)
 367{
 368        struct lpuart_serial_platdata *plat = dev->platdata;
 369
 370        if (is_lpuart32(dev))
 371                _lpuart32_serial_putc(plat, c);
 372        else
 373                _lpuart_serial_putc(plat, c);
 374
 375        return 0;
 376}
 377
 378static int lpuart_serial_pending(struct udevice *dev, bool input)
 379{
 380        struct lpuart_serial_platdata *plat = dev->platdata;
 381        struct lpuart_fsl *reg = plat->reg;
 382        struct lpuart_fsl_reg32 *reg32 = plat->reg;
 383        u32 stat;
 384
 385        if (is_lpuart32(dev)) {
 386                if (input) {
 387                        return _lpuart32_serial_tstc(plat);
 388                } else {
 389                        lpuart_read32(plat->flags, &reg32->stat, &stat);
 390                        return stat & STAT_TDRE ? 0 : 1;
 391                }
 392        }
 393
 394        if (input)
 395                return _lpuart_serial_tstc(plat);
 396        else
 397                return __raw_readb(&reg->us1) & US1_TDRE ? 0 : 1;
 398}
 399
 400static int lpuart_serial_probe(struct udevice *dev)
 401{
 402        struct lpuart_serial_platdata *plat = dev->platdata;
 403
 404        if (is_lpuart32(dev))
 405                return _lpuart32_serial_init(plat);
 406        else
 407                return _lpuart_serial_init(plat);
 408}
 409
 410static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
 411{
 412        struct lpuart_serial_platdata *plat = dev->platdata;
 413        const void *blob = gd->fdt_blob;
 414        int node = dev_of_offset(dev);
 415        fdt_addr_t addr;
 416
 417        addr = devfdt_get_addr(dev);
 418        if (addr == FDT_ADDR_T_NONE)
 419                return -EINVAL;
 420
 421        plat->reg = (void *)addr;
 422        plat->flags = dev_get_driver_data(dev);
 423
 424        if (!fdt_node_check_compatible(blob, node, "fsl,ls1021a-lpuart"))
 425                plat->devtype = DEV_LS1021A;
 426        else if (!fdt_node_check_compatible(blob, node, "fsl,imx7ulp-lpuart"))
 427                plat->devtype = DEV_MX7ULP;
 428        else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart"))
 429                plat->devtype = DEV_VF610;
 430
 431        return 0;
 432}
 433
 434static const struct dm_serial_ops lpuart_serial_ops = {
 435        .putc = lpuart_serial_putc,
 436        .pending = lpuart_serial_pending,
 437        .getc = lpuart_serial_getc,
 438        .setbrg = lpuart_serial_setbrg,
 439};
 440
 441static const struct udevice_id lpuart_serial_ids[] = {
 442        { .compatible = "fsl,ls1021a-lpuart", .data =
 443                LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG },
 444        { .compatible = "fsl,imx7ulp-lpuart",
 445                .data = LPUART_FLAG_REGMAP_32BIT_REG },
 446        { .compatible = "fsl,vf610-lpuart"},
 447        { }
 448};
 449
 450U_BOOT_DRIVER(serial_lpuart) = {
 451        .name   = "serial_lpuart",
 452        .id     = UCLASS_SERIAL,
 453        .of_match = lpuart_serial_ids,
 454        .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
 455        .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
 456        .probe = lpuart_serial_probe,
 457        .ops    = &lpuart_serial_ops,
 458        .flags = DM_FLAG_PRE_RELOC,
 459};
 460