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 57363e6da1SSimon Glass static inline void serial_out_shift(void *addr, int shift, int value) 5876571674SSimon Glass { 5976571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 6076571674SSimon Glass outb(value, (ulong)addr); 6176571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 6276571674SSimon Glass out_le32(addr, value); 6376571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 6476571674SSimon Glass out_be32(addr, value); 6590914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 6690914008SSimon Glass writel(value, addr); 6776571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 6876571674SSimon Glass writeb(value, addr + (1 << shift) - 1); 6976571674SSimon Glass #else 7076571674SSimon Glass writeb(value, addr); 7176571674SSimon Glass #endif 7276571674SSimon Glass } 7376571674SSimon Glass 74363e6da1SSimon Glass static inline int serial_in_shift(void *addr, int shift) 7576571674SSimon Glass { 7676571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 7776571674SSimon Glass return inb((ulong)addr); 7876571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 7976571674SSimon Glass return in_le32(addr); 8076571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 8176571674SSimon Glass return in_be32(addr); 8290914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 8390914008SSimon Glass return readl(addr); 8476571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 8520379c11SAxel Lin return readb(addr + (1 << shift) - 1); 8676571674SSimon Glass #else 8776571674SSimon Glass return readb(addr); 8876571674SSimon Glass #endif 8976571674SSimon Glass } 9076571674SSimon Glass 91fa4ce723SMarek Vasut #ifdef CONFIG_DM_SERIAL 92fa4ce723SMarek Vasut 93fa4ce723SMarek Vasut #ifndef CONFIG_SYS_NS16550_CLK 94fa4ce723SMarek Vasut #define CONFIG_SYS_NS16550_CLK 0 95fa4ce723SMarek Vasut #endif 96fa4ce723SMarek Vasut 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; 103df8ec55dSPaul Burton addr = (unsigned char *)plat->base + offset; 104df8ec55dSPaul Burton 10512e431b2SSimon Glass /* 10612e431b2SSimon Glass * As far as we know it doesn't make sense to support selection of 10712e431b2SSimon Glass * these options at run-time, so use the existing CONFIG options. 10812e431b2SSimon Glass */ 10959b35dddSMichal Simek serial_out_shift(addr + plat->reg_offset, plat->reg_shift, value); 11012e431b2SSimon Glass } 11112e431b2SSimon Glass 11212e431b2SSimon Glass static int ns16550_readb(NS16550_t port, int offset) 11312e431b2SSimon Glass { 11412e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 11512e431b2SSimon Glass unsigned char *addr; 11612e431b2SSimon Glass 11712e431b2SSimon Glass offset *= 1 << plat->reg_shift; 118df8ec55dSPaul Burton addr = (unsigned char *)plat->base + offset; 11976571674SSimon Glass 12059b35dddSMichal Simek return serial_in_shift(addr + plat->reg_offset, plat->reg_shift); 12112e431b2SSimon Glass } 12212e431b2SSimon Glass 12312e431b2SSimon Glass /* We can clean these up once everything is moved to driver model */ 12412e431b2SSimon Glass #define serial_out(value, addr) \ 125363e6da1SSimon Glass ns16550_writeb(com_port, \ 126363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port, value) 12712e431b2SSimon Glass #define serial_in(addr) \ 128363e6da1SSimon Glass ns16550_readb(com_port, \ 129363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port) 13012e431b2SSimon Glass #endif 13112e431b2SSimon Glass 13203c6f176SMarek Vasut int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) 133fa54eb12SSimon Glass { 134fa54eb12SSimon Glass const unsigned int mode_x_div = 16; 135fa54eb12SSimon Glass 13621d00436SSimon Glass return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); 13721d00436SSimon Glass } 13821d00436SSimon Glass 1398bbe33c8SSimon Glass static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) 1408bbe33c8SSimon Glass { 1418bbe33c8SSimon Glass serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); 1428bbe33c8SSimon Glass serial_out(baud_divisor & 0xff, &com_port->dll); 1438bbe33c8SSimon Glass serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); 1448bbe33c8SSimon Glass serial_out(UART_LCRVAL, &com_port->lcr); 1458bbe33c8SSimon Glass } 1468bbe33c8SSimon Glass 1471378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_init(NS16550_t com_port, int baud_divisor) 1481378df79SJean-Christophe PLAGNIOL-VILLARD { 149956a8baeSGregoire Gentil #if (defined(CONFIG_SPL_BUILD) && \ 150956a8baeSGregoire Gentil (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) 151fd2aeac5SManfred Huber /* 152956a8baeSGregoire Gentil * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode 153956a8baeSGregoire Gentil * before SPL starts only THRE bit is set. We have to empty the 154956a8baeSGregoire Gentil * transmitter before initialization starts. 155fd2aeac5SManfred Huber */ 156fd2aeac5SManfred Huber if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) 157fd2aeac5SManfred Huber == UART_LSR_THRE) { 15812e431b2SSimon Glass if (baud_divisor != -1) 1598bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 160fd2aeac5SManfred Huber serial_out(0, &com_port->mdr1); 161fd2aeac5SManfred Huber } 162fd2aeac5SManfred Huber #endif 163fd2aeac5SManfred Huber 164cb55b332SScott Wood while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT)) 165cb55b332SScott Wood ; 166cb55b332SScott Wood 167a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 168456ccfdfSTom Rini #if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \ 169456ccfdfSTom Rini defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 170167cdad1SGraeme Russ serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ 1711378df79SJean-Christophe PLAGNIOL-VILLARD #endif 172167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 173167cdad1SGraeme Russ serial_out(UART_FCRVAL, &com_port->fcr); 17412e431b2SSimon Glass if (baud_divisor != -1) 1758bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 1768ac22a60SMasahiro Yamada #if defined(CONFIG_OMAP) || \ 1776213a68fSMatt Porter defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ 1789ed6e412STENART Antoine defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 1795289e83aSChandan Nath 180f8df9d0dSSimon Glass /* /16 is proper to hit 115200 with 48MHz */ 181f8df9d0dSSimon Glass serial_out(0, &com_port->mdr1); 182b4746d8bSMike Frysinger #endif /* CONFIG_OMAP */ 1837c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 184ef509b90SVitaly Andrianov serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); 185ef509b90SVitaly Andrianov #endif 1861378df79SJean-Christophe PLAGNIOL-VILLARD } 1871378df79SJean-Christophe PLAGNIOL-VILLARD 188f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 1891378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_reinit(NS16550_t com_port, int baud_divisor) 1901378df79SJean-Christophe PLAGNIOL-VILLARD { 191a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 1928bbe33c8SSimon Glass NS16550_setbrg(com_port, 0); 193167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 194167cdad1SGraeme Russ serial_out(UART_FCRVAL, &com_port->fcr); 1958bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 1961378df79SJean-Christophe PLAGNIOL-VILLARD } 197f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 1981378df79SJean-Christophe PLAGNIOL-VILLARD 1991378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_putc(NS16550_t com_port, char c) 2001378df79SJean-Christophe PLAGNIOL-VILLARD { 201f8df9d0dSSimon Glass while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0) 202f8df9d0dSSimon Glass ; 203167cdad1SGraeme Russ serial_out(c, &com_port->thr); 2041a2d9b30SStefan Roese 2051a2d9b30SStefan Roese /* 2061a2d9b30SStefan Roese * Call watchdog_reset() upon newline. This is done here in putc 2071a2d9b30SStefan Roese * since the environment code uses a single puts() to print the complete 2081a2d9b30SStefan Roese * environment upon "printenv". So we can't put this watchdog call 2091a2d9b30SStefan Roese * in puts(). 2101a2d9b30SStefan Roese */ 2111a2d9b30SStefan Roese if (c == '\n') 2121a2d9b30SStefan Roese WATCHDOG_RESET(); 2131378df79SJean-Christophe PLAGNIOL-VILLARD } 2141378df79SJean-Christophe PLAGNIOL-VILLARD 215f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 2161378df79SJean-Christophe PLAGNIOL-VILLARD char NS16550_getc(NS16550_t com_port) 2171378df79SJean-Christophe PLAGNIOL-VILLARD { 218167cdad1SGraeme Russ while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { 219f2041388SMarek Vasut #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY) 2201378df79SJean-Christophe PLAGNIOL-VILLARD extern void usbtty_poll(void); 2211378df79SJean-Christophe PLAGNIOL-VILLARD usbtty_poll(); 2221378df79SJean-Christophe PLAGNIOL-VILLARD #endif 223a1b322a9SLadislav Michl WATCHDOG_RESET(); 2241378df79SJean-Christophe PLAGNIOL-VILLARD } 225167cdad1SGraeme Russ return serial_in(&com_port->rbr); 2261378df79SJean-Christophe PLAGNIOL-VILLARD } 2271378df79SJean-Christophe PLAGNIOL-VILLARD 2281378df79SJean-Christophe PLAGNIOL-VILLARD int NS16550_tstc(NS16550_t com_port) 2291378df79SJean-Christophe PLAGNIOL-VILLARD { 230f8df9d0dSSimon Glass return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0; 2311378df79SJean-Christophe PLAGNIOL-VILLARD } 2321378df79SJean-Christophe PLAGNIOL-VILLARD 233f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 23412e431b2SSimon Glass 23521d00436SSimon Glass #ifdef CONFIG_DEBUG_UART_NS16550 23621d00436SSimon Glass 23721d00436SSimon Glass #include <debug_uart.h> 23821d00436SSimon Glass 2396e780c7aSSimon Glass #define serial_dout(reg, value) \ 2406e780c7aSSimon Glass serial_out_shift((char *)com_port + \ 2416e780c7aSSimon Glass ((char *)reg - (char *)com_port) * \ 2426e780c7aSSimon Glass (1 << CONFIG_DEBUG_UART_SHIFT), \ 2436e780c7aSSimon Glass CONFIG_DEBUG_UART_SHIFT, value) 2446e780c7aSSimon Glass #define serial_din(reg) \ 2456e780c7aSSimon Glass serial_in_shift((char *)com_port + \ 2466e780c7aSSimon Glass ((char *)reg - (char *)com_port) * \ 2476e780c7aSSimon Glass (1 << CONFIG_DEBUG_UART_SHIFT), \ 2486e780c7aSSimon Glass CONFIG_DEBUG_UART_SHIFT) 2496e780c7aSSimon Glass 25097b05973SSimon Glass static inline void _debug_uart_init(void) 25121d00436SSimon Glass { 25221d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 25321d00436SSimon Glass int baud_divisor; 25421d00436SSimon Glass 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); 2656e780c7aSSimon Glass serial_dout(&com_port->fcr, UART_FCRVAL); 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 { 27521d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 27621d00436SSimon Glass 2776e780c7aSSimon Glass while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) 27821d00436SSimon Glass ; 2796e780c7aSSimon Glass serial_dout(&com_port->thr, ch); 28021d00436SSimon Glass } 28121d00436SSimon Glass 28221d00436SSimon Glass DEBUG_UART_FUNCS 28321d00436SSimon Glass 28421d00436SSimon Glass #endif 28521d00436SSimon Glass 28612e431b2SSimon Glass #ifdef CONFIG_DM_SERIAL 28712e431b2SSimon Glass static int ns16550_serial_putc(struct udevice *dev, const char ch) 28812e431b2SSimon Glass { 28912e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 29012e431b2SSimon Glass 29112e431b2SSimon Glass if (!(serial_in(&com_port->lsr) & UART_LSR_THRE)) 29212e431b2SSimon Glass return -EAGAIN; 29312e431b2SSimon Glass serial_out(ch, &com_port->thr); 29412e431b2SSimon Glass 29512e431b2SSimon Glass /* 29612e431b2SSimon Glass * Call watchdog_reset() upon newline. This is done here in putc 29712e431b2SSimon Glass * since the environment code uses a single puts() to print the complete 29812e431b2SSimon Glass * environment upon "printenv". So we can't put this watchdog call 29912e431b2SSimon Glass * in puts(). 30012e431b2SSimon Glass */ 30112e431b2SSimon Glass if (ch == '\n') 30212e431b2SSimon Glass WATCHDOG_RESET(); 30312e431b2SSimon Glass 30412e431b2SSimon Glass return 0; 30512e431b2SSimon Glass } 30612e431b2SSimon Glass 30712e431b2SSimon Glass static int ns16550_serial_pending(struct udevice *dev, bool input) 30812e431b2SSimon Glass { 30912e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 31012e431b2SSimon Glass 31112e431b2SSimon Glass if (input) 31212e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0; 31312e431b2SSimon Glass else 31412e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; 31512e431b2SSimon Glass } 31612e431b2SSimon Glass 31712e431b2SSimon Glass static int ns16550_serial_getc(struct udevice *dev) 31812e431b2SSimon Glass { 31912e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 32012e431b2SSimon Glass 321aea2be20SSimon Glass if (!(serial_in(&com_port->lsr) & UART_LSR_DR)) 32212e431b2SSimon Glass return -EAGAIN; 32312e431b2SSimon Glass 32412e431b2SSimon Glass return serial_in(&com_port->rbr); 32512e431b2SSimon Glass } 32612e431b2SSimon Glass 32712e431b2SSimon Glass static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) 32812e431b2SSimon Glass { 32912e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 33012e431b2SSimon Glass struct ns16550_platdata *plat = com_port->plat; 33112e431b2SSimon Glass int clock_divisor; 33212e431b2SSimon Glass 33312e431b2SSimon Glass clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); 33412e431b2SSimon Glass 33512e431b2SSimon Glass NS16550_setbrg(com_port, clock_divisor); 33612e431b2SSimon Glass 33712e431b2SSimon Glass return 0; 33812e431b2SSimon Glass } 33912e431b2SSimon Glass 34012e431b2SSimon Glass int ns16550_serial_probe(struct udevice *dev) 34112e431b2SSimon Glass { 34212e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 34312e431b2SSimon Glass 34411c1a878SSimon Glass com_port->plat = dev_get_platdata(dev); 34512e431b2SSimon Glass NS16550_init(com_port, -1); 34612e431b2SSimon Glass 34712e431b2SSimon Glass return 0; 34812e431b2SSimon Glass } 34912e431b2SSimon Glass 350*b2927fbaSSimon Glass #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) 35112e431b2SSimon Glass int ns16550_serial_ofdata_to_platdata(struct udevice *dev) 35212e431b2SSimon Glass { 35312e431b2SSimon Glass struct ns16550_platdata *plat = dev->platdata; 35412e431b2SSimon Glass fdt_addr_t addr; 35512e431b2SSimon Glass 3563db886a5SBin Meng /* try Processor Local Bus device first */ 3574e9838c1SSimon Glass addr = dev_get_addr(dev); 358fcc0a877SSimon Glass #if defined(CONFIG_PCI) && defined(CONFIG_DM_PCI) 3593db886a5SBin Meng if (addr == FDT_ADDR_T_NONE) { 3603db886a5SBin Meng /* then try pci device */ 3613db886a5SBin Meng struct fdt_pci_addr pci_addr; 3623db886a5SBin Meng u32 bar; 3633db886a5SBin Meng int ret; 3643db886a5SBin Meng 3653db886a5SBin Meng /* we prefer to use a memory-mapped register */ 3663db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset, 3673db886a5SBin Meng FDT_PCI_SPACE_MEM32, "reg", 3683db886a5SBin Meng &pci_addr); 3693db886a5SBin Meng if (ret) { 3703db886a5SBin Meng /* try if there is any i/o-mapped register */ 3713db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, 3723db886a5SBin Meng dev->of_offset, 3733db886a5SBin Meng FDT_PCI_SPACE_IO, 3743db886a5SBin Meng "reg", &pci_addr); 3753db886a5SBin Meng if (ret) 3763db886a5SBin Meng return ret; 3773db886a5SBin Meng } 3783db886a5SBin Meng 379fcc0a877SSimon Glass ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar); 3803db886a5SBin Meng if (ret) 3813db886a5SBin Meng return ret; 3823db886a5SBin Meng 3833db886a5SBin Meng addr = bar; 3843db886a5SBin Meng } 3853db886a5SBin Meng #endif 3863db886a5SBin Meng 38712e431b2SSimon Glass if (addr == FDT_ADDR_T_NONE) 38812e431b2SSimon Glass return -EINVAL; 38912e431b2SSimon Glass 390df8ec55dSPaul Burton #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 391167efe01SSimon Glass plat->base = addr; 392df8ec55dSPaul Burton #else 393df8ec55dSPaul Burton plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE); 394df8ec55dSPaul Burton #endif 395df8ec55dSPaul Burton 39659b35dddSMichal Simek plat->reg_offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 39759b35dddSMichal Simek "reg-offset", 0); 39812e431b2SSimon Glass plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 39980e06146SThomas Chou "reg-shift", 0); 4008e62d32eSThomas Chou plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 4018e62d32eSThomas Chou "clock-frequency", 4028e62d32eSThomas Chou CONFIG_SYS_NS16550_CLK); 4038e62d32eSThomas Chou if (!plat->clock) { 4048e62d32eSThomas Chou debug("ns16550 clock not defined\n"); 4058e62d32eSThomas Chou return -EINVAL; 4068e62d32eSThomas Chou } 40712e431b2SSimon Glass 40812e431b2SSimon Glass return 0; 40912e431b2SSimon Glass } 41011c1a878SSimon Glass #endif 41112e431b2SSimon Glass 41212e431b2SSimon Glass const struct dm_serial_ops ns16550_serial_ops = { 41312e431b2SSimon Glass .putc = ns16550_serial_putc, 41412e431b2SSimon Glass .pending = ns16550_serial_pending, 41512e431b2SSimon Glass .getc = ns16550_serial_getc, 41612e431b2SSimon Glass .setbrg = ns16550_serial_setbrg, 41712e431b2SSimon Glass }; 4188e62d32eSThomas Chou 419*b2927fbaSSimon Glass #if !CONFIG_IS_ENABLED(OF_PLATDATA) 4208e62d32eSThomas Chou #if CONFIG_IS_ENABLED(OF_CONTROL) 421cc4228f9SThomas Chou /* 422cc4228f9SThomas Chou * Please consider existing compatible strings before adding a new 423cc4228f9SThomas Chou * one to keep this table compact. Or you may add a generic "ns16550" 424cc4228f9SThomas Chou * compatible string to your dts. 425cc4228f9SThomas Chou */ 4268e62d32eSThomas Chou static const struct udevice_id ns16550_serial_ids[] = { 4278e62d32eSThomas Chou { .compatible = "ns16550" }, 4288e62d32eSThomas Chou { .compatible = "ns16550a" }, 4298e62d32eSThomas Chou { .compatible = "nvidia,tegra20-uart" }, 4308e62d32eSThomas Chou { .compatible = "snps,dw-apb-uart" }, 4318e62d32eSThomas Chou { .compatible = "ti,omap2-uart" }, 4328e62d32eSThomas Chou { .compatible = "ti,omap3-uart" }, 4338e62d32eSThomas Chou { .compatible = "ti,omap4-uart" }, 4348e62d32eSThomas Chou { .compatible = "ti,am3352-uart" }, 4358e62d32eSThomas Chou { .compatible = "ti,am4372-uart" }, 4368e62d32eSThomas Chou { .compatible = "ti,dra742-uart" }, 4378e62d32eSThomas Chou {} 4388e62d32eSThomas Chou }; 4398e62d32eSThomas Chou #endif 4408e62d32eSThomas Chou 441b7e29834SSimon Glass #if CONFIG_IS_ENABLED(SERIAL_PRESENT) 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 }; 455b7e29834SSimon Glass #endif 456*b2927fbaSSimon Glass #endif /* !OF_PLATDATA */ 45712e431b2SSimon Glass #endif /* CONFIG_DM_SERIAL */ 458