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> 1112e431b2SSimon Glass #include <fdtdec.h> 121378df79SJean-Christophe PLAGNIOL-VILLARD #include <ns16550.h> 1312e431b2SSimon Glass #include <serial.h> 14a1b322a9SLadislav Michl #include <watchdog.h> 15167cdad1SGraeme Russ #include <linux/types.h> 16167cdad1SGraeme Russ #include <asm/io.h> 171378df79SJean-Christophe PLAGNIOL-VILLARD 1812e431b2SSimon Glass DECLARE_GLOBAL_DATA_PTR; 1912e431b2SSimon Glass 20200779e3SDetlev Zundel #define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */ 21200779e3SDetlev Zundel #define UART_MCRVAL (UART_MCR_DTR | \ 22200779e3SDetlev Zundel UART_MCR_RTS) /* RTS/DTR */ 23200779e3SDetlev Zundel #define UART_FCRVAL (UART_FCR_FIFO_EN | \ 24200779e3SDetlev Zundel UART_FCR_RXSR | \ 25200779e3SDetlev Zundel UART_FCR_TXSR) /* Clear & enable FIFOs */ 2612e431b2SSimon Glass 2712e431b2SSimon Glass #ifndef CONFIG_DM_SERIAL 28167cdad1SGraeme Russ #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 29167cdad1SGraeme Russ #define serial_out(x, y) outb(x, (ulong)y) 30167cdad1SGraeme Russ #define serial_in(y) inb((ulong)y) 3179df1208SDave Aldridge #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE > 0) 3279df1208SDave Aldridge #define serial_out(x, y) out_be32(y, x) 3379df1208SDave Aldridge #define serial_in(y) in_be32(y) 3479df1208SDave Aldridge #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE < 0) 3579df1208SDave Aldridge #define serial_out(x, y) out_le32(y, x) 3679df1208SDave Aldridge #define serial_in(y) in_le32(y) 37167cdad1SGraeme Russ #else 38167cdad1SGraeme Russ #define serial_out(x, y) writeb(x, y) 39167cdad1SGraeme Russ #define serial_in(y) readb(y) 40167cdad1SGraeme Russ #endif 4112e431b2SSimon Glass #endif /* !CONFIG_DM_SERIAL */ 421378df79SJean-Christophe PLAGNIOL-VILLARD 437c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 44ef509b90SVitaly Andrianov #define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 45ef509b90SVitaly Andrianov #define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0)) 46d57dee57SKaricheri, Muralidharan #undef UART_MCRVAL 47d57dee57SKaricheri, Muralidharan #ifdef CONFIG_SERIAL_HW_FLOW_CONTROL 48d57dee57SKaricheri, Muralidharan #define UART_MCRVAL (UART_MCR_RTS | UART_MCR_AFE) 49d57dee57SKaricheri, Muralidharan #else 50d57dee57SKaricheri, Muralidharan #define UART_MCRVAL (UART_MCR_RTS) 51d57dee57SKaricheri, Muralidharan #endif 52ef509b90SVitaly Andrianov #endif 53ef509b90SVitaly Andrianov 54a160ea0bSPrafulla Wadaskar #ifndef CONFIG_SYS_NS16550_IER 55a160ea0bSPrafulla Wadaskar #define CONFIG_SYS_NS16550_IER 0x00 56a160ea0bSPrafulla Wadaskar #endif /* CONFIG_SYS_NS16550_IER */ 57a160ea0bSPrafulla Wadaskar 58363e6da1SSimon Glass static inline void serial_out_shift(void *addr, int shift, int value) 5976571674SSimon Glass { 6076571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 6176571674SSimon Glass outb(value, (ulong)addr); 6276571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 6376571674SSimon Glass out_le32(addr, value); 6476571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 6576571674SSimon Glass out_be32(addr, value); 6690914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 6790914008SSimon Glass writel(value, addr); 6876571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 6976571674SSimon Glass writeb(value, addr + (1 << shift) - 1); 7076571674SSimon Glass #else 7176571674SSimon Glass writeb(value, addr); 7276571674SSimon Glass #endif 7376571674SSimon Glass } 7476571674SSimon Glass 75363e6da1SSimon Glass static inline int serial_in_shift(void *addr, int shift) 7676571674SSimon Glass { 7776571674SSimon Glass #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 7876571674SSimon Glass return inb((ulong)addr); 7976571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 8076571674SSimon Glass return in_le32(addr); 8176571674SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 8276571674SSimon Glass return in_be32(addr); 8390914008SSimon Glass #elif defined(CONFIG_SYS_NS16550_MEM32) 8490914008SSimon Glass return readl(addr); 8576571674SSimon Glass #elif defined(CONFIG_SYS_BIG_ENDIAN) 8620379c11SAxel Lin return readb(addr + (1 << shift) - 1); 8776571674SSimon Glass #else 8876571674SSimon Glass return readb(addr); 8976571674SSimon Glass #endif 9076571674SSimon Glass } 9176571674SSimon Glass 92fa4ce723SMarek Vasut #ifdef CONFIG_DM_SERIAL 93fa4ce723SMarek Vasut 94fa4ce723SMarek Vasut #ifndef CONFIG_SYS_NS16550_CLK 95fa4ce723SMarek Vasut #define CONFIG_SYS_NS16550_CLK 0 96fa4ce723SMarek Vasut #endif 97fa4ce723SMarek Vasut 9812e431b2SSimon Glass static void ns16550_writeb(NS16550_t port, int offset, int value) 9912e431b2SSimon Glass { 10012e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 10112e431b2SSimon Glass unsigned char *addr; 10212e431b2SSimon Glass 10312e431b2SSimon Glass offset *= 1 << plat->reg_shift; 104df8ec55dSPaul Burton addr = (unsigned char *)plat->base + offset; 105df8ec55dSPaul Burton 10612e431b2SSimon Glass /* 10712e431b2SSimon Glass * As far as we know it doesn't make sense to support selection of 10812e431b2SSimon Glass * these options at run-time, so use the existing CONFIG options. 10912e431b2SSimon Glass */ 11059b35dddSMichal Simek serial_out_shift(addr + plat->reg_offset, plat->reg_shift, value); 11112e431b2SSimon Glass } 11212e431b2SSimon Glass 11312e431b2SSimon Glass static int ns16550_readb(NS16550_t port, int offset) 11412e431b2SSimon Glass { 11512e431b2SSimon Glass struct ns16550_platdata *plat = port->plat; 11612e431b2SSimon Glass unsigned char *addr; 11712e431b2SSimon Glass 11812e431b2SSimon Glass offset *= 1 << plat->reg_shift; 119df8ec55dSPaul Burton addr = (unsigned char *)plat->base + offset; 12076571674SSimon Glass 12159b35dddSMichal Simek return serial_in_shift(addr + plat->reg_offset, plat->reg_shift); 12212e431b2SSimon Glass } 12312e431b2SSimon Glass 12412e431b2SSimon Glass /* We can clean these up once everything is moved to driver model */ 12512e431b2SSimon Glass #define serial_out(value, addr) \ 126363e6da1SSimon Glass ns16550_writeb(com_port, \ 127363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port, value) 12812e431b2SSimon Glass #define serial_in(addr) \ 129363e6da1SSimon Glass ns16550_readb(com_port, \ 130363e6da1SSimon Glass (unsigned char *)addr - (unsigned char *)com_port) 13112e431b2SSimon Glass #endif 13212e431b2SSimon Glass 13303c6f176SMarek Vasut int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) 134fa54eb12SSimon Glass { 135fa54eb12SSimon Glass const unsigned int mode_x_div = 16; 136fa54eb12SSimon Glass 13721d00436SSimon Glass return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); 13821d00436SSimon Glass } 13921d00436SSimon Glass 1408bbe33c8SSimon Glass static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) 1418bbe33c8SSimon Glass { 1428bbe33c8SSimon Glass serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); 1438bbe33c8SSimon Glass serial_out(baud_divisor & 0xff, &com_port->dll); 1448bbe33c8SSimon Glass serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); 1458bbe33c8SSimon Glass serial_out(UART_LCRVAL, &com_port->lcr); 1468bbe33c8SSimon Glass } 1478bbe33c8SSimon Glass 1481378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_init(NS16550_t com_port, int baud_divisor) 1491378df79SJean-Christophe PLAGNIOL-VILLARD { 150956a8baeSGregoire Gentil #if (defined(CONFIG_SPL_BUILD) && \ 151956a8baeSGregoire Gentil (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) 152fd2aeac5SManfred Huber /* 153956a8baeSGregoire Gentil * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode 154956a8baeSGregoire Gentil * before SPL starts only THRE bit is set. We have to empty the 155956a8baeSGregoire Gentil * transmitter before initialization starts. 156fd2aeac5SManfred Huber */ 157fd2aeac5SManfred Huber if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) 158fd2aeac5SManfred Huber == UART_LSR_THRE) { 15912e431b2SSimon Glass if (baud_divisor != -1) 1608bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 161fd2aeac5SManfred Huber serial_out(0, &com_port->mdr1); 162fd2aeac5SManfred Huber } 163fd2aeac5SManfred Huber #endif 164fd2aeac5SManfred Huber 165cb55b332SScott Wood while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT)) 166cb55b332SScott Wood ; 167cb55b332SScott Wood 168a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 169456ccfdfSTom Rini #if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \ 170456ccfdfSTom Rini defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 171167cdad1SGraeme Russ serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ 1721378df79SJean-Christophe PLAGNIOL-VILLARD #endif 173167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 174167cdad1SGraeme Russ serial_out(UART_FCRVAL, &com_port->fcr); 17512e431b2SSimon Glass if (baud_divisor != -1) 1768bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 1778ac22a60SMasahiro Yamada #if defined(CONFIG_OMAP) || \ 1786213a68fSMatt Porter defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ 1799ed6e412STENART Antoine defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 1805289e83aSChandan Nath 181f8df9d0dSSimon Glass /* /16 is proper to hit 115200 with 48MHz */ 182f8df9d0dSSimon Glass serial_out(0, &com_port->mdr1); 183b4746d8bSMike Frysinger #endif /* CONFIG_OMAP */ 1847c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE) 185ef509b90SVitaly Andrianov serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); 186ef509b90SVitaly Andrianov #endif 1871378df79SJean-Christophe PLAGNIOL-VILLARD } 1881378df79SJean-Christophe PLAGNIOL-VILLARD 189f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 1901378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_reinit(NS16550_t com_port, int baud_divisor) 1911378df79SJean-Christophe PLAGNIOL-VILLARD { 192a160ea0bSPrafulla Wadaskar serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 1938bbe33c8SSimon Glass NS16550_setbrg(com_port, 0); 194167cdad1SGraeme Russ serial_out(UART_MCRVAL, &com_port->mcr); 195167cdad1SGraeme Russ serial_out(UART_FCRVAL, &com_port->fcr); 1968bbe33c8SSimon Glass NS16550_setbrg(com_port, baud_divisor); 1971378df79SJean-Christophe PLAGNIOL-VILLARD } 198f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 1991378df79SJean-Christophe PLAGNIOL-VILLARD 2001378df79SJean-Christophe PLAGNIOL-VILLARD void NS16550_putc(NS16550_t com_port, char c) 2011378df79SJean-Christophe PLAGNIOL-VILLARD { 202f8df9d0dSSimon Glass while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0) 203f8df9d0dSSimon Glass ; 204167cdad1SGraeme Russ serial_out(c, &com_port->thr); 2051a2d9b30SStefan Roese 2061a2d9b30SStefan Roese /* 2071a2d9b30SStefan Roese * Call watchdog_reset() upon newline. This is done here in putc 2081a2d9b30SStefan Roese * since the environment code uses a single puts() to print the complete 2091a2d9b30SStefan Roese * environment upon "printenv". So we can't put this watchdog call 2101a2d9b30SStefan Roese * in puts(). 2111a2d9b30SStefan Roese */ 2121a2d9b30SStefan Roese if (c == '\n') 2131a2d9b30SStefan Roese WATCHDOG_RESET(); 2141378df79SJean-Christophe PLAGNIOL-VILLARD } 2151378df79SJean-Christophe PLAGNIOL-VILLARD 216f5675aa5SRon Madrid #ifndef CONFIG_NS16550_MIN_FUNCTIONS 2171378df79SJean-Christophe PLAGNIOL-VILLARD char NS16550_getc(NS16550_t com_port) 2181378df79SJean-Christophe PLAGNIOL-VILLARD { 219167cdad1SGraeme Russ while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { 220f2041388SMarek Vasut #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY) 2211378df79SJean-Christophe PLAGNIOL-VILLARD extern void usbtty_poll(void); 2221378df79SJean-Christophe PLAGNIOL-VILLARD usbtty_poll(); 2231378df79SJean-Christophe PLAGNIOL-VILLARD #endif 224a1b322a9SLadislav Michl WATCHDOG_RESET(); 2251378df79SJean-Christophe PLAGNIOL-VILLARD } 226167cdad1SGraeme Russ return serial_in(&com_port->rbr); 2271378df79SJean-Christophe PLAGNIOL-VILLARD } 2281378df79SJean-Christophe PLAGNIOL-VILLARD 2291378df79SJean-Christophe PLAGNIOL-VILLARD int NS16550_tstc(NS16550_t com_port) 2301378df79SJean-Christophe PLAGNIOL-VILLARD { 231f8df9d0dSSimon Glass return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0; 2321378df79SJean-Christophe PLAGNIOL-VILLARD } 2331378df79SJean-Christophe PLAGNIOL-VILLARD 234f5675aa5SRon Madrid #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 23512e431b2SSimon Glass 23621d00436SSimon Glass #ifdef CONFIG_DEBUG_UART_NS16550 23721d00436SSimon Glass 23821d00436SSimon Glass #include <debug_uart.h> 23921d00436SSimon Glass 2406e780c7aSSimon Glass #define serial_dout(reg, value) \ 2416e780c7aSSimon Glass serial_out_shift((char *)com_port + \ 2426e780c7aSSimon Glass ((char *)reg - (char *)com_port) * \ 2436e780c7aSSimon Glass (1 << CONFIG_DEBUG_UART_SHIFT), \ 2446e780c7aSSimon Glass CONFIG_DEBUG_UART_SHIFT, value) 2456e780c7aSSimon Glass #define serial_din(reg) \ 2466e780c7aSSimon Glass serial_in_shift((char *)com_port + \ 2476e780c7aSSimon Glass ((char *)reg - (char *)com_port) * \ 2486e780c7aSSimon Glass (1 << CONFIG_DEBUG_UART_SHIFT), \ 2496e780c7aSSimon Glass CONFIG_DEBUG_UART_SHIFT) 2506e780c7aSSimon Glass 25197b05973SSimon Glass static inline void _debug_uart_init(void) 25221d00436SSimon Glass { 25321d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 25421d00436SSimon Glass int baud_divisor; 25521d00436SSimon Glass 25621d00436SSimon Glass /* 25721d00436SSimon Glass * We copy the code from above because it is already horribly messy. 25821d00436SSimon Glass * Trying to refactor to nicely remove the duplication doesn't seem 25921d00436SSimon Glass * feasible. The better fix is to move all users of this driver to 26021d00436SSimon Glass * driver model. 26121d00436SSimon Glass */ 26203c6f176SMarek Vasut baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, 26321d00436SSimon Glass CONFIG_BAUDRATE); 2646e780c7aSSimon Glass serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); 2656e780c7aSSimon Glass serial_dout(&com_port->mcr, UART_MCRVAL); 2666e780c7aSSimon Glass serial_dout(&com_port->fcr, UART_FCRVAL); 26721d00436SSimon Glass 2686e780c7aSSimon Glass serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); 2696e780c7aSSimon Glass serial_dout(&com_port->dll, baud_divisor & 0xff); 2706e780c7aSSimon Glass serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); 2716e780c7aSSimon Glass serial_dout(&com_port->lcr, UART_LCRVAL); 27221d00436SSimon Glass } 27321d00436SSimon Glass 27421d00436SSimon Glass static inline void _debug_uart_putc(int ch) 27521d00436SSimon Glass { 27621d00436SSimon Glass struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 27721d00436SSimon Glass 2786e780c7aSSimon Glass while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) 27921d00436SSimon Glass ; 2806e780c7aSSimon Glass serial_dout(&com_port->thr, ch); 28121d00436SSimon Glass } 28221d00436SSimon Glass 28321d00436SSimon Glass DEBUG_UART_FUNCS 28421d00436SSimon Glass 28521d00436SSimon Glass #endif 28621d00436SSimon Glass 28712e431b2SSimon Glass #ifdef CONFIG_DM_SERIAL 28812e431b2SSimon Glass static int ns16550_serial_putc(struct udevice *dev, const char ch) 28912e431b2SSimon Glass { 29012e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 29112e431b2SSimon Glass 29212e431b2SSimon Glass if (!(serial_in(&com_port->lsr) & UART_LSR_THRE)) 29312e431b2SSimon Glass return -EAGAIN; 29412e431b2SSimon Glass serial_out(ch, &com_port->thr); 29512e431b2SSimon Glass 29612e431b2SSimon Glass /* 29712e431b2SSimon Glass * Call watchdog_reset() upon newline. This is done here in putc 29812e431b2SSimon Glass * since the environment code uses a single puts() to print the complete 29912e431b2SSimon Glass * environment upon "printenv". So we can't put this watchdog call 30012e431b2SSimon Glass * in puts(). 30112e431b2SSimon Glass */ 30212e431b2SSimon Glass if (ch == '\n') 30312e431b2SSimon Glass WATCHDOG_RESET(); 30412e431b2SSimon Glass 30512e431b2SSimon Glass return 0; 30612e431b2SSimon Glass } 30712e431b2SSimon Glass 30812e431b2SSimon Glass static int ns16550_serial_pending(struct udevice *dev, bool input) 30912e431b2SSimon Glass { 31012e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 31112e431b2SSimon Glass 31212e431b2SSimon Glass if (input) 31312e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0; 31412e431b2SSimon Glass else 31512e431b2SSimon Glass return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; 31612e431b2SSimon Glass } 31712e431b2SSimon Glass 31812e431b2SSimon Glass static int ns16550_serial_getc(struct udevice *dev) 31912e431b2SSimon Glass { 32012e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 32112e431b2SSimon Glass 322aea2be20SSimon Glass if (!(serial_in(&com_port->lsr) & UART_LSR_DR)) 32312e431b2SSimon Glass return -EAGAIN; 32412e431b2SSimon Glass 32512e431b2SSimon Glass return serial_in(&com_port->rbr); 32612e431b2SSimon Glass } 32712e431b2SSimon Glass 32812e431b2SSimon Glass static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) 32912e431b2SSimon Glass { 33012e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 33112e431b2SSimon Glass struct ns16550_platdata *plat = com_port->plat; 33212e431b2SSimon Glass int clock_divisor; 33312e431b2SSimon Glass 33412e431b2SSimon Glass clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); 33512e431b2SSimon Glass 33612e431b2SSimon Glass NS16550_setbrg(com_port, clock_divisor); 33712e431b2SSimon Glass 33812e431b2SSimon Glass return 0; 33912e431b2SSimon Glass } 34012e431b2SSimon Glass 34112e431b2SSimon Glass int ns16550_serial_probe(struct udevice *dev) 34212e431b2SSimon Glass { 34312e431b2SSimon Glass struct NS16550 *const com_port = dev_get_priv(dev); 34412e431b2SSimon Glass 34511c1a878SSimon Glass com_port->plat = dev_get_platdata(dev); 34612e431b2SSimon Glass NS16550_init(com_port, -1); 34712e431b2SSimon Glass 34812e431b2SSimon Glass return 0; 34912e431b2SSimon Glass } 35012e431b2SSimon Glass 351b2927fbaSSimon Glass #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) 35212e431b2SSimon Glass int ns16550_serial_ofdata_to_platdata(struct udevice *dev) 35312e431b2SSimon Glass { 35412e431b2SSimon Glass struct ns16550_platdata *plat = dev->platdata; 35512e431b2SSimon Glass fdt_addr_t addr; 356*021abf69SMasahiro Yamada struct clk clk; 357*021abf69SMasahiro Yamada int err; 35812e431b2SSimon Glass 3593db886a5SBin Meng /* try Processor Local Bus device first */ 3604e9838c1SSimon Glass addr = dev_get_addr(dev); 361fcc0a877SSimon Glass #if defined(CONFIG_PCI) && defined(CONFIG_DM_PCI) 3623db886a5SBin Meng if (addr == FDT_ADDR_T_NONE) { 3633db886a5SBin Meng /* then try pci device */ 3643db886a5SBin Meng struct fdt_pci_addr pci_addr; 3653db886a5SBin Meng u32 bar; 3663db886a5SBin Meng int ret; 3673db886a5SBin Meng 3683db886a5SBin Meng /* we prefer to use a memory-mapped register */ 3693db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset, 3703db886a5SBin Meng FDT_PCI_SPACE_MEM32, "reg", 3713db886a5SBin Meng &pci_addr); 3723db886a5SBin Meng if (ret) { 3733db886a5SBin Meng /* try if there is any i/o-mapped register */ 3743db886a5SBin Meng ret = fdtdec_get_pci_addr(gd->fdt_blob, 3753db886a5SBin Meng dev->of_offset, 3763db886a5SBin Meng FDT_PCI_SPACE_IO, 3773db886a5SBin Meng "reg", &pci_addr); 3783db886a5SBin Meng if (ret) 3793db886a5SBin Meng return ret; 3803db886a5SBin Meng } 3813db886a5SBin Meng 382fcc0a877SSimon Glass ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar); 3833db886a5SBin Meng if (ret) 3843db886a5SBin Meng return ret; 3853db886a5SBin Meng 3863db886a5SBin Meng addr = bar; 3873db886a5SBin Meng } 3883db886a5SBin Meng #endif 3893db886a5SBin Meng 39012e431b2SSimon Glass if (addr == FDT_ADDR_T_NONE) 39112e431b2SSimon Glass return -EINVAL; 39212e431b2SSimon Glass 393df8ec55dSPaul Burton #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 394167efe01SSimon Glass plat->base = addr; 395df8ec55dSPaul Burton #else 396df8ec55dSPaul Burton plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE); 397df8ec55dSPaul Burton #endif 398df8ec55dSPaul Burton 39959b35dddSMichal Simek plat->reg_offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 40059b35dddSMichal Simek "reg-offset", 0); 40112e431b2SSimon Glass plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 40280e06146SThomas Chou "reg-shift", 0); 40350fce1d5SPaul Burton 40450fce1d5SPaul Burton err = clk_get_by_index(dev, 0, &clk); 40550fce1d5SPaul Burton if (!err) { 40650fce1d5SPaul Burton err = clk_get_rate(&clk); 40750fce1d5SPaul Burton if (!IS_ERR_VALUE(err)) 40850fce1d5SPaul Burton plat->clock = err; 409ab895d6aSAlexandre Courbot } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) { 41050fce1d5SPaul Burton debug("ns16550 failed to get clock\n"); 41150fce1d5SPaul Burton return err; 41250fce1d5SPaul Burton } 41350fce1d5SPaul Burton 41450fce1d5SPaul Burton if (!plat->clock) 4158e62d32eSThomas Chou plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 4168e62d32eSThomas Chou "clock-frequency", 4178e62d32eSThomas Chou CONFIG_SYS_NS16550_CLK); 4188e62d32eSThomas Chou if (!plat->clock) { 4198e62d32eSThomas Chou debug("ns16550 clock not defined\n"); 4208e62d32eSThomas Chou return -EINVAL; 4218e62d32eSThomas Chou } 42212e431b2SSimon Glass 42312e431b2SSimon Glass return 0; 42412e431b2SSimon Glass } 42511c1a878SSimon Glass #endif 42612e431b2SSimon Glass 42712e431b2SSimon Glass const struct dm_serial_ops ns16550_serial_ops = { 42812e431b2SSimon Glass .putc = ns16550_serial_putc, 42912e431b2SSimon Glass .pending = ns16550_serial_pending, 43012e431b2SSimon Glass .getc = ns16550_serial_getc, 43112e431b2SSimon Glass .setbrg = ns16550_serial_setbrg, 43212e431b2SSimon Glass }; 4338e62d32eSThomas Chou 434b2927fbaSSimon Glass #if !CONFIG_IS_ENABLED(OF_PLATDATA) 4358e62d32eSThomas Chou #if CONFIG_IS_ENABLED(OF_CONTROL) 436cc4228f9SThomas Chou /* 437cc4228f9SThomas Chou * Please consider existing compatible strings before adding a new 438cc4228f9SThomas Chou * one to keep this table compact. Or you may add a generic "ns16550" 439cc4228f9SThomas Chou * compatible string to your dts. 440cc4228f9SThomas Chou */ 4418e62d32eSThomas Chou static const struct udevice_id ns16550_serial_ids[] = { 4428e62d32eSThomas Chou { .compatible = "ns16550" }, 4438e62d32eSThomas Chou { .compatible = "ns16550a" }, 4448e62d32eSThomas Chou { .compatible = "nvidia,tegra20-uart" }, 4458e62d32eSThomas Chou { .compatible = "snps,dw-apb-uart" }, 4468e62d32eSThomas Chou { .compatible = "ti,omap2-uart" }, 4478e62d32eSThomas Chou { .compatible = "ti,omap3-uart" }, 4488e62d32eSThomas Chou { .compatible = "ti,omap4-uart" }, 4498e62d32eSThomas Chou { .compatible = "ti,am3352-uart" }, 4508e62d32eSThomas Chou { .compatible = "ti,am4372-uart" }, 4518e62d32eSThomas Chou { .compatible = "ti,dra742-uart" }, 4528e62d32eSThomas Chou {} 4538e62d32eSThomas Chou }; 4548e62d32eSThomas Chou #endif 4558e62d32eSThomas Chou 456b7e29834SSimon Glass #if CONFIG_IS_ENABLED(SERIAL_PRESENT) 4578e62d32eSThomas Chou U_BOOT_DRIVER(ns16550_serial) = { 4588e62d32eSThomas Chou .name = "ns16550_serial", 4598e62d32eSThomas Chou .id = UCLASS_SERIAL, 4608e62d32eSThomas Chou #if CONFIG_IS_ENABLED(OF_CONTROL) 4618e62d32eSThomas Chou .of_match = ns16550_serial_ids, 4628e62d32eSThomas Chou .ofdata_to_platdata = ns16550_serial_ofdata_to_platdata, 4638e62d32eSThomas Chou .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), 4648e62d32eSThomas Chou #endif 4658e62d32eSThomas Chou .priv_auto_alloc_size = sizeof(struct NS16550), 4668e62d32eSThomas Chou .probe = ns16550_serial_probe, 4678e62d32eSThomas Chou .ops = &ns16550_serial_ops, 468b7e5a643SSimon Glass .flags = DM_FLAG_PRE_RELOC, 4698e62d32eSThomas Chou }; 470b7e29834SSimon Glass #endif 471b2927fbaSSimon Glass #endif /* !OF_PLATDATA */ 47212e431b2SSimon Glass #endif /* CONFIG_DM_SERIAL */ 473