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