11378df79SJean-Christophe PLAGNIOL-VILLARD /* 21378df79SJean-Christophe PLAGNIOL-VILLARD * COM1 NS16550 support 3a47a12beSStefan Roese * originally from linux source (arch/powerpc/boot/ns16550.c) 46d0f6bcfSJean-Christophe PLAGNIOL-VILLARD * modified to use CONFIG_SYS_ISA_MEM and new defines 51378df79SJean-Christophe PLAGNIOL-VILLARD */ 61378df79SJean-Christophe PLAGNIOL-VILLARD 7fa54eb12SSimon Glass #include <common.h> 850fce1d5SPaul Burton #include <clk.h> 912e431b2SSimon Glass #include <dm.h> 1012e431b2SSimon Glass #include <errno.h> 111378df79SJean-Christophe PLAGNIOL-VILLARD #include <ns16550.h> 1212e431b2SSimon Glass #include <serial.h> 13a1b322a9SLadislav Michl #include <watchdog.h> 14167cdad1SGraeme Russ #include <linux/types.h> 15167cdad1SGraeme Russ #include <asm/io.h> 161378df79SJean-Christophe PLAGNIOL-VILLARD 1712e431b2SSimon Glass DECLARE_GLOBAL_DATA_PTR; 1812e431b2SSimon Glass 19200779e3SDetlev Zundel #define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */ 20200779e3SDetlev Zundel #define UART_MCRVAL (UART_MCR_DTR | \ 21200779e3SDetlev Zundel UART_MCR_RTS) /* RTS/DTR */ 2212e431b2SSimon Glass 2312e431b2SSimon Glass #ifndef CONFIG_DM_SERIAL 24167cdad1SGraeme Russ #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 25167cdad1SGraeme Russ #define serial_out(x, y) outb(x, (ulong)y) 26167cdad1SGraeme Russ #define serial_in(y) inb((ulong)y) 2779df1208SDave Aldridge #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE > 0) 2879df1208SDave Aldridge #define serial_out(x, y) out_be32(y, x) 2979df1208SDave Aldridge #define serial_in(y) in_be32(y) 3079df1208SDave Aldridge #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE < 0) 3179df1208SDave Aldridge #define serial_out(x, y) out_le32(y, x) 3279df1208SDave Aldridge #define serial_in(y) in_le32(y) 33167cdad1SGraeme Russ #else 34167cdad1SGraeme Russ #define serial_out(x, y) writeb(x, y) 35167cdad1SGraeme Russ #define serial_in(y) readb(y) 36167cdad1SGraeme Russ #endif 3712e431b2SSimon Glass #endif /* !CONFIG_DM_SERIAL */ 381378df79SJean-Christophe PLAGNIOL-VILLARD 397c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 40ef509b90SVitaly Andrianov #define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 41ef509b90SVitaly Andrianov #define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0)) 42d57dee57SKaricheri, Muralidharan #undef UART_MCRVAL 43d57dee57SKaricheri, Muralidharan #ifdef CONFIG_SERIAL_HW_FLOW_CONTROL 44d57dee57SKaricheri, Muralidharan #define UART_MCRVAL (UART_MCR_RTS | UART_MCR_AFE) 45d57dee57SKaricheri, Muralidharan #else 46d57dee57SKaricheri, Muralidharan #define UART_MCRVAL (UART_MCR_RTS) 47d57dee57SKaricheri, Muralidharan #endif 48ef509b90SVitaly Andrianov #endif 49ef509b90SVitaly Andrianov 50a160ea0bSPrafulla Wadaskar #ifndef CONFIG_SYS_NS16550_IER 51a160ea0bSPrafulla Wadaskar #define CONFIG_SYS_NS16550_IER 0x00 52a160ea0bSPrafulla Wadaskar #endif /* CONFIG_SYS_NS16550_IER */ 53a160ea0bSPrafulla Wadaskar 54363e6da1SSimon Glass static inline void serial_out_shift(void *addr, int shift, int value) 5576571674SSimon Glass { 5676571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 5776571674SSimon Glass outb(value, (ulong)addr); 5876571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 5976571674SSimon Glass out_le32(addr, value); 6076571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 6176571674SSimon Glass out_be32(addr, value); 6290914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 6390914008SSimon Glass writel(value, addr); 6476571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 6576571674SSimon Glass writeb(value, addr + (1 << shift) - 1); 6676571674SSimon Glass #else 6776571674SSimon Glass writeb(value, addr); 6876571674SSimon Glass #endif 6976571674SSimon Glass } 7076571674SSimon Glass 71363e6da1SSimon Glass static inline int serial_in_shift(void *addr, int shift) 7276571674SSimon Glass { 7376571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 7476571674SSimon Glass return inb((ulong)addr); 7576571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 7676571674SSimon Glass return in_le32(addr); 7776571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 7876571674SSimon Glass return in_be32(addr); 7990914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 8090914008SSimon Glass return readl(addr); 8176571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 8220379c11SAxel Lin return readb(addr + (1 << shift) - 1); 8376571674SSimon Glass #else 8476571674SSimon Glass return readb(addr); 8576571674SSimon Glass #endif 8676571674SSimon Glass } 8776571674SSimon Glass 88fa4ce723SMarek Vasut #ifdef CONFIG_DM_SERIAL 89fa4ce723SMarek Vasut 90fa4ce723SMarek Vasut #ifndef CONFIG_SYS_NS16550_CLK 918c22eae6SJoseph Chen #define CONFIG_SYS_NS16550_CLK 0 92fa4ce723SMarek Vasut #endif 93fa4ce723SMarek Vasut 9412e431b2SSimon Glass static void ns16550_writeb(NS16550_t port, int offset, int value) 9512e431b2SSimon Glass { 9612e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 9712e431b2SSimon Glass unsigned char *addr; 9812e431b2SSimon Glass 9912e431b2SSimon Glass offset *= 1 << plat->reg_shift; 100df8ec55dSPaul Burton addr = (unsigned char *)plat->base + offset; 101df8ec55dSPaul Burton 10212e431b2SSimon Glass /* 10312e431b2SSimon Glass * As far as we know it doesn't make sense to support selection of 10412e431b2SSimon Glass * these options at run-time, so use the existing CONFIG options. 10512e431b2SSimon Glass */ 10659b35dddSMichal Simek serial_out_shift(addr + plat->reg_offset, plat->reg_shift, value); 10712e431b2SSimon Glass } 10812e431b2SSimon Glass 10912e431b2SSimon Glass static int ns16550_readb(NS16550_t port, int offset) 11012e431b2SSimon Glass { 11112e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 11212e431b2SSimon Glass unsigned char *addr; 11312e431b2SSimon Glass 11412e431b2SSimon Glass offset *= 1 << plat->reg_shift; 115df8ec55dSPaul Burton addr = (unsigned char *)plat->base + offset; 11676571674SSimon Glass 11759b35dddSMichal Simek return serial_in_shift(addr + plat->reg_offset, plat->reg_shift); 11812e431b2SSimon Glass } 11912e431b2SSimon Glass 12065f83802SMarek Vasut static u32 ns16550_getfcr(NS16550_t port) 12165f83802SMarek Vasut { 12265f83802SMarek Vasut struct ns16550_platdata *plat = port->plat; 12365f83802SMarek Vasut 12465f83802SMarek Vasut return plat->fcr; 12565f83802SMarek Vasut } 12665f83802SMarek Vasut 12712e431b2SSimon Glass /* We can clean these up once everything is moved to driver model */ 12812e431b2SSimon Glass #define serial_out(value, addr) \ 129363e6da1SSimon Glass ns16550_writeb(com_port, \ 130363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port, value) 13112e431b2SSimon Glass #define serial_in(addr) \ 132363e6da1SSimon Glass ns16550_readb(com_port, \ 133363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port) 13465f83802SMarek Vasut #else 13565f83802SMarek Vasut static u32 ns16550_getfcr(NS16550_t port) 13665f83802SMarek Vasut { 13717fa0326SHeiko Schocher return UART_FCR_DEFVAL; 13865f83802SMarek Vasut } 13912e431b2SSimon Glass #endif 14012e431b2SSimon Glass 14103c6f176SMarek Vasut int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) 142fa54eb12SSimon Glass { 143fa54eb12SSimon Glass const unsigned int mode_x_div = 16; 144fa54eb12SSimon Glass 14521d00436SSimon Glass return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); 14621d00436SSimon Glass } 14721d00436SSimon Glass 1488bbe33c8SSimon Glass static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) 1498bbe33c8SSimon Glass { 1508bbe33c8SSimon Glass serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); 1518bbe33c8SSimon Glass serial_out(baud_divisor & 0xff, &com_port->dll); 1528bbe33c8SSimon Glass serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); 1538bbe33c8SSimon Glass serial_out(UART_LCRVAL, &com_port->lcr); 1548bbe33c8SSimon Glass } 1558bbe33c8SSimon Glass 1561378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_init(NS16550_t com_port, int baud_divisor) 1571378df79SJean-Christophe PLAGNIOL-VILLARD { 158956a8baeSGregoire Gentil #if (defined(CONFIG_SPL_BUILD) && \ 159956a8baeSGregoire Gentil (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) 160fd2aeac5SManfred Huber /* 161956a8baeSGregoire Gentil * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode 162956a8baeSGregoire Gentil * before SPL starts only THRE bit is set. We have to empty the 163956a8baeSGregoire Gentil * transmitter before initialization starts. 164fd2aeac5SManfred Huber */ 165fd2aeac5SManfred Huber if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) 166fd2aeac5SManfred Huber == UART_LSR_THRE) { 16712e431b2SSimon Glass if (baud_divisor != -1) 1688bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 169fd2aeac5SManfred Huber serial_out(0, &com_port->mdr1); 170fd2aeac5SManfred Huber } 171fd2aeac5SManfred Huber #endif 172fd2aeac5SManfred Huber 173cb55b332SScott Wood while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT)) 174cb55b332SScott Wood ; 175cb55b332SScott Wood 176a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 17789024ddcSTom Rini #if defined(CONFIG_ARCH_OMAP2PLUS) 178167cdad1SGraeme Russ serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ 1791378df79SJean-Christophe PLAGNIOL-VILLARD #endif 180167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 18165f83802SMarek Vasut serial_out(ns16550_getfcr(com_port), &com_port->fcr); 18212e431b2SSimon Glass if (baud_divisor != -1) 1838bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 18489024ddcSTom Rini #if defined(CONFIG_ARCH_OMAP2PLUS) || defined(CONFIG_SOC_DA8XX) 185f8df9d0dSSimon Glass /* /16 is proper to hit 115200 with 48MHz */ 186f8df9d0dSSimon Glass serial_out(0, &com_port->mdr1); 18789024ddcSTom Rini #endif 1887c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 189ef509b90SVitaly Andrianov serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); 190ef509b90SVitaly Andrianov #endif 1911378df79SJean-Christophe PLAGNIOL-VILLARD } 1921378df79SJean-Christophe PLAGNIOL-VILLARD 193f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 1941378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_reinit(NS16550_t com_port, int baud_divisor) 1951378df79SJean-Christophe PLAGNIOL-VILLARD { 196a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 1978bbe33c8SSimon Glass NS16550_setbrg(com_port, 0); 198167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 19965f83802SMarek Vasut serial_out(ns16550_getfcr(com_port), &com_port->fcr); 2008bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 2011378df79SJean-Christophe PLAGNIOL-VILLARD } 202f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 2031378df79SJean-Christophe PLAGNIOL-VILLARD 2041378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_putc(NS16550_t com_port, char c) 2051378df79SJean-Christophe PLAGNIOL-VILLARD { 206f8df9d0dSSimon Glass while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0) 207f8df9d0dSSimon Glass ; 208167cdad1SGraeme Russ serial_out(c, &com_port->thr); 2091a2d9b30SStefan Roese 2101a2d9b30SStefan Roese /* 2111a2d9b30SStefan Roese * Call watchdog_reset() upon newline. This is done here in putc 2121a2d9b30SStefan Roese * since the environment code uses a single puts() to print the complete 2131a2d9b30SStefan Roese * environment upon "printenv". So we can't put this watchdog call 2141a2d9b30SStefan Roese * in puts(). 2151a2d9b30SStefan Roese */ 2161a2d9b30SStefan Roese if (c == '\n') 2171a2d9b30SStefan Roese WATCHDOG_RESET(); 2181378df79SJean-Christophe PLAGNIOL-VILLARD } 2191378df79SJean-Christophe PLAGNIOL-VILLARD 220f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 2211378df79SJean-Christophe PLAGNIOL-VILLARD char NS16550_getc(NS16550_t com_port) 2221378df79SJean-Christophe PLAGNIOL-VILLARD { 223167cdad1SGraeme Russ while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { 224f2041388SMarek Vasut #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY) 2251378df79SJean-Christophe PLAGNIOL-VILLARD extern void usbtty_poll(void); 2261378df79SJean-Christophe PLAGNIOL-VILLARD usbtty_poll(); 2271378df79SJean-Christophe PLAGNIOL-VILLARD #endif 228a1b322a9SLadislav Michl WATCHDOG_RESET(); 2291378df79SJean-Christophe PLAGNIOL-VILLARD } 230167cdad1SGraeme Russ return serial_in(&com_port->rbr); 2311378df79SJean-Christophe PLAGNIOL-VILLARD } 2321378df79SJean-Christophe PLAGNIOL-VILLARD 2331378df79SJean-Christophe PLAGNIOL-VILLARD int NS16550_tstc(NS16550_t com_port) 2341378df79SJean-Christophe PLAGNIOL-VILLARD { 235f8df9d0dSSimon Glass return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0; 2361378df79SJean-Christophe PLAGNIOL-VILLARD } 2371378df79SJean-Christophe PLAGNIOL-VILLARD 238f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 23912e431b2SSimon Glass 24021d00436SSimon Glass #ifdef CONFIG_DEBUG_UART_NS16550 24121d00436SSimon Glass 24221d00436SSimon Glass #include <debug_uart.h> 24321d00436SSimon Glass 24497b05973SSimon Glass static inline void _debug_uart_init(void) 24521d00436SSimon Glass { 24621d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 24721d00436SSimon Glass int baud_divisor; 24821d00436SSimon Glass 249034db995SJoseph Chen if (gd && gd->flags & GD_FLG_DISABLE_CONSOLE) 250034db995SJoseph Chen return; 251034db995SJoseph Chen 252034db995SJoseph Chen if (gd && gd->serial.using_pre_serial) 253034db995SJoseph Chen return; 254034db995SJoseph Chen 25521d00436SSimon Glass /* 25621d00436SSimon Glass * We copy the code from above because it is already horribly messy. 25721d00436SSimon Glass * Trying to refactor to nicely remove the duplication doesn't seem 25821d00436SSimon Glass * feasible. The better fix is to move all users of this driver to 25921d00436SSimon Glass * driver model. 26021d00436SSimon Glass */ 26103c6f176SMarek Vasut baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, 26221d00436SSimon Glass CONFIG_BAUDRATE); 2636e780c7aSSimon Glass serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); 2646e780c7aSSimon Glass serial_dout(&com_port->mcr, UART_MCRVAL); 26517fa0326SHeiko Schocher serial_dout(&com_port->fcr, UART_FCR_DEFVAL); 26621d00436SSimon Glass 2676e780c7aSSimon Glass serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); 2686e780c7aSSimon Glass serial_dout(&com_port->dll, baud_divisor & 0xff); 2696e780c7aSSimon Glass serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); 2706e780c7aSSimon Glass serial_dout(&com_port->lcr, UART_LCRVAL); 27121d00436SSimon Glass } 27221d00436SSimon Glass 27321d00436SSimon Glass static inline void _debug_uart_putc(int ch) 27421d00436SSimon Glass { 275034db995SJoseph Chen struct NS16550 *com_port; 276034db995SJoseph Chen 277034db995SJoseph Chen if (gd && gd->flags & GD_FLG_DISABLE_CONSOLE) 278034db995SJoseph Chen return; 279034db995SJoseph Chen 280034db995SJoseph Chen if (gd && gd->serial.addr) 281034db995SJoseph Chen com_port = (struct NS16550 *)gd->serial.addr; 282034db995SJoseph Chen else 283034db995SJoseph Chen com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 28421d00436SSimon Glass 2856e780c7aSSimon Glass while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) 28621d00436SSimon Glass ; 2876e780c7aSSimon Glass serial_dout(&com_port->thr, ch); 28821d00436SSimon Glass } 28921d00436SSimon Glass 290034db995SJoseph Chen static inline int _debug_uart_getc(void) 291034db995SJoseph Chen { 292034db995SJoseph Chen struct NS16550 *com_port; 293034db995SJoseph Chen 294034db995SJoseph Chen if (gd && gd->flags & GD_FLG_DISABLE_CONSOLE) 295034db995SJoseph Chen return 0; 296034db995SJoseph Chen 297034db995SJoseph Chen if (gd && gd->serial.addr) 298034db995SJoseph Chen com_port = (struct NS16550 *)gd->serial.addr; 299034db995SJoseph Chen else 300034db995SJoseph Chen com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 301034db995SJoseph Chen 302034db995SJoseph Chen while (!(serial_din(&com_port->lsr) & UART_LSR_DR)) 303034db995SJoseph Chen ; 304034db995SJoseph Chen 305034db995SJoseph Chen return serial_din(&com_port->rbr); 306034db995SJoseph Chen } 307034db995SJoseph Chen 308034db995SJoseph Chen static inline int _debug_uart_tstc(int input) 309034db995SJoseph Chen { 310034db995SJoseph Chen struct NS16550 *com_port; 311034db995SJoseph Chen 312034db995SJoseph Chen if (gd && gd->flags & GD_FLG_DISABLE_CONSOLE) 313034db995SJoseph Chen return 0; 314034db995SJoseph Chen 315034db995SJoseph Chen if (gd && gd->serial.addr) 316034db995SJoseph Chen com_port = (struct NS16550 *)gd->serial.addr; 317034db995SJoseph Chen else 318034db995SJoseph Chen com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 319034db995SJoseph Chen 320034db995SJoseph Chen if (input) 321034db995SJoseph Chen return serial_din(&com_port->lsr) & UART_LSR_DR ? 1 : 0; 322034db995SJoseph Chen else 323034db995SJoseph Chen return serial_din(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; 324034db995SJoseph Chen } 325034db995SJoseph Chen 326034db995SJoseph Chen static inline int _debug_uart_clrc(void) 327034db995SJoseph Chen { 328034db995SJoseph Chen struct NS16550 *com_port; 329034db995SJoseph Chen 330034db995SJoseph Chen if (gd && gd->flags & GD_FLG_DISABLE_CONSOLE) 331034db995SJoseph Chen return 0; 332034db995SJoseph Chen 333034db995SJoseph Chen if (gd && gd->serial.addr) 334034db995SJoseph Chen com_port = (struct NS16550 *)gd->serial.addr; 335034db995SJoseph Chen else 336034db995SJoseph Chen com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 337034db995SJoseph Chen 338034db995SJoseph Chen /* 339034db995SJoseph Chen * Wait fifo flush. 340034db995SJoseph Chen * 341034db995SJoseph Chen * UART_USR: bit2 trans_fifo_empty: 342034db995SJoseph Chen * 0 = Transmit FIFO is not empty 343034db995SJoseph Chen * 1 = Transmit FIFO is empty 344034db995SJoseph Chen */ 345034db995SJoseph Chen while (!(serial_din(&com_port->rbr + 0x1f) & 0x04)) 346034db995SJoseph Chen ; 347034db995SJoseph Chen 348034db995SJoseph Chen return 0; 349034db995SJoseph Chen } 350034db995SJoseph Chen 351034db995SJoseph Chen /* should use gd->baudrate, it can be updated by env callback: on_baudrate() */ 352034db995SJoseph Chen static inline int _debug_uart_setbrg(void) 353034db995SJoseph Chen { 354034db995SJoseph Chen struct NS16550 *com_port; 355034db995SJoseph Chen int baud_divisor; 356034db995SJoseph Chen 357034db995SJoseph Chen if (gd && gd->flags & GD_FLG_DISABLE_CONSOLE) 358034db995SJoseph Chen return 0; 359034db995SJoseph Chen 360034db995SJoseph Chen if (gd && gd->serial.addr) { 361034db995SJoseph Chen com_port = (struct NS16550 *)gd->serial.addr; 362034db995SJoseph Chen baud_divisor = ns16550_calc_divisor(com_port, 363034db995SJoseph Chen CONFIG_DEBUG_UART_CLOCK, gd->baudrate); 364034db995SJoseph Chen } else { 365034db995SJoseph Chen com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 366034db995SJoseph Chen baud_divisor = ns16550_calc_divisor(com_port, 367034db995SJoseph Chen CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); 368034db995SJoseph Chen } 369034db995SJoseph Chen 370034db995SJoseph Chen serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); 371034db995SJoseph Chen serial_dout(&com_port->dll, baud_divisor & 0xff); 372034db995SJoseph Chen serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); 373034db995SJoseph Chen serial_dout(&com_port->lcr, UART_LCRVAL); 374034db995SJoseph Chen 375034db995SJoseph Chen return 0; 376034db995SJoseph Chen } 377034db995SJoseph Chen 37821d00436SSimon Glass DEBUG_UART_FUNCS 37921d00436SSimon Glass 38021d00436SSimon Glass #endif 38121d00436SSimon Glass 382a52cf086SLokesh Vutla #ifdef CONFIG_DEBUG_UART_OMAP 383a52cf086SLokesh Vutla 384a52cf086SLokesh Vutla #include <debug_uart.h> 385a52cf086SLokesh Vutla 386a52cf086SLokesh Vutla static inline void _debug_uart_init(void) 387a52cf086SLokesh Vutla { 388a52cf086SLokesh Vutla struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 389a52cf086SLokesh Vutla int baud_divisor; 390a52cf086SLokesh Vutla 391a52cf086SLokesh Vutla baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, 392a52cf086SLokesh Vutla CONFIG_BAUDRATE); 393a52cf086SLokesh Vutla serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); 394a52cf086SLokesh Vutla serial_dout(&com_port->mdr1, 0x7); 395a52cf086SLokesh Vutla serial_dout(&com_port->mcr, UART_MCRVAL); 396a52cf086SLokesh Vutla serial_dout(&com_port->fcr, UART_FCR_DEFVAL); 397a52cf086SLokesh Vutla 398a52cf086SLokesh Vutla serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); 399a52cf086SLokesh Vutla serial_dout(&com_port->dll, baud_divisor & 0xff); 400a52cf086SLokesh Vutla serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); 401a52cf086SLokesh Vutla serial_dout(&com_port->lcr, UART_LCRVAL); 402a52cf086SLokesh Vutla serial_dout(&com_port->mdr1, 0x0); 403a52cf086SLokesh Vutla } 404a52cf086SLokesh Vutla 405a52cf086SLokesh Vutla static inline void _debug_uart_putc(int ch) 406a52cf086SLokesh Vutla { 407a52cf086SLokesh Vutla struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 408a52cf086SLokesh Vutla 409a52cf086SLokesh Vutla while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) 410a52cf086SLokesh Vutla ; 411a52cf086SLokesh Vutla serial_dout(&com_port->thr, ch); 412a52cf086SLokesh Vutla } 413a52cf086SLokesh Vutla 414a52cf086SLokesh Vutla DEBUG_UART_FUNCS 415a52cf086SLokesh Vutla 416a52cf086SLokesh Vutla #endif 417a52cf086SLokesh Vutla 41812e431b2SSimon Glass #ifdef CONFIG_DM_SERIAL 41912e431b2SSimon Glass static int ns16550_serial_putc(struct udevice *dev, const char ch) 42012e431b2SSimon Glass { 42112e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 42212e431b2SSimon Glass 423034db995SJoseph Chen /* 424034db995SJoseph Chen * Use fifo function. 425034db995SJoseph Chen * 426034db995SJoseph Chen * UART_USR: bit1 trans_fifo_not_full: 427034db995SJoseph Chen * 0 = Transmit FIFO is full; 428034db995SJoseph Chen * 1 = Transmit FIFO is not full; 429034db995SJoseph Chen */ 430034db995SJoseph Chen while (!(serial_in(&com_port->rbr + 0x1f) & 0x02)) 431034db995SJoseph Chen ; 43212e431b2SSimon Glass serial_out(ch, &com_port->thr); 43312e431b2SSimon Glass 43412e431b2SSimon Glass /* 43512e431b2SSimon Glass * Call watchdog_reset() upon newline. This is done here in putc 43612e431b2SSimon Glass * since the environment code uses a single puts() to print the complete 43712e431b2SSimon Glass * environment upon "printenv". So we can't put this watchdog call 43812e431b2SSimon Glass * in puts(). 43912e431b2SSimon Glass */ 44012e431b2SSimon Glass if (ch == '\n') 44112e431b2SSimon Glass WATCHDOG_RESET(); 44212e431b2SSimon Glass 44312e431b2SSimon Glass return 0; 44412e431b2SSimon Glass } 44512e431b2SSimon Glass 44612e431b2SSimon Glass static int ns16550_serial_pending(struct udevice *dev, bool input) 44712e431b2SSimon Glass { 44812e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 44912e431b2SSimon Glass 45012e431b2SSimon Glass if (input) 4517fded0ceSStefan Roese return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0; 45212e431b2SSimon Glass else 45312e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; 45412e431b2SSimon Glass } 45512e431b2SSimon Glass 45612e431b2SSimon Glass static int ns16550_serial_getc(struct udevice *dev) 45712e431b2SSimon Glass { 4587fded0ceSStefan Roese struct NS16550 *const com_port = dev_get_priv(dev); 4597fded0ceSStefan Roese 4607fded0ceSStefan Roese if (!(serial_in(&com_port->lsr) & UART_LSR_DR)) 46112e431b2SSimon Glass return -EAGAIN; 46212e431b2SSimon Glass 4637fded0ceSStefan Roese return serial_in(&com_port->rbr); 46412e431b2SSimon Glass } 46512e431b2SSimon Glass 46612e431b2SSimon Glass static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) 46712e431b2SSimon Glass { 46812e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 46912e431b2SSimon Glass struct ns16550_platdata *plat = com_port->plat; 47012e431b2SSimon Glass int clock_divisor; 47112e431b2SSimon Glass 47212e431b2SSimon Glass clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); 47312e431b2SSimon Glass 47412e431b2SSimon Glass NS16550_setbrg(com_port, clock_divisor); 47512e431b2SSimon Glass 47612e431b2SSimon Glass return 0; 47712e431b2SSimon Glass } 47812e431b2SSimon Glass 479034db995SJoseph Chen static int ns16550_serial_clear(struct udevice *dev) 480034db995SJoseph Chen { 481034db995SJoseph Chen struct NS16550 *const com_port = dev_get_priv(dev); 482034db995SJoseph Chen 483034db995SJoseph Chen /* 484034db995SJoseph Chen * Wait fifo flush. 485034db995SJoseph Chen * 486034db995SJoseph Chen * UART_USR: bit2 trans_fifo_empty: 487034db995SJoseph Chen * 0 = Transmit FIFO is not empty 488034db995SJoseph Chen * 1 = Transmit FIFO is empty 489034db995SJoseph Chen */ 490034db995SJoseph Chen while (!(serial_in(&com_port->rbr + 0x1f) & 0x04)) 491034db995SJoseph Chen ; 492034db995SJoseph Chen 493034db995SJoseph Chen return 0; 494034db995SJoseph Chen } 495034db995SJoseph Chen 49612e431b2SSimon Glass int ns16550_serial_probe(struct udevice *dev) 49712e431b2SSimon Glass { 49812e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 49912e431b2SSimon Glass 50011c1a878SSimon Glass com_port->plat = dev_get_platdata(dev); 50112e431b2SSimon Glass NS16550_init(com_port, -1); 50212e431b2SSimon Glass 50312e431b2SSimon Glass return 0; 50412e431b2SSimon Glass } 50512e431b2SSimon Glass 50679fd9281SMarek Vasut #if CONFIG_IS_ENABLED(OF_CONTROL) 50779fd9281SMarek Vasut enum { 50879fd9281SMarek Vasut PORT_NS16550 = 0, 5090b060eefSMarek Vasut PORT_JZ4780, 51079fd9281SMarek Vasut }; 51179fd9281SMarek Vasut #endif 51279fd9281SMarek Vasut 513b2927fbaSSimon Glass #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) 51412e431b2SSimon Glass int ns16550_serial_ofdata_to_platdata(struct udevice *dev) 51512e431b2SSimon Glass { 51612e431b2SSimon Glass struct ns16550_platdata *plat = dev->platdata; 5170b060eefSMarek Vasut const u32 port_type = dev_get_driver_data(dev); 51812e431b2SSimon Glass fdt_addr_t addr; 519021abf69SMasahiro Yamada struct clk clk; 520021abf69SMasahiro Yamada int err; 52112e431b2SSimon Glass 5223db886a5SBin Meng /* try Processor Local Bus device first */ 523db9f8f6aSSimon Glass addr = dev_read_addr(dev); 524*00f93bdfSJoseph Chen #if defined(CONFIG_PCI) && CONFIG_IS_ENABLED(DM_PCI) 5253db886a5SBin Meng if (addr == FDT_ADDR_T_NONE) { 5263db886a5SBin Meng /* then try pci device */ 5273db886a5SBin Meng struct fdt_pci_addr pci_addr; 5283db886a5SBin Meng u32 bar; 5293db886a5SBin Meng int ret; 5303db886a5SBin Meng 5313db886a5SBin Meng /* we prefer to use a memory-mapped register */ 532e160f7d4SSimon Glass ret = fdtdec_get_pci_addr(gd->fdt_blob, dev_of_offset(dev), 5333db886a5SBin Meng FDT_PCI_SPACE_MEM32, "reg", 5343db886a5SBin Meng &pci_addr); 5353db886a5SBin Meng if (ret) { 5363db886a5SBin Meng /* try if there is any i/o-mapped register */ 5373db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, 538e160f7d4SSimon Glass dev_of_offset(dev), 5393db886a5SBin Meng FDT_PCI_SPACE_IO, 5403db886a5SBin Meng "reg", &pci_addr); 5413db886a5SBin Meng if (ret) 5423db886a5SBin Meng return ret; 5433db886a5SBin Meng } 5443db886a5SBin Meng 545fcc0a877SSimon Glass ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar); 5463db886a5SBin Meng if (ret) 5473db886a5SBin Meng return ret; 5483db886a5SBin Meng 5493db886a5SBin Meng addr = bar; 5503db886a5SBin Meng } 5513db886a5SBin Meng #endif 5523db886a5SBin Meng 55312e431b2SSimon Glass if (addr == FDT_ADDR_T_NONE) 55412e431b2SSimon Glass return -EINVAL; 55512e431b2SSimon Glass 556df8ec55dSPaul Burton #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 557167efe01SSimon Glass plat->base = addr; 558df8ec55dSPaul Burton #else 559df8ec55dSPaul Burton plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE); 560df8ec55dSPaul Burton #endif 561df8ec55dSPaul Burton 5623d40479fSPhilipp Tomsich plat->reg_offset = dev_read_u32_default(dev, "reg-offset", 0); 5633d40479fSPhilipp Tomsich plat->reg_shift = dev_read_u32_default(dev, "reg-shift", 0); 56450fce1d5SPaul Burton 56550fce1d5SPaul Burton err = clk_get_by_index(dev, 0, &clk); 56650fce1d5SPaul Burton if (!err) { 56750fce1d5SPaul Burton err = clk_get_rate(&clk); 56850fce1d5SPaul Burton if (!IS_ERR_VALUE(err)) 56950fce1d5SPaul Burton plat->clock = err; 570ab895d6aSAlexandre Courbot } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) { 571034db995SJoseph Chen printf("ns16550 failed to get clock, err=%d\n", err); 57250fce1d5SPaul Burton return err; 57350fce1d5SPaul Burton } 57450fce1d5SPaul Burton 57550fce1d5SPaul Burton if (!plat->clock) 5763d40479fSPhilipp Tomsich plat->clock = dev_read_u32_default(dev, "clock-frequency", 5778e62d32eSThomas Chou CONFIG_SYS_NS16550_CLK); 5788e62d32eSThomas Chou if (!plat->clock) { 5798e62d32eSThomas Chou debug("ns16550 clock not defined\n"); 5808e62d32eSThomas Chou return -EINVAL; 5818e62d32eSThomas Chou } 58212e431b2SSimon Glass 58317fa0326SHeiko Schocher plat->fcr = UART_FCR_DEFVAL; 5840b060eefSMarek Vasut if (port_type == PORT_JZ4780) 5850b060eefSMarek Vasut plat->fcr |= UART_FCR_UME; 58665f83802SMarek Vasut 58712e431b2SSimon Glass return 0; 58812e431b2SSimon Glass } 58911c1a878SSimon Glass #endif 59012e431b2SSimon Glass 59112e431b2SSimon Glass const struct dm_serial_ops ns16550_serial_ops = { 59212e431b2SSimon Glass .putc = ns16550_serial_putc, 59312e431b2SSimon Glass .pending = ns16550_serial_pending, 59412e431b2SSimon Glass .getc = ns16550_serial_getc, 59512e431b2SSimon Glass .setbrg = ns16550_serial_setbrg, 596034db995SJoseph Chen .clear = ns16550_serial_clear, 59712e431b2SSimon Glass }; 5988e62d32eSThomas Chou 599034db995SJoseph Chen #if CONFIG_IS_ENABLED(SERIAL_PRESENT) 6006f8c351eSAlexandru Gagniuc #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) 601cc4228f9SThomas Chou /* 602cc4228f9SThomas Chou * Please consider existing compatible strings before adding a new 603cc4228f9SThomas Chou * one to keep this table compact. Or you may add a generic "ns16550" 604cc4228f9SThomas Chou * compatible string to your dts. 605cc4228f9SThomas Chou */ 6068e62d32eSThomas Chou static const struct udevice_id ns16550_serial_ids[] = { 60779fd9281SMarek Vasut { .compatible = "ns16550", .data = PORT_NS16550 }, 60879fd9281SMarek Vasut { .compatible = "ns16550a", .data = PORT_NS16550 }, 6090b060eefSMarek Vasut { .compatible = "ingenic,jz4780-uart", .data = PORT_JZ4780 }, 61079fd9281SMarek Vasut { .compatible = "nvidia,tegra20-uart", .data = PORT_NS16550 }, 61179fd9281SMarek Vasut { .compatible = "snps,dw-apb-uart", .data = PORT_NS16550 }, 61279fd9281SMarek Vasut { .compatible = "ti,omap2-uart", .data = PORT_NS16550 }, 61379fd9281SMarek Vasut { .compatible = "ti,omap3-uart", .data = PORT_NS16550 }, 61479fd9281SMarek Vasut { .compatible = "ti,omap4-uart", .data = PORT_NS16550 }, 61579fd9281SMarek Vasut { .compatible = "ti,am3352-uart", .data = PORT_NS16550 }, 61679fd9281SMarek Vasut { .compatible = "ti,am4372-uart", .data = PORT_NS16550 }, 61779fd9281SMarek Vasut { .compatible = "ti,dra742-uart", .data = PORT_NS16550 }, 6188e62d32eSThomas Chou {} 6198e62d32eSThomas Chou }; 6206f8c351eSAlexandru Gagniuc #endif /* OF_CONTROL && !OF_PLATDATA */ 6218e62d32eSThomas Chou 6226f8c351eSAlexandru Gagniuc /* TODO(sjg@chromium.org): Integrate this into a macro like CONFIG_IS_ENABLED */ 6236f8c351eSAlexandru Gagniuc #if !defined(CONFIG_TPL_BUILD) || defined(CONFIG_TPL_DM_SERIAL) 6248e62d32eSThomas Chou U_BOOT_DRIVER(ns16550_serial) = { 6258e62d32eSThomas Chou .name = "ns16550_serial", 6268e62d32eSThomas Chou .id = UCLASS_SERIAL, 6276f8c351eSAlexandru Gagniuc #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) 6288e62d32eSThomas Chou .of_match = ns16550_serial_ids, 6298e62d32eSThomas Chou .ofdata_to_platdata = ns16550_serial_ofdata_to_platdata, 6308e62d32eSThomas Chou .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), 6318e62d32eSThomas Chou #endif 6328e62d32eSThomas Chou .priv_auto_alloc_size = sizeof(struct NS16550), 6338e62d32eSThomas Chou .probe = ns16550_serial_probe, 6348e62d32eSThomas Chou .ops = &ns16550_serial_ops, 635b7e5a643SSimon Glass .flags = DM_FLAG_PRE_RELOC, 6368e62d32eSThomas Chou }; 637b7e29834SSimon Glass #endif 6386f8c351eSAlexandru Gagniuc #endif /* SERIAL_PRESENT */ 6396f8c351eSAlexandru Gagniuc 64012e431b2SSimon Glass #endif /* CONFIG_DM_SERIAL */ 641