ns16550.c 11 KB

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