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> 812e431b2SSimon Glass #include <dm.h> 912e431b2SSimon Glass #include <errno.h> 1012e431b2SSimon Glass #include <fdtdec.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 */ 22200779e3SDetlev Zundel #define UART_FCRVAL (UART_FCR_FIFO_EN | \ 23200779e3SDetlev Zundel UART_FCR_RXSR | \ 24200779e3SDetlev Zundel UART_FCR_TXSR) /* Clear & enable FIFOs */ 2512e431b2SSimon Glass 2612e431b2SSimon Glass #ifndef CONFIG_DM_SERIAL 27167cdad1SGraeme Russ #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 28167cdad1SGraeme Russ #define serial_out(x, y) outb(x, (ulong)y) 29167cdad1SGraeme Russ #define serial_in(y) inb((ulong)y) 3079df1208SDave Aldridge #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE > 0) 3179df1208SDave Aldridge #define serial_out(x, y) out_be32(y, x) 3279df1208SDave Aldridge #define serial_in(y) in_be32(y) 3379df1208SDave Aldridge #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE < 0) 3479df1208SDave Aldridge #define serial_out(x, y) out_le32(y, x) 3579df1208SDave Aldridge #define serial_in(y) in_le32(y) 36167cdad1SGraeme Russ #else 37167cdad1SGraeme Russ #define serial_out(x, y) writeb(x, y) 38167cdad1SGraeme Russ #define serial_in(y) readb(y) 39167cdad1SGraeme Russ #endif 4012e431b2SSimon Glass #endif /* !CONFIG_DM_SERIAL */ 411378df79SJean-Christophe PLAGNIOL-VILLARD 427c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 43ef509b90SVitaly Andrianov #define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 44ef509b90SVitaly Andrianov #define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0)) 45d57dee57SKaricheri, Muralidharan #undef UART_MCRVAL 46d57dee57SKaricheri, Muralidharan #ifdef CONFIG_SERIAL_HW_FLOW_CONTROL 47d57dee57SKaricheri, Muralidharan #define UART_MCRVAL (UART_MCR_RTS | UART_MCR_AFE) 48d57dee57SKaricheri, Muralidharan #else 49d57dee57SKaricheri, Muralidharan #define UART_MCRVAL (UART_MCR_RTS) 50d57dee57SKaricheri, Muralidharan #endif 51ef509b90SVitaly Andrianov #endif 52ef509b90SVitaly Andrianov 53a160ea0bSPrafulla Wadaskar #ifndef CONFIG_SYS_NS16550_IER 54a160ea0bSPrafulla Wadaskar #define CONFIG_SYS_NS16550_IER 0x00 55a160ea0bSPrafulla Wadaskar #endif /* CONFIG_SYS_NS16550_IER */ 56a160ea0bSPrafulla Wadaskar 5712e431b2SSimon Glass #ifdef CONFIG_DM_SERIAL 5876571674SSimon Glass 598e62d32eSThomas Chou #ifndef CONFIG_SYS_NS16550_CLK 608e62d32eSThomas Chou #define CONFIG_SYS_NS16550_CLK 0 618e62d32eSThomas Chou #endif 628e62d32eSThomas Chou 63363e6da1SSimon Glass static inline void serial_out_shift(void *addr, int shift, int value) 6476571674SSimon Glass { 6576571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 6676571674SSimon Glass outb(value, (ulong)addr); 6776571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 6876571674SSimon Glass out_le32(addr, value); 6976571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 7076571674SSimon Glass out_be32(addr, value); 7190914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 7290914008SSimon Glass writel(value, addr); 7376571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 7476571674SSimon Glass writeb(value, addr + (1 << shift) - 1); 7576571674SSimon Glass #else 7676571674SSimon Glass writeb(value, addr); 7776571674SSimon Glass #endif 7876571674SSimon Glass } 7976571674SSimon Glass 80363e6da1SSimon Glass static inline int serial_in_shift(void *addr, int shift) 8176571674SSimon Glass { 8276571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 8376571674SSimon Glass return inb((ulong)addr); 8476571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 8576571674SSimon Glass return in_le32(addr); 8676571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 8776571674SSimon Glass return in_be32(addr); 8890914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 8990914008SSimon Glass return readl(addr); 9076571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 9120379c11SAxel Lin return readb(addr + (1 << shift) - 1); 9276571674SSimon Glass #else 9376571674SSimon Glass return readb(addr); 9476571674SSimon Glass #endif 9576571674SSimon Glass } 9676571674SSimon Glass 9712e431b2SSimon Glass static void ns16550_writeb(NS16550_t port, int offset, int value) 9812e431b2SSimon Glass { 9912e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 10012e431b2SSimon Glass unsigned char *addr; 10112e431b2SSimon Glass 10212e431b2SSimon Glass offset *= 1 << plat->reg_shift; 10377d7b5cdSThomas Chou addr = map_physmem(plat->base, 0, MAP_NOCACHE) + offset; 10412e431b2SSimon Glass /* 10512e431b2SSimon Glass * As far as we know it doesn't make sense to support selection of 10612e431b2SSimon Glass * these options at run-time, so use the existing CONFIG options. 10712e431b2SSimon Glass */ 10876571674SSimon Glass serial_out_shift(addr, plat->reg_shift, value); 10912e431b2SSimon Glass } 11012e431b2SSimon Glass 11112e431b2SSimon Glass static int ns16550_readb(NS16550_t port, int offset) 11212e431b2SSimon Glass { 11312e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 11412e431b2SSimon Glass unsigned char *addr; 11512e431b2SSimon Glass 11612e431b2SSimon Glass offset *= 1 << plat->reg_shift; 11777d7b5cdSThomas Chou addr = map_physmem(plat->base, 0, MAP_NOCACHE) + offset; 11876571674SSimon Glass 11976571674SSimon Glass return serial_in_shift(addr, plat->reg_shift); 12012e431b2SSimon Glass } 12112e431b2SSimon Glass 12212e431b2SSimon Glass /* We can clean these up once everything is moved to driver model */ 12312e431b2SSimon Glass #define serial_out(value, addr) \ 124363e6da1SSimon Glass ns16550_writeb(com_port, \ 125363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port, value) 12612e431b2SSimon Glass #define serial_in(addr) \ 127363e6da1SSimon Glass ns16550_readb(com_port, \ 128363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port) 12912e431b2SSimon Glass #endif 13012e431b2SSimon Glass 13121d00436SSimon Glass static inline int calc_divisor(NS16550_t port, int clock, int baudrate) 132fa54eb12SSimon Glass { 133fa54eb12SSimon Glass const unsigned int mode_x_div = 16; 134fa54eb12SSimon Glass 13521d00436SSimon Glass return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); 13621d00436SSimon Glass } 13721d00436SSimon Glass 13821d00436SSimon Glass int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) 13921d00436SSimon Glass { 140fa54eb12SSimon Glass #ifdef CONFIG_OMAP1510 141fa54eb12SSimon Glass /* If can't cleanly clock 115200 set div to 1 */ 142fa54eb12SSimon Glass if ((clock == 12000000) && (baudrate == 115200)) { 143fa54eb12SSimon Glass port->osc_12m_sel = OSC_12M_SEL; /* enable 6.5 * divisor */ 144fa54eb12SSimon Glass return 1; /* return 1 for base divisor */ 145fa54eb12SSimon Glass } 146fa54eb12SSimon Glass port->osc_12m_sel = 0; /* clear if previsouly set */ 147fa54eb12SSimon Glass #endif 148fa54eb12SSimon Glass 14921d00436SSimon Glass return calc_divisor(port, clock, baudrate); 150fa54eb12SSimon Glass } 151fa54eb12SSimon Glass 1528bbe33c8SSimon Glass static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) 1538bbe33c8SSimon Glass { 1548bbe33c8SSimon Glass serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); 1558bbe33c8SSimon Glass serial_out(baud_divisor & 0xff, &com_port->dll); 1568bbe33c8SSimon Glass serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); 1578bbe33c8SSimon Glass serial_out(UART_LCRVAL, &com_port->lcr); 1588bbe33c8SSimon Glass } 1598bbe33c8SSimon Glass 1601378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_init(NS16550_t com_port, int baud_divisor) 1611378df79SJean-Christophe PLAGNIOL-VILLARD { 162956a8baeSGregoire Gentil #if (defined(CONFIG_SPL_BUILD) && \ 163956a8baeSGregoire Gentil (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) 164fd2aeac5SManfred Huber /* 165956a8baeSGregoire Gentil * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode 166956a8baeSGregoire Gentil * before SPL starts only THRE bit is set. We have to empty the 167956a8baeSGregoire Gentil * transmitter before initialization starts. 168fd2aeac5SManfred Huber */ 169fd2aeac5SManfred Huber if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) 170fd2aeac5SManfred Huber == UART_LSR_THRE) { 17112e431b2SSimon Glass if (baud_divisor != -1) 1728bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 173fd2aeac5SManfred Huber serial_out(0, &com_port->mdr1); 174fd2aeac5SManfred Huber } 175fd2aeac5SManfred Huber #endif 176fd2aeac5SManfred Huber 177cb55b332SScott Wood while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT)) 178cb55b332SScott Wood ; 179cb55b332SScott Wood 180a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 181456ccfdfSTom Rini #if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \ 182456ccfdfSTom Rini defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 183167cdad1SGraeme Russ serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ 1841378df79SJean-Christophe PLAGNIOL-VILLARD #endif 185167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 186167cdad1SGraeme Russ serial_out(UART_FCRVAL, &com_port->fcr); 18712e431b2SSimon Glass if (baud_divisor != -1) 1888bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 1898ac22a60SMasahiro Yamada #if defined(CONFIG_OMAP) || \ 1906213a68fSMatt Porter defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ 1919ed6e412STENART Antoine defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 1925289e83aSChandan Nath 193f8df9d0dSSimon Glass /* /16 is proper to hit 115200 with 48MHz */ 194f8df9d0dSSimon Glass serial_out(0, &com_port->mdr1); 195b4746d8bSMike Frysinger #endif /* CONFIG_OMAP */ 1967c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 197ef509b90SVitaly Andrianov serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); 198ef509b90SVitaly Andrianov #endif 1991378df79SJean-Christophe PLAGNIOL-VILLARD } 2001378df79SJean-Christophe PLAGNIOL-VILLARD 201f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 2021378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_reinit(NS16550_t com_port, int baud_divisor) 2031378df79SJean-Christophe PLAGNIOL-VILLARD { 204a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 2058bbe33c8SSimon Glass NS16550_setbrg(com_port, 0); 206167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 207167cdad1SGraeme Russ serial_out(UART_FCRVAL, &com_port->fcr); 2088bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 2091378df79SJean-Christophe PLAGNIOL-VILLARD } 210f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 2111378df79SJean-Christophe PLAGNIOL-VILLARD 2121378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_putc(NS16550_t com_port, char c) 2131378df79SJean-Christophe PLAGNIOL-VILLARD { 214f8df9d0dSSimon Glass while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0) 215f8df9d0dSSimon Glass ; 216167cdad1SGraeme Russ serial_out(c, &com_port->thr); 2171a2d9b30SStefan Roese 2181a2d9b30SStefan Roese /* 2191a2d9b30SStefan Roese * Call watchdog_reset() upon newline. This is done here in putc 2201a2d9b30SStefan Roese * since the environment code uses a single puts() to print the complete 2211a2d9b30SStefan Roese * environment upon "printenv". So we can't put this watchdog call 2221a2d9b30SStefan Roese * in puts(). 2231a2d9b30SStefan Roese */ 2241a2d9b30SStefan Roese if (c == '\n') 2251a2d9b30SStefan Roese WATCHDOG_RESET(); 2261378df79SJean-Christophe PLAGNIOL-VILLARD } 2271378df79SJean-Christophe PLAGNIOL-VILLARD 228f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 2291378df79SJean-Christophe PLAGNIOL-VILLARD char NS16550_getc(NS16550_t com_port) 2301378df79SJean-Christophe PLAGNIOL-VILLARD { 231167cdad1SGraeme Russ while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { 232f2041388SMarek Vasut #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY) 2331378df79SJean-Christophe PLAGNIOL-VILLARD extern void usbtty_poll(void); 2341378df79SJean-Christophe PLAGNIOL-VILLARD usbtty_poll(); 2351378df79SJean-Christophe PLAGNIOL-VILLARD #endif 236a1b322a9SLadislav Michl WATCHDOG_RESET(); 2371378df79SJean-Christophe PLAGNIOL-VILLARD } 238167cdad1SGraeme Russ return serial_in(&com_port->rbr); 2391378df79SJean-Christophe PLAGNIOL-VILLARD } 2401378df79SJean-Christophe PLAGNIOL-VILLARD 2411378df79SJean-Christophe PLAGNIOL-VILLARD int NS16550_tstc(NS16550_t com_port) 2421378df79SJean-Christophe PLAGNIOL-VILLARD { 243f8df9d0dSSimon Glass return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0; 2441378df79SJean-Christophe PLAGNIOL-VILLARD } 2451378df79SJean-Christophe PLAGNIOL-VILLARD 246f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 24712e431b2SSimon Glass 24821d00436SSimon Glass #ifdef CONFIG_DEBUG_UART_NS16550 24921d00436SSimon Glass 25021d00436SSimon Glass #include <debug_uart.h> 25121d00436SSimon Glass 2526e780c7aSSimon Glass #define serial_dout(reg, value) \ 2536e780c7aSSimon Glass serial_out_shift((char *)com_port + \ 2546e780c7aSSimon Glass ((char *)reg - (char *)com_port) * \ 2556e780c7aSSimon Glass (1 << CONFIG_DEBUG_UART_SHIFT), \ 2566e780c7aSSimon Glass CONFIG_DEBUG_UART_SHIFT, value) 2576e780c7aSSimon Glass #define serial_din(reg) \ 2586e780c7aSSimon Glass serial_in_shift((char *)com_port + \ 2596e780c7aSSimon Glass ((char *)reg - (char *)com_port) * \ 2606e780c7aSSimon Glass (1 << CONFIG_DEBUG_UART_SHIFT), \ 2616e780c7aSSimon Glass CONFIG_DEBUG_UART_SHIFT) 2626e780c7aSSimon Glass 26397b05973SSimon Glass static inline void _debug_uart_init(void) 26421d00436SSimon Glass { 26521d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 26621d00436SSimon Glass int baud_divisor; 26721d00436SSimon Glass 26821d00436SSimon Glass /* 26921d00436SSimon Glass * We copy the code from above because it is already horribly messy. 27021d00436SSimon Glass * Trying to refactor to nicely remove the duplication doesn't seem 27121d00436SSimon Glass * feasible. The better fix is to move all users of this driver to 27221d00436SSimon Glass * driver model. 27321d00436SSimon Glass */ 27421d00436SSimon Glass baud_divisor = calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, 27521d00436SSimon Glass CONFIG_BAUDRATE); 2766e780c7aSSimon Glass serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); 2776e780c7aSSimon Glass serial_dout(&com_port->mcr, UART_MCRVAL); 2786e780c7aSSimon Glass serial_dout(&com_port->fcr, UART_FCRVAL); 27921d00436SSimon Glass 2806e780c7aSSimon Glass serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); 2816e780c7aSSimon Glass serial_dout(&com_port->dll, baud_divisor & 0xff); 2826e780c7aSSimon Glass serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); 2836e780c7aSSimon Glass serial_dout(&com_port->lcr, UART_LCRVAL); 28421d00436SSimon Glass } 28521d00436SSimon Glass 28621d00436SSimon Glass static inline void _debug_uart_putc(int ch) 28721d00436SSimon Glass { 28821d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 28921d00436SSimon Glass 2906e780c7aSSimon Glass while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) 29121d00436SSimon Glass ; 2926e780c7aSSimon Glass serial_dout(&com_port->thr, ch); 29321d00436SSimon Glass } 29421d00436SSimon Glass 29521d00436SSimon Glass DEBUG_UART_FUNCS 29621d00436SSimon Glass 29721d00436SSimon Glass #endif 29821d00436SSimon Glass 29912e431b2SSimon Glass #ifdef CONFIG_DM_SERIAL 30012e431b2SSimon Glass static int ns16550_serial_putc(struct udevice *dev, const char ch) 30112e431b2SSimon Glass { 30212e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 30312e431b2SSimon Glass 30412e431b2SSimon Glass if (!(serial_in(&com_port->lsr) & UART_LSR_THRE)) 30512e431b2SSimon Glass return -EAGAIN; 30612e431b2SSimon Glass serial_out(ch, &com_port->thr); 30712e431b2SSimon Glass 30812e431b2SSimon Glass /* 30912e431b2SSimon Glass * Call watchdog_reset() upon newline. This is done here in putc 31012e431b2SSimon Glass * since the environment code uses a single puts() to print the complete 31112e431b2SSimon Glass * environment upon "printenv". So we can't put this watchdog call 31212e431b2SSimon Glass * in puts(). 31312e431b2SSimon Glass */ 31412e431b2SSimon Glass if (ch == '\n') 31512e431b2SSimon Glass WATCHDOG_RESET(); 31612e431b2SSimon Glass 31712e431b2SSimon Glass return 0; 31812e431b2SSimon Glass } 31912e431b2SSimon Glass 32012e431b2SSimon Glass static int ns16550_serial_pending(struct udevice *dev, bool input) 32112e431b2SSimon Glass { 32212e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 32312e431b2SSimon Glass 32412e431b2SSimon Glass if (input) 32512e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0; 32612e431b2SSimon Glass else 32712e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; 32812e431b2SSimon Glass } 32912e431b2SSimon Glass 33012e431b2SSimon Glass static int ns16550_serial_getc(struct udevice *dev) 33112e431b2SSimon Glass { 33212e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 33312e431b2SSimon Glass 334aea2be20SSimon Glass if (!(serial_in(&com_port->lsr) & UART_LSR_DR)) 33512e431b2SSimon Glass return -EAGAIN; 33612e431b2SSimon Glass 33712e431b2SSimon Glass return serial_in(&com_port->rbr); 33812e431b2SSimon Glass } 33912e431b2SSimon Glass 34012e431b2SSimon Glass static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) 34112e431b2SSimon Glass { 34212e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 34312e431b2SSimon Glass struct ns16550_platdata *plat = com_port->plat; 34412e431b2SSimon Glass int clock_divisor; 34512e431b2SSimon Glass 34612e431b2SSimon Glass clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); 34712e431b2SSimon Glass 34812e431b2SSimon Glass NS16550_setbrg(com_port, clock_divisor); 34912e431b2SSimon Glass 35012e431b2SSimon Glass return 0; 35112e431b2SSimon Glass } 35212e431b2SSimon Glass 35312e431b2SSimon Glass int ns16550_serial_probe(struct udevice *dev) 35412e431b2SSimon Glass { 35512e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 35612e431b2SSimon Glass 35711c1a878SSimon Glass com_port->plat = dev_get_platdata(dev); 35812e431b2SSimon Glass NS16550_init(com_port, -1); 35912e431b2SSimon Glass 36012e431b2SSimon Glass return 0; 36112e431b2SSimon Glass } 36212e431b2SSimon Glass 3630f925822SMasahiro Yamada #if CONFIG_IS_ENABLED(OF_CONTROL) 36412e431b2SSimon Glass int ns16550_serial_ofdata_to_platdata(struct udevice *dev) 36512e431b2SSimon Glass { 36612e431b2SSimon Glass struct ns16550_platdata *plat = dev->platdata; 36712e431b2SSimon Glass fdt_addr_t addr; 36812e431b2SSimon Glass 3693db886a5SBin Meng /* try Processor Local Bus device first */ 3704e9838c1SSimon Glass addr = dev_get_addr(dev); 371*fcc0a877SSimon Glass #if defined(CONFIG_PCI) && defined(CONFIG_DM_PCI) 3723db886a5SBin Meng if (addr == FDT_ADDR_T_NONE) { 3733db886a5SBin Meng /* then try pci device */ 3743db886a5SBin Meng struct fdt_pci_addr pci_addr; 3753db886a5SBin Meng u32 bar; 3763db886a5SBin Meng int ret; 3773db886a5SBin Meng 3783db886a5SBin Meng /* we prefer to use a memory-mapped register */ 3793db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset, 3803db886a5SBin Meng FDT_PCI_SPACE_MEM32, "reg", 3813db886a5SBin Meng &pci_addr); 3823db886a5SBin Meng if (ret) { 3833db886a5SBin Meng /* try if there is any i/o-mapped register */ 3843db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, 3853db886a5SBin Meng dev->of_offset, 3863db886a5SBin Meng FDT_PCI_SPACE_IO, 3873db886a5SBin Meng "reg", &pci_addr); 3883db886a5SBin Meng if (ret) 3893db886a5SBin Meng return ret; 3903db886a5SBin Meng } 3913db886a5SBin Meng 392*fcc0a877SSimon Glass ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar); 3933db886a5SBin Meng if (ret) 3943db886a5SBin Meng return ret; 3953db886a5SBin Meng 3963db886a5SBin Meng addr = bar; 3973db886a5SBin Meng } 3983db886a5SBin Meng #endif 3993db886a5SBin Meng 40012e431b2SSimon Glass if (addr == FDT_ADDR_T_NONE) 40112e431b2SSimon Glass return -EINVAL; 40212e431b2SSimon Glass 403167efe01SSimon Glass plat->base = addr; 40412e431b2SSimon Glass plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 40580e06146SThomas Chou "reg-shift", 0); 4068e62d32eSThomas Chou plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 4078e62d32eSThomas Chou "clock-frequency", 4088e62d32eSThomas Chou CONFIG_SYS_NS16550_CLK); 4098e62d32eSThomas Chou if (!plat->clock) { 4108e62d32eSThomas Chou debug("ns16550 clock not defined\n"); 4118e62d32eSThomas Chou return -EINVAL; 4128e62d32eSThomas Chou } 41312e431b2SSimon Glass 41412e431b2SSimon Glass return 0; 41512e431b2SSimon Glass } 41611c1a878SSimon Glass #endif 41712e431b2SSimon Glass 41812e431b2SSimon Glass const struct dm_serial_ops ns16550_serial_ops = { 41912e431b2SSimon Glass .putc = ns16550_serial_putc, 42012e431b2SSimon Glass .pending = ns16550_serial_pending, 42112e431b2SSimon Glass .getc = ns16550_serial_getc, 42212e431b2SSimon Glass .setbrg = ns16550_serial_setbrg, 42312e431b2SSimon Glass }; 4248e62d32eSThomas Chou 4258e62d32eSThomas Chou #if CONFIG_IS_ENABLED(OF_CONTROL) 4268e62d32eSThomas Chou static const struct udevice_id ns16550_serial_ids[] = { 4278e62d32eSThomas Chou { .compatible = "ns16550" }, 4288e62d32eSThomas Chou { .compatible = "ns16550a" }, 4298e62d32eSThomas Chou { .compatible = "nvidia,tegra20-uart" }, 430a51dbebfShuang lin { .compatible = "rockchip,rk3036-uart" }, 4318e62d32eSThomas Chou { .compatible = "snps,dw-apb-uart" }, 4328e62d32eSThomas Chou { .compatible = "ti,omap2-uart" }, 4338e62d32eSThomas Chou { .compatible = "ti,omap3-uart" }, 4348e62d32eSThomas Chou { .compatible = "ti,omap4-uart" }, 4358e62d32eSThomas Chou { .compatible = "ti,am3352-uart" }, 4368e62d32eSThomas Chou { .compatible = "ti,am4372-uart" }, 4378e62d32eSThomas Chou { .compatible = "ti,dra742-uart" }, 4388e62d32eSThomas Chou {} 4398e62d32eSThomas Chou }; 4408e62d32eSThomas Chou #endif 4418e62d32eSThomas Chou 4428e62d32eSThomas Chou U_BOOT_DRIVER(ns16550_serial) = { 4438e62d32eSThomas Chou .name = "ns16550_serial", 4448e62d32eSThomas Chou .id = UCLASS_SERIAL, 4458e62d32eSThomas Chou #if CONFIG_IS_ENABLED(OF_CONTROL) 4468e62d32eSThomas Chou .of_match = ns16550_serial_ids, 4478e62d32eSThomas Chou .ofdata_to_platdata = ns16550_serial_ofdata_to_platdata, 4488e62d32eSThomas Chou .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), 4498e62d32eSThomas Chou #endif 4508e62d32eSThomas Chou .priv_auto_alloc_size = sizeof(struct NS16550), 4518e62d32eSThomas Chou .probe = ns16550_serial_probe, 4528e62d32eSThomas Chou .ops = &ns16550_serial_ops, 453b7e5a643SSimon Glass .flags = DM_FLAG_PRE_RELOC, 4548e62d32eSThomas Chou }; 45512e431b2SSimon Glass #endif /* CONFIG_DM_SERIAL */ 456