1 /* 2 * COM1 NS16550 support 3 * originally from linux source (arch/powerpc/boot/ns16550.c) 4 * modified to use CONFIG_SYS_ISA_MEM and new defines 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <errno.h> 10 #include <fdtdec.h> 11 #include <ns16550.h> 12 #include <serial.h> 13 #include <watchdog.h> 14 #include <linux/types.h> 15 #include <asm/io.h> 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 #define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */ 20 #define UART_MCRVAL (UART_MCR_DTR | \ 21 UART_MCR_RTS) /* RTS/DTR */ 22 #define UART_FCRVAL (UART_FCR_FIFO_EN | \ 23 UART_FCR_RXSR | \ 24 UART_FCR_TXSR) /* Clear & enable FIFOs */ 25 26 #ifndef CONFIG_DM_SERIAL 27 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 28 #define serial_out(x, y) outb(x, (ulong)y) 29 #define serial_in(y) inb((ulong)y) 30 #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE > 0) 31 #define serial_out(x, y) out_be32(y, x) 32 #define serial_in(y) in_be32(y) 33 #elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE < 0) 34 #define serial_out(x, y) out_le32(y, x) 35 #define serial_in(y) in_le32(y) 36 #else 37 #define serial_out(x, y) writeb(x, y) 38 #define serial_in(y) readb(y) 39 #endif 40 #endif /* !CONFIG_DM_SERIAL */ 41 42 #if defined(CONFIG_SOC_KEYSTONE) 43 #define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 44 #define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0)) 45 #undef UART_MCRVAL 46 #ifdef CONFIG_SERIAL_HW_FLOW_CONTROL 47 #define UART_MCRVAL (UART_MCR_RTS | UART_MCR_AFE) 48 #else 49 #define UART_MCRVAL (UART_MCR_RTS) 50 #endif 51 #endif 52 53 #ifndef CONFIG_SYS_NS16550_IER 54 #define CONFIG_SYS_NS16550_IER 0x00 55 #endif /* CONFIG_SYS_NS16550_IER */ 56 57 #ifdef CONFIG_DM_SERIAL 58 59 static inline void serial_out_shift(unsigned char *addr, int shift, int value) 60 { 61 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 62 outb(value, (ulong)addr); 63 #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 64 out_le32(addr, value); 65 #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 66 out_be32(addr, value); 67 #elif defined(CONFIG_SYS_BIG_ENDIAN) 68 writeb(value, addr + (1 << shift) - 1); 69 #else 70 writeb(value, addr); 71 #endif 72 } 73 74 static inline int serial_in_shift(unsigned char *addr, int shift) 75 { 76 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED 77 return inb((ulong)addr); 78 #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) 79 return in_le32(addr); 80 #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) 81 return in_be32(addr); 82 #elif defined(CONFIG_SYS_BIG_ENDIAN) 83 return readb(addr + (1 << reg_shift) - 1); 84 #else 85 return readb(addr); 86 #endif 87 } 88 89 static void ns16550_writeb(NS16550_t port, int offset, int value) 90 { 91 struct ns16550_platdata *plat = port->plat; 92 unsigned char *addr; 93 94 offset *= 1 << plat->reg_shift; 95 addr = map_sysmem(plat->base, 0) + offset; 96 /* 97 * As far as we know it doesn't make sense to support selection of 98 * these options at run-time, so use the existing CONFIG options. 99 */ 100 serial_out_shift(addr, plat->reg_shift, value); 101 } 102 103 static int ns16550_readb(NS16550_t port, int offset) 104 { 105 struct ns16550_platdata *plat = port->plat; 106 unsigned char *addr; 107 108 offset *= 1 << plat->reg_shift; 109 addr = map_sysmem(plat->base, 0) + offset; 110 111 return serial_in_shift(addr, plat->reg_shift); 112 } 113 114 /* We can clean these up once everything is moved to driver model */ 115 #define serial_out(value, addr) \ 116 ns16550_writeb(com_port, addr - (unsigned char *)com_port, value) 117 #define serial_in(addr) \ 118 ns16550_readb(com_port, addr - (unsigned char *)com_port) 119 #endif 120 121 static inline int calc_divisor(NS16550_t port, int clock, int baudrate) 122 { 123 const unsigned int mode_x_div = 16; 124 125 return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); 126 } 127 128 int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) 129 { 130 #ifdef CONFIG_OMAP1510 131 /* If can't cleanly clock 115200 set div to 1 */ 132 if ((clock == 12000000) && (baudrate == 115200)) { 133 port->osc_12m_sel = OSC_12M_SEL; /* enable 6.5 * divisor */ 134 return 1; /* return 1 for base divisor */ 135 } 136 port->osc_12m_sel = 0; /* clear if previsouly set */ 137 #endif 138 139 return calc_divisor(port, clock, baudrate); 140 } 141 142 static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) 143 { 144 serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); 145 serial_out(baud_divisor & 0xff, &com_port->dll); 146 serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); 147 serial_out(UART_LCRVAL, &com_port->lcr); 148 } 149 150 void NS16550_init(NS16550_t com_port, int baud_divisor) 151 { 152 #if (defined(CONFIG_SPL_BUILD) && \ 153 (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) 154 /* 155 * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode 156 * before SPL starts only THRE bit is set. We have to empty the 157 * transmitter before initialization starts. 158 */ 159 if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) 160 == UART_LSR_THRE) { 161 if (baud_divisor != -1) 162 NS16550_setbrg(com_port, baud_divisor); 163 serial_out(0, &com_port->mdr1); 164 } 165 #endif 166 167 while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT)) 168 ; 169 170 serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 171 #if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \ 172 defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 173 serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ 174 #endif 175 NS16550_setbrg(com_port, 0); 176 serial_out(UART_MCRVAL, &com_port->mcr); 177 serial_out(UART_FCRVAL, &com_port->fcr); 178 if (baud_divisor != -1) 179 NS16550_setbrg(com_port, baud_divisor); 180 #if defined(CONFIG_OMAP) || \ 181 defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ 182 defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) 183 184 /* /16 is proper to hit 115200 with 48MHz */ 185 serial_out(0, &com_port->mdr1); 186 #endif /* CONFIG_OMAP */ 187 #if defined(CONFIG_SOC_KEYSTONE) 188 serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC); 189 #endif 190 } 191 192 #ifndef CONFIG_NS16550_MIN_FUNCTIONS 193 void NS16550_reinit(NS16550_t com_port, int baud_divisor) 194 { 195 serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); 196 NS16550_setbrg(com_port, 0); 197 serial_out(UART_MCRVAL, &com_port->mcr); 198 serial_out(UART_FCRVAL, &com_port->fcr); 199 NS16550_setbrg(com_port, baud_divisor); 200 } 201 #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 202 203 void NS16550_putc(NS16550_t com_port, char c) 204 { 205 while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0) 206 ; 207 serial_out(c, &com_port->thr); 208 209 /* 210 * Call watchdog_reset() upon newline. This is done here in putc 211 * since the environment code uses a single puts() to print the complete 212 * environment upon "printenv". So we can't put this watchdog call 213 * in puts(). 214 */ 215 if (c == '\n') 216 WATCHDOG_RESET(); 217 } 218 219 #ifndef CONFIG_NS16550_MIN_FUNCTIONS 220 char NS16550_getc(NS16550_t com_port) 221 { 222 while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { 223 #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY) 224 extern void usbtty_poll(void); 225 usbtty_poll(); 226 #endif 227 WATCHDOG_RESET(); 228 } 229 return serial_in(&com_port->rbr); 230 } 231 232 int NS16550_tstc(NS16550_t com_port) 233 { 234 return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0; 235 } 236 237 #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ 238 239 #ifdef CONFIG_DEBUG_UART_NS16550 240 241 #include <debug_uart.h> 242 243 void debug_uart_init(void) 244 { 245 struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 246 int baud_divisor; 247 248 /* 249 * We copy the code from above because it is already horribly messy. 250 * Trying to refactor to nicely remove the duplication doesn't seem 251 * feasible. The better fix is to move all users of this driver to 252 * driver model. 253 */ 254 baud_divisor = calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, 255 CONFIG_BAUDRATE); 256 257 serial_out_shift(&com_port->ier, 0, CONFIG_SYS_NS16550_IER); 258 serial_out_shift(&com_port->mcr, 0, UART_MCRVAL); 259 serial_out_shift(&com_port->fcr, 0, UART_FCRVAL); 260 261 serial_out_shift(&com_port->lcr, 0, UART_LCR_BKSE | UART_LCRVAL); 262 serial_out_shift(&com_port->dll, 0, baud_divisor & 0xff); 263 serial_out_shift(&com_port->dlm, 0, (baud_divisor >> 8) & 0xff); 264 serial_out_shift(&com_port->lcr, 0, UART_LCRVAL); 265 } 266 267 static inline void _debug_uart_putc(int ch) 268 { 269 struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; 270 271 while (!(serial_in_shift(&com_port->lsr, 0) & UART_LSR_THRE)) 272 ; 273 serial_out_shift(&com_port->thr, 0, ch); 274 } 275 276 DEBUG_UART_FUNCS 277 278 #endif 279 280 #ifdef CONFIG_DM_SERIAL 281 static int ns16550_serial_putc(struct udevice *dev, const char ch) 282 { 283 struct NS16550 *const com_port = dev_get_priv(dev); 284 285 if (!(serial_in(&com_port->lsr) & UART_LSR_THRE)) 286 return -EAGAIN; 287 serial_out(ch, &com_port->thr); 288 289 /* 290 * Call watchdog_reset() upon newline. This is done here in putc 291 * since the environment code uses a single puts() to print the complete 292 * environment upon "printenv". So we can't put this watchdog call 293 * in puts(). 294 */ 295 if (ch == '\n') 296 WATCHDOG_RESET(); 297 298 return 0; 299 } 300 301 static int ns16550_serial_pending(struct udevice *dev, bool input) 302 { 303 struct NS16550 *const com_port = dev_get_priv(dev); 304 305 if (input) 306 return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0; 307 else 308 return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; 309 } 310 311 static int ns16550_serial_getc(struct udevice *dev) 312 { 313 struct NS16550 *const com_port = dev_get_priv(dev); 314 315 if (!(serial_in(&com_port->lsr) & UART_LSR_DR)) 316 return -EAGAIN; 317 318 return serial_in(&com_port->rbr); 319 } 320 321 static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) 322 { 323 struct NS16550 *const com_port = dev_get_priv(dev); 324 struct ns16550_platdata *plat = com_port->plat; 325 int clock_divisor; 326 327 clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); 328 329 NS16550_setbrg(com_port, clock_divisor); 330 331 return 0; 332 } 333 334 int ns16550_serial_probe(struct udevice *dev) 335 { 336 struct NS16550 *const com_port = dev_get_priv(dev); 337 338 com_port->plat = dev_get_platdata(dev); 339 NS16550_init(com_port, -1); 340 341 return 0; 342 } 343 344 #ifdef CONFIG_OF_CONTROL 345 int ns16550_serial_ofdata_to_platdata(struct udevice *dev) 346 { 347 struct ns16550_platdata *plat = dev->platdata; 348 fdt_addr_t addr; 349 350 /* try Processor Local Bus device first */ 351 addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); 352 #ifdef CONFIG_PCI 353 if (addr == FDT_ADDR_T_NONE) { 354 /* then try pci device */ 355 struct fdt_pci_addr pci_addr; 356 u32 bar; 357 int ret; 358 359 /* we prefer to use a memory-mapped register */ 360 ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset, 361 FDT_PCI_SPACE_MEM32, "reg", 362 &pci_addr); 363 if (ret) { 364 /* try if there is any i/o-mapped register */ 365 ret = fdtdec_get_pci_addr(gd->fdt_blob, 366 dev->of_offset, 367 FDT_PCI_SPACE_IO, 368 "reg", &pci_addr); 369 if (ret) 370 return ret; 371 } 372 373 ret = fdtdec_get_pci_bar32(gd->fdt_blob, dev->of_offset, 374 &pci_addr, &bar); 375 if (ret) 376 return ret; 377 378 addr = bar; 379 } 380 #endif 381 382 if (addr == FDT_ADDR_T_NONE) 383 return -EINVAL; 384 385 plat->base = addr; 386 plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 387 "reg-shift", 1); 388 389 return 0; 390 } 391 #endif 392 393 const struct dm_serial_ops ns16550_serial_ops = { 394 .putc = ns16550_serial_putc, 395 .pending = ns16550_serial_pending, 396 .getc = ns16550_serial_getc, 397 .setbrg = ns16550_serial_setbrg, 398 }; 399 #endif /* CONFIG_DM_SERIAL */ 400