ns16550.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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 = plat->base + 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, 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 = plat->base + offset;
  79. #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
  80. return inb(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) && defined(CONFIG_OMAP34XX))
  120. /*
  121. * On some OMAP3 devices when UART3 is configured for boot mode before
  122. * SPL starts only THRE bit is set. We have to empty the transmitter
  123. * before initialization starts.
  124. */
  125. if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
  126. == UART_LSR_THRE) {
  127. if (baud_divisor != -1)
  128. NS16550_setbrg(com_port, baud_divisor);
  129. serial_out(0, &com_port->mdr1);
  130. }
  131. #endif
  132. while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT))
  133. ;
  134. serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
  135. #if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \
  136. defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
  137. serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/
  138. #endif
  139. NS16550_setbrg(com_port, 0);
  140. serial_out(UART_MCRVAL, &com_port->mcr);
  141. serial_out(UART_FCRVAL, &com_port->fcr);
  142. if (baud_divisor != -1)
  143. NS16550_setbrg(com_port, baud_divisor);
  144. #if defined(CONFIG_OMAP) || \
  145. defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
  146. defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
  147. /* /16 is proper to hit 115200 with 48MHz */
  148. serial_out(0, &com_port->mdr1);
  149. #endif /* CONFIG_OMAP */
  150. #if defined(CONFIG_SOC_KEYSTONE)
  151. serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC);
  152. #endif
  153. }
  154. #ifndef CONFIG_NS16550_MIN_FUNCTIONS
  155. void NS16550_reinit(NS16550_t com_port, int baud_divisor)
  156. {
  157. serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
  158. NS16550_setbrg(com_port, 0);
  159. serial_out(UART_MCRVAL, &com_port->mcr);
  160. serial_out(UART_FCRVAL, &com_port->fcr);
  161. NS16550_setbrg(com_port, baud_divisor);
  162. }
  163. #endif /* CONFIG_NS16550_MIN_FUNCTIONS */
  164. void NS16550_putc(NS16550_t com_port, char c)
  165. {
  166. while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0)
  167. ;
  168. serial_out(c, &com_port->thr);
  169. /*
  170. * Call watchdog_reset() upon newline. This is done here in putc
  171. * since the environment code uses a single puts() to print the complete
  172. * environment upon "printenv". So we can't put this watchdog call
  173. * in puts().
  174. */
  175. if (c == '\n')
  176. WATCHDOG_RESET();
  177. }
  178. #ifndef CONFIG_NS16550_MIN_FUNCTIONS
  179. char NS16550_getc(NS16550_t com_port)
  180. {
  181. while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) {
  182. #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY)
  183. extern void usbtty_poll(void);
  184. usbtty_poll();
  185. #endif
  186. WATCHDOG_RESET();
  187. }
  188. return serial_in(&com_port->rbr);
  189. }
  190. int NS16550_tstc(NS16550_t com_port)
  191. {
  192. return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0;
  193. }
  194. #endif /* CONFIG_NS16550_MIN_FUNCTIONS */
  195. #ifdef CONFIG_DM_SERIAL
  196. static int ns16550_serial_putc(struct udevice *dev, const char ch)
  197. {
  198. struct NS16550 *const com_port = dev_get_priv(dev);
  199. if (!(serial_in(&com_port->lsr) & UART_LSR_THRE))
  200. return -EAGAIN;
  201. serial_out(ch, &com_port->thr);
  202. /*
  203. * Call watchdog_reset() upon newline. This is done here in putc
  204. * since the environment code uses a single puts() to print the complete
  205. * environment upon "printenv". So we can't put this watchdog call
  206. * in puts().
  207. */
  208. if (ch == '\n')
  209. WATCHDOG_RESET();
  210. return 0;
  211. }
  212. static int ns16550_serial_pending(struct udevice *dev, bool input)
  213. {
  214. struct NS16550 *const com_port = dev_get_priv(dev);
  215. if (input)
  216. return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
  217. else
  218. return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1;
  219. }
  220. static int ns16550_serial_getc(struct udevice *dev)
  221. {
  222. struct NS16550 *const com_port = dev_get_priv(dev);
  223. if (!serial_in(&com_port->lsr) & UART_LSR_DR)
  224. return -EAGAIN;
  225. return serial_in(&com_port->rbr);
  226. }
  227. static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
  228. {
  229. struct NS16550 *const com_port = dev_get_priv(dev);
  230. struct ns16550_platdata *plat = com_port->plat;
  231. int clock_divisor;
  232. clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate);
  233. NS16550_setbrg(com_port, clock_divisor);
  234. return 0;
  235. }
  236. int ns16550_serial_probe(struct udevice *dev)
  237. {
  238. struct NS16550 *const com_port = dev_get_priv(dev);
  239. NS16550_init(com_port, -1);
  240. return 0;
  241. }
  242. int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
  243. {
  244. struct NS16550 *const com_port = dev_get_priv(dev);
  245. struct ns16550_platdata *plat = dev->platdata;
  246. fdt_addr_t addr;
  247. addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
  248. if (addr == FDT_ADDR_T_NONE)
  249. return -EINVAL;
  250. plat->base = (unsigned char *)addr;
  251. plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
  252. "reg-shift", 1);
  253. com_port->plat = plat;
  254. return 0;
  255. }
  256. const struct dm_serial_ops ns16550_serial_ops = {
  257. .putc = ns16550_serial_putc,
  258. .pending = ns16550_serial_pending,
  259. .getc = ns16550_serial_getc,
  260. .setbrg = ns16550_serial_setbrg,
  261. };
  262. #endif /* CONFIG_DM_SERIAL */