ns16550.c 9.2 KB

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