1427eba70SAlison Wang /* 2427eba70SAlison Wang * Copyright 2013 Freescale Semiconductor, Inc. 3427eba70SAlison Wang * 41a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 5427eba70SAlison Wang */ 6427eba70SAlison Wang 7427eba70SAlison Wang #include <common.h> 8fdbae099SBin Meng #include <dm.h> 9*c40d612bSPeng Fan #include <fsl_lpuart.h> 10427eba70SAlison Wang #include <watchdog.h> 11427eba70SAlison Wang #include <asm/io.h> 12427eba70SAlison Wang #include <serial.h> 13427eba70SAlison Wang #include <linux/compiler.h> 14427eba70SAlison Wang #include <asm/arch/imx-regs.h> 15427eba70SAlison Wang #include <asm/arch/clock.h> 16427eba70SAlison Wang 17427eba70SAlison Wang #define US1_TDRE (1 << 7) 18427eba70SAlison Wang #define US1_RDRF (1 << 5) 19a3db78d8SStefan Agner #define US1_OR (1 << 3) 20427eba70SAlison Wang #define UC2_TE (1 << 3) 21427eba70SAlison Wang #define UC2_RE (1 << 2) 2289e69fd4SStefan Agner #define CFIFO_TXFLUSH (1 << 7) 2389e69fd4SStefan Agner #define CFIFO_RXFLUSH (1 << 6) 2489e69fd4SStefan Agner #define SFIFO_RXOF (1 << 2) 2589e69fd4SStefan Agner #define SFIFO_RXUF (1 << 0) 26427eba70SAlison Wang 276209e14cSJingchang Lu #define STAT_LBKDIF (1 << 31) 286209e14cSJingchang Lu #define STAT_RXEDGIF (1 << 30) 296209e14cSJingchang Lu #define STAT_TDRE (1 << 23) 306209e14cSJingchang Lu #define STAT_RDRF (1 << 21) 316209e14cSJingchang Lu #define STAT_IDLE (1 << 20) 326209e14cSJingchang Lu #define STAT_OR (1 << 19) 336209e14cSJingchang Lu #define STAT_NF (1 << 18) 346209e14cSJingchang Lu #define STAT_FE (1 << 17) 356209e14cSJingchang Lu #define STAT_PF (1 << 16) 366209e14cSJingchang Lu #define STAT_MA1F (1 << 15) 376209e14cSJingchang Lu #define STAT_MA2F (1 << 14) 386209e14cSJingchang Lu #define STAT_FLAGS (STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \ 396209e14cSJingchang Lu STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F) 406209e14cSJingchang Lu 416209e14cSJingchang Lu #define CTRL_TE (1 << 19) 426209e14cSJingchang Lu #define CTRL_RE (1 << 18) 436209e14cSJingchang Lu 446209e14cSJingchang Lu #define FIFO_TXFE 0x80 456209e14cSJingchang Lu #define FIFO_RXFE 0x40 466209e14cSJingchang Lu 476209e14cSJingchang Lu #define WATER_TXWATER_OFF 1 486209e14cSJingchang Lu #define WATER_RXWATER_OFF 16 496209e14cSJingchang Lu 50427eba70SAlison Wang DECLARE_GLOBAL_DATA_PTR; 51427eba70SAlison Wang 52*c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_32BIT_REG BIT(0) 53*c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_ENDIAN_BIG BIT(1) 54*c40d612bSPeng Fan 55fdbae099SBin Meng struct lpuart_serial_platdata { 56*c40d612bSPeng Fan void *reg; 57*c40d612bSPeng Fan ulong flags; 58fdbae099SBin Meng }; 59fdbae099SBin Meng 60*c40d612bSPeng Fan static void lpuart_read32(u32 flags, u32 *addr, u32 *val) 61427eba70SAlison Wang { 62*c40d612bSPeng Fan if (flags & LPUART_FLAG_REGMAP_32BIT_REG) { 63*c40d612bSPeng Fan if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG) 64*c40d612bSPeng Fan *(u32 *)val = in_be32(addr); 65*c40d612bSPeng Fan else 66*c40d612bSPeng Fan *(u32 *)val = in_le32(addr); 67*c40d612bSPeng Fan } 68*c40d612bSPeng Fan } 69*c40d612bSPeng Fan 70*c40d612bSPeng Fan static void lpuart_write32(u32 flags, u32 *addr, u32 val) 71*c40d612bSPeng Fan { 72*c40d612bSPeng Fan if (flags & LPUART_FLAG_REGMAP_32BIT_REG) { 73*c40d612bSPeng Fan if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG) 74*c40d612bSPeng Fan out_be32(addr, val); 75*c40d612bSPeng Fan else 76*c40d612bSPeng Fan out_le32(addr, val); 77*c40d612bSPeng Fan } 78*c40d612bSPeng Fan } 79*c40d612bSPeng Fan 80*c40d612bSPeng Fan 81*c40d612bSPeng Fan #ifndef CONFIG_SYS_CLK_FREQ 82*c40d612bSPeng Fan #define CONFIG_SYS_CLK_FREQ 0 83*c40d612bSPeng Fan #endif 84*c40d612bSPeng Fan 85*c40d612bSPeng Fan u32 __weak get_lpuart_clk(void) 86*c40d612bSPeng Fan { 87*c40d612bSPeng Fan return CONFIG_SYS_CLK_FREQ; 88*c40d612bSPeng Fan } 89*c40d612bSPeng Fan 90*c40d612bSPeng Fan static bool is_lpuart32(struct udevice *dev) 91*c40d612bSPeng Fan { 92*c40d612bSPeng Fan struct lpuart_serial_platdata *plat = dev->platdata; 93*c40d612bSPeng Fan 94*c40d612bSPeng Fan return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG; 95*c40d612bSPeng Fan } 96*c40d612bSPeng Fan 97*c40d612bSPeng Fan static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat, 98*c40d612bSPeng Fan int baudrate) 99*c40d612bSPeng Fan { 100*c40d612bSPeng Fan struct lpuart_fsl *base = plat->reg; 101*c40d612bSPeng Fan u32 clk = get_lpuart_clk(); 102427eba70SAlison Wang u16 sbr; 103427eba70SAlison Wang 1046ca13b12SBin Meng sbr = (u16)(clk / (16 * baudrate)); 105427eba70SAlison Wang 10647f1bfcaSBin Meng /* place adjustment later - n/32 BRFA */ 107427eba70SAlison Wang __raw_writeb(sbr >> 8, &base->ubdh); 108427eba70SAlison Wang __raw_writeb(sbr & 0xff, &base->ubdl); 109427eba70SAlison Wang } 110427eba70SAlison Wang 111*c40d612bSPeng Fan static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat) 112427eba70SAlison Wang { 113*c40d612bSPeng Fan struct lpuart_fsl *base = plat->reg; 114a3db78d8SStefan Agner while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR))) 115427eba70SAlison Wang WATCHDOG_RESET(); 116427eba70SAlison Wang 117a3db78d8SStefan Agner barrier(); 118427eba70SAlison Wang 119427eba70SAlison Wang return __raw_readb(&base->ud); 120427eba70SAlison Wang } 121427eba70SAlison Wang 122*c40d612bSPeng Fan static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat, 123*c40d612bSPeng Fan const char c) 124427eba70SAlison Wang { 125*c40d612bSPeng Fan struct lpuart_fsl *base = plat->reg; 126*c40d612bSPeng Fan 127427eba70SAlison Wang while (!(__raw_readb(&base->us1) & US1_TDRE)) 128427eba70SAlison Wang WATCHDOG_RESET(); 129427eba70SAlison Wang 130427eba70SAlison Wang __raw_writeb(c, &base->ud); 131427eba70SAlison Wang } 132427eba70SAlison Wang 13347f1bfcaSBin Meng /* Test whether a character is in the RX buffer */ 134*c40d612bSPeng Fan static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat) 135427eba70SAlison Wang { 136*c40d612bSPeng Fan struct lpuart_fsl *base = plat->reg; 137*c40d612bSPeng Fan 138427eba70SAlison Wang if (__raw_readb(&base->urcfifo) == 0) 139427eba70SAlison Wang return 0; 140427eba70SAlison Wang 141427eba70SAlison Wang return 1; 142427eba70SAlison Wang } 143427eba70SAlison Wang 144427eba70SAlison Wang /* 145427eba70SAlison Wang * Initialise the serial port with the given baudrate. The settings 146427eba70SAlison Wang * are always 8 data bits, no parity, 1 stop bit, no start bits. 147427eba70SAlison Wang */ 148*c40d612bSPeng Fan static int _lpuart_serial_init(struct lpuart_serial_platdata *plat) 149427eba70SAlison Wang { 150*c40d612bSPeng Fan struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg; 151427eba70SAlison Wang u8 ctrl; 152427eba70SAlison Wang 153427eba70SAlison Wang ctrl = __raw_readb(&base->uc2); 154427eba70SAlison Wang ctrl &= ~UC2_RE; 155427eba70SAlison Wang ctrl &= ~UC2_TE; 156427eba70SAlison Wang __raw_writeb(ctrl, &base->uc2); 157427eba70SAlison Wang 158427eba70SAlison Wang __raw_writeb(0, &base->umodem); 159427eba70SAlison Wang __raw_writeb(0, &base->uc1); 160427eba70SAlison Wang 16189e69fd4SStefan Agner /* Disable FIFO and flush buffer */ 16289e69fd4SStefan Agner __raw_writeb(0x0, &base->upfifo); 16389e69fd4SStefan Agner __raw_writeb(0x0, &base->utwfifo); 16489e69fd4SStefan Agner __raw_writeb(0x1, &base->urwfifo); 16589e69fd4SStefan Agner __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo); 16689e69fd4SStefan Agner 167427eba70SAlison Wang /* provide data bits, parity, stop bit, etc */ 168*c40d612bSPeng Fan _lpuart_serial_setbrg(plat, gd->baudrate); 169427eba70SAlison Wang 170427eba70SAlison Wang __raw_writeb(UC2_RE | UC2_TE, &base->uc2); 171427eba70SAlison Wang 172427eba70SAlison Wang return 0; 173427eba70SAlison Wang } 174427eba70SAlison Wang 175*c40d612bSPeng Fan static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat, 176*c40d612bSPeng Fan int baudrate) 177fdbae099SBin Meng { 178*c40d612bSPeng Fan struct lpuart_fsl_reg32 *base = plat->reg; 1799b46213bSShaohui Xie u32 clk = get_lpuart_clk(); 1806209e14cSJingchang Lu u32 sbr; 1816209e14cSJingchang Lu 1826ca13b12SBin Meng sbr = (clk / (16 * baudrate)); 1836209e14cSJingchang Lu 18447f1bfcaSBin Meng /* place adjustment later - n/32 BRFA */ 185*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->baud, sbr); 1866209e14cSJingchang Lu } 1876209e14cSJingchang Lu 188*c40d612bSPeng Fan static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat) 1896209e14cSJingchang Lu { 190*c40d612bSPeng Fan struct lpuart_fsl_reg32 *base = plat->reg; 1916209e14cSJingchang Lu u32 stat; 1926209e14cSJingchang Lu 193*c40d612bSPeng Fan lpuart_read32(plat->flags, &base->stat, &stat); 194*c40d612bSPeng Fan while ((stat & STAT_RDRF) == 0) { 195*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->stat, STAT_FLAGS); 1966209e14cSJingchang Lu WATCHDOG_RESET(); 197*c40d612bSPeng Fan lpuart_read32(plat->flags, &base->stat, &stat); 1986209e14cSJingchang Lu } 1996209e14cSJingchang Lu 200*c40d612bSPeng Fan /* Reuse stat */ 201*c40d612bSPeng Fan lpuart_read32(plat->flags, &base->data, &stat); 202*c40d612bSPeng Fan 203*c40d612bSPeng Fan return stat & 0x3ff; 2046209e14cSJingchang Lu } 2056209e14cSJingchang Lu 206*c40d612bSPeng Fan static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat, 207*c40d612bSPeng Fan const char c) 2086209e14cSJingchang Lu { 209*c40d612bSPeng Fan struct lpuart_fsl_reg32 *base = plat->reg; 210*c40d612bSPeng Fan u32 stat; 2116209e14cSJingchang Lu 212*c40d612bSPeng Fan while (true) { 213*c40d612bSPeng Fan lpuart_read32(plat->flags, &base->stat, &stat); 214*c40d612bSPeng Fan 215*c40d612bSPeng Fan if ((stat & STAT_TDRE)) 216*c40d612bSPeng Fan break; 217*c40d612bSPeng Fan 218*c40d612bSPeng Fan WATCHDOG_RESET(); 219*c40d612bSPeng Fan } 220*c40d612bSPeng Fan 221*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->data, c); 2226209e14cSJingchang Lu } 2236209e14cSJingchang Lu 22447f1bfcaSBin Meng /* Test whether a character is in the RX buffer */ 225*c40d612bSPeng Fan static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat) 2266209e14cSJingchang Lu { 227*c40d612bSPeng Fan struct lpuart_fsl_reg32 *base = plat->reg; 228*c40d612bSPeng Fan u32 water; 229*c40d612bSPeng Fan 230*c40d612bSPeng Fan lpuart_read32(plat->flags, &base->water, &water); 231*c40d612bSPeng Fan 232*c40d612bSPeng Fan if ((water >> 24) == 0) 2336209e14cSJingchang Lu return 0; 2346209e14cSJingchang Lu 2356209e14cSJingchang Lu return 1; 2366209e14cSJingchang Lu } 2376209e14cSJingchang Lu 2386209e14cSJingchang Lu /* 2396209e14cSJingchang Lu * Initialise the serial port with the given baudrate. The settings 2406209e14cSJingchang Lu * are always 8 data bits, no parity, 1 stop bit, no start bits. 2416209e14cSJingchang Lu */ 242*c40d612bSPeng Fan static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat) 2436209e14cSJingchang Lu { 244*c40d612bSPeng Fan struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg; 245*c40d612bSPeng Fan u32 ctrl; 2466209e14cSJingchang Lu 247*c40d612bSPeng Fan lpuart_read32(plat->flags, &base->ctrl, &ctrl); 2486209e14cSJingchang Lu ctrl &= ~CTRL_RE; 2496209e14cSJingchang Lu ctrl &= ~CTRL_TE; 250*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->ctrl, ctrl); 2516209e14cSJingchang Lu 252*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->modir, 0); 253*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE)); 2546209e14cSJingchang Lu 255*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->match, 0); 2566209e14cSJingchang Lu 25747f1bfcaSBin Meng /* provide data bits, parity, stop bit, etc */ 258*c40d612bSPeng Fan _lpuart32_serial_setbrg(plat, gd->baudrate); 2596209e14cSJingchang Lu 260*c40d612bSPeng Fan lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE); 2616209e14cSJingchang Lu 2626209e14cSJingchang Lu return 0; 2636209e14cSJingchang Lu } 2646209e14cSJingchang Lu 265*c40d612bSPeng Fan static int lpuart_serial_setbrg(struct udevice *dev, int baudrate) 266fdbae099SBin Meng { 267fdbae099SBin Meng struct lpuart_serial_platdata *plat = dev->platdata; 268fdbae099SBin Meng 269*c40d612bSPeng Fan if (is_lpuart32(dev)) 270*c40d612bSPeng Fan _lpuart32_serial_setbrg(plat, baudrate); 271*c40d612bSPeng Fan else 272*c40d612bSPeng Fan _lpuart_serial_setbrg(plat, baudrate); 273fdbae099SBin Meng 274fdbae099SBin Meng return 0; 275fdbae099SBin Meng } 276fdbae099SBin Meng 277*c40d612bSPeng Fan static int lpuart_serial_getc(struct udevice *dev) 278fdbae099SBin Meng { 279fdbae099SBin Meng struct lpuart_serial_platdata *plat = dev->platdata; 280fdbae099SBin Meng 281*c40d612bSPeng Fan if (is_lpuart32(dev)) 282*c40d612bSPeng Fan return _lpuart32_serial_getc(plat); 283*c40d612bSPeng Fan 284*c40d612bSPeng Fan return _lpuart_serial_getc(plat); 285fdbae099SBin Meng } 286fdbae099SBin Meng 287*c40d612bSPeng Fan static int lpuart_serial_putc(struct udevice *dev, const char c) 288fdbae099SBin Meng { 289fdbae099SBin Meng struct lpuart_serial_platdata *plat = dev->platdata; 290fdbae099SBin Meng 291*c40d612bSPeng Fan if (is_lpuart32(dev)) 292*c40d612bSPeng Fan _lpuart32_serial_putc(plat, c); 293*c40d612bSPeng Fan else 294*c40d612bSPeng Fan _lpuart_serial_putc(plat, c); 295fdbae099SBin Meng 296fdbae099SBin Meng return 0; 297fdbae099SBin Meng } 298fdbae099SBin Meng 299*c40d612bSPeng Fan static int lpuart_serial_pending(struct udevice *dev, bool input) 300fdbae099SBin Meng { 301fdbae099SBin Meng struct lpuart_serial_platdata *plat = dev->platdata; 302fdbae099SBin Meng struct lpuart_fsl *reg = plat->reg; 303*c40d612bSPeng Fan struct lpuart_fsl_reg32 *reg32 = plat->reg; 304*c40d612bSPeng Fan u32 stat; 305*c40d612bSPeng Fan 306*c40d612bSPeng Fan if (is_lpuart32(dev)) { 307*c40d612bSPeng Fan if (input) { 308*c40d612bSPeng Fan return _lpuart32_serial_tstc(plat); 309*c40d612bSPeng Fan } else { 310*c40d612bSPeng Fan lpuart_read32(plat->flags, ®32->stat, &stat); 311*c40d612bSPeng Fan return stat & STAT_TDRE ? 0 : 1; 312*c40d612bSPeng Fan } 313*c40d612bSPeng Fan } 314fdbae099SBin Meng 315fdbae099SBin Meng if (input) 316*c40d612bSPeng Fan return _lpuart_serial_tstc(plat); 317fdbae099SBin Meng else 318*c40d612bSPeng Fan return __raw_readb(®->us1) & US1_TDRE ? 0 : 1; 319fdbae099SBin Meng } 320fdbae099SBin Meng 321*c40d612bSPeng Fan static int lpuart_serial_probe(struct udevice *dev) 322fdbae099SBin Meng { 323fdbae099SBin Meng struct lpuart_serial_platdata *plat = dev->platdata; 324fdbae099SBin Meng 325*c40d612bSPeng Fan if (is_lpuart32(dev)) 326*c40d612bSPeng Fan return _lpuart32_serial_init(plat); 327*c40d612bSPeng Fan else 328*c40d612bSPeng Fan return _lpuart_serial_init(plat); 329fdbae099SBin Meng } 330427eba70SAlison Wang 331fdbae099SBin Meng static int lpuart_serial_ofdata_to_platdata(struct udevice *dev) 332fdbae099SBin Meng { 333fdbae099SBin Meng struct lpuart_serial_platdata *plat = dev->platdata; 334fdbae099SBin Meng fdt_addr_t addr; 335fdbae099SBin Meng 336fdbae099SBin Meng addr = dev_get_addr(dev); 337fdbae099SBin Meng if (addr == FDT_ADDR_T_NONE) 338fdbae099SBin Meng return -EINVAL; 339fdbae099SBin Meng 340*c40d612bSPeng Fan plat->reg = (void *)addr; 341*c40d612bSPeng Fan plat->flags = dev_get_driver_data(dev); 342fdbae099SBin Meng 343fdbae099SBin Meng return 0; 344fdbae099SBin Meng } 345fdbae099SBin Meng 346fdbae099SBin Meng static const struct dm_serial_ops lpuart_serial_ops = { 347fdbae099SBin Meng .putc = lpuart_serial_putc, 348fdbae099SBin Meng .pending = lpuart_serial_pending, 349fdbae099SBin Meng .getc = lpuart_serial_getc, 350fdbae099SBin Meng .setbrg = lpuart_serial_setbrg, 351fdbae099SBin Meng }; 352fdbae099SBin Meng 353fdbae099SBin Meng static const struct udevice_id lpuart_serial_ids[] = { 354*c40d612bSPeng Fan { .compatible = "fsl,ls1021a-lpuart", .data = 355*c40d612bSPeng Fan LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG }, 356fdbae099SBin Meng { .compatible = "fsl,vf610-lpuart"}, 357fdbae099SBin Meng { } 358fdbae099SBin Meng }; 359fdbae099SBin Meng 360fdbae099SBin Meng U_BOOT_DRIVER(serial_lpuart) = { 361fdbae099SBin Meng .name = "serial_lpuart", 362fdbae099SBin Meng .id = UCLASS_SERIAL, 363fdbae099SBin Meng .of_match = lpuart_serial_ids, 364fdbae099SBin Meng .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata, 365fdbae099SBin Meng .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata), 366fdbae099SBin Meng .probe = lpuart_serial_probe, 367fdbae099SBin Meng .ops = &lpuart_serial_ops, 368fdbae099SBin Meng .flags = DM_FLAG_PRE_RELOC, 369fdbae099SBin Meng }; 370