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