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