ns16550.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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 <clk.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. 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. static inline void serial_out_shift(void *addr, int shift, int value)
  52. {
  53. #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
  54. outb(value, (ulong)addr);
  55. #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN)
  56. out_le32(addr, value);
  57. #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN)
  58. out_be32(addr, value);
  59. #elif defined(CONFIG_SYS_NS16550_MEM32)
  60. writel(value, addr);
  61. #elif defined(CONFIG_SYS_BIG_ENDIAN)
  62. writeb(value, addr + (1 << shift) - 1);
  63. #else
  64. writeb(value, addr);
  65. #endif
  66. }
  67. static inline int serial_in_shift(void *addr, int shift)
  68. {
  69. #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
  70. return inb((ulong)addr);
  71. #elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN)
  72. return in_le32(addr);
  73. #elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN)
  74. return in_be32(addr);
  75. #elif defined(CONFIG_SYS_NS16550_MEM32)
  76. return readl(addr);
  77. #elif defined(CONFIG_SYS_BIG_ENDIAN)
  78. return readb(addr + (1 << shift) - 1);
  79. #else
  80. return readb(addr);
  81. #endif
  82. }
  83. #ifdef CONFIG_DM_SERIAL
  84. #ifndef CONFIG_SYS_NS16550_CLK
  85. #define CONFIG_SYS_NS16550_CLK 0
  86. #endif
  87. static void ns16550_writeb(NS16550_t port, int offset, int value)
  88. {
  89. struct ns16550_platdata *plat = port->plat;
  90. unsigned char *addr;
  91. offset *= 1 << plat->reg_shift;
  92. addr = (unsigned char *)plat->base + offset;
  93. /*
  94. * As far as we know it doesn't make sense to support selection of
  95. * these options at run-time, so use the existing CONFIG options.
  96. */
  97. serial_out_shift(addr + plat->reg_offset, plat->reg_shift, value);
  98. }
  99. static int ns16550_readb(NS16550_t port, int offset)
  100. {
  101. struct ns16550_platdata *plat = port->plat;
  102. unsigned char *addr;
  103. offset *= 1 << plat->reg_shift;
  104. addr = (unsigned char *)plat->base + offset;
  105. return serial_in_shift(addr + plat->reg_offset, plat->reg_shift);
  106. }
  107. static u32 ns16550_getfcr(NS16550_t port)
  108. {
  109. struct ns16550_platdata *plat = port->plat;
  110. return plat->fcr;
  111. }
  112. /* We can clean these up once everything is moved to driver model */
  113. #define serial_out(value, addr) \
  114. ns16550_writeb(com_port, \
  115. (unsigned char *)addr - (unsigned char *)com_port, value)
  116. #define serial_in(addr) \
  117. ns16550_readb(com_port, \
  118. (unsigned char *)addr - (unsigned char *)com_port)
  119. #else
  120. static u32 ns16550_getfcr(NS16550_t port)
  121. {
  122. return UART_FCRVAL;
  123. }
  124. #endif
  125. int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate)
  126. {
  127. const unsigned int mode_x_div = 16;
  128. return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate);
  129. }
  130. static void NS16550_setbrg(NS16550_t com_port, int baud_divisor)
  131. {
  132. serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
  133. serial_out(baud_divisor & 0xff, &com_port->dll);
  134. serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
  135. serial_out(UART_LCRVAL, &com_port->lcr);
  136. }
  137. void NS16550_init(NS16550_t com_port, int baud_divisor)
  138. {
  139. #if (defined(CONFIG_SPL_BUILD) && \
  140. (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX)))
  141. /*
  142. * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode
  143. * before SPL starts only THRE bit is set. We have to empty the
  144. * transmitter before initialization starts.
  145. */
  146. if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
  147. == UART_LSR_THRE) {
  148. if (baud_divisor != -1)
  149. NS16550_setbrg(com_port, baud_divisor);
  150. serial_out(0, &com_port->mdr1);
  151. }
  152. #endif
  153. while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT))
  154. ;
  155. serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
  156. #if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \
  157. defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
  158. serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/
  159. #endif
  160. serial_out(UART_MCRVAL, &com_port->mcr);
  161. serial_out(ns16550_getfcr(com_port), &com_port->fcr);
  162. if (baud_divisor != -1)
  163. NS16550_setbrg(com_port, baud_divisor);
  164. #if defined(CONFIG_OMAP) || \
  165. defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
  166. defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
  167. /* /16 is proper to hit 115200 with 48MHz */
  168. serial_out(0, &com_port->mdr1);
  169. #endif /* CONFIG_OMAP */
  170. #if defined(CONFIG_SOC_KEYSTONE)
  171. serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC);
  172. #endif
  173. }
  174. #ifndef CONFIG_NS16550_MIN_FUNCTIONS
  175. void NS16550_reinit(NS16550_t com_port, int baud_divisor)
  176. {
  177. serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
  178. NS16550_setbrg(com_port, 0);
  179. serial_out(UART_MCRVAL, &com_port->mcr);
  180. serial_out(ns16550_getfcr(com_port), &com_port->fcr);
  181. NS16550_setbrg(com_port, baud_divisor);
  182. }
  183. #endif /* CONFIG_NS16550_MIN_FUNCTIONS */
  184. void NS16550_putc(NS16550_t com_port, char c)
  185. {
  186. while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0)
  187. ;
  188. serial_out(c, &com_port->thr);
  189. /*
  190. * Call watchdog_reset() upon newline. This is done here in putc
  191. * since the environment code uses a single puts() to print the complete
  192. * environment upon "printenv". So we can't put this watchdog call
  193. * in puts().
  194. */
  195. if (c == '\n')
  196. WATCHDOG_RESET();
  197. }
  198. #ifndef CONFIG_NS16550_MIN_FUNCTIONS
  199. char NS16550_getc(NS16550_t com_port)
  200. {
  201. while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) {
  202. #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY)
  203. extern void usbtty_poll(void);
  204. usbtty_poll();
  205. #endif
  206. WATCHDOG_RESET();
  207. }
  208. return serial_in(&com_port->rbr);
  209. }
  210. int NS16550_tstc(NS16550_t com_port)
  211. {
  212. return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0;
  213. }
  214. #endif /* CONFIG_NS16550_MIN_FUNCTIONS */
  215. #ifdef CONFIG_DEBUG_UART_NS16550
  216. #include <debug_uart.h>
  217. #define serial_dout(reg, value) \
  218. serial_out_shift((char *)com_port + \
  219. ((char *)reg - (char *)com_port) * \
  220. (1 << CONFIG_DEBUG_UART_SHIFT), \
  221. CONFIG_DEBUG_UART_SHIFT, value)
  222. #define serial_din(reg) \
  223. serial_in_shift((char *)com_port + \
  224. ((char *)reg - (char *)com_port) * \
  225. (1 << CONFIG_DEBUG_UART_SHIFT), \
  226. CONFIG_DEBUG_UART_SHIFT)
  227. static inline void _debug_uart_init(void)
  228. {
  229. struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
  230. int baud_divisor;
  231. /*
  232. * We copy the code from above because it is already horribly messy.
  233. * Trying to refactor to nicely remove the duplication doesn't seem
  234. * feasible. The better fix is to move all users of this driver to
  235. * driver model.
  236. */
  237. baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK,
  238. CONFIG_BAUDRATE);
  239. serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER);
  240. serial_dout(&com_port->mcr, UART_MCRVAL);
  241. serial_dout(&com_port->fcr, UART_FCRVAL);
  242. serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL);
  243. serial_dout(&com_port->dll, baud_divisor & 0xff);
  244. serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff);
  245. serial_dout(&com_port->lcr, UART_LCRVAL);
  246. }
  247. static inline void _debug_uart_putc(int ch)
  248. {
  249. struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
  250. while (!(serial_din(&com_port->lsr) & UART_LSR_THRE))
  251. ;
  252. serial_dout(&com_port->thr, ch);
  253. }
  254. DEBUG_UART_FUNCS
  255. #endif
  256. #ifdef CONFIG_DM_SERIAL
  257. static int ns16550_serial_putc(struct udevice *dev, const char ch)
  258. {
  259. struct NS16550 *const com_port = dev_get_priv(dev);
  260. if (!(serial_in(&com_port->lsr) & UART_LSR_THRE))
  261. return -EAGAIN;
  262. serial_out(ch, &com_port->thr);
  263. /*
  264. * Call watchdog_reset() upon newline. This is done here in putc
  265. * since the environment code uses a single puts() to print the complete
  266. * environment upon "printenv". So we can't put this watchdog call
  267. * in puts().
  268. */
  269. if (ch == '\n')
  270. WATCHDOG_RESET();
  271. return 0;
  272. }
  273. static int ns16550_serial_pending(struct udevice *dev, bool input)
  274. {
  275. struct NS16550 *const com_port = dev_get_priv(dev);
  276. if (input)
  277. return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0;
  278. else
  279. return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1;
  280. }
  281. static int ns16550_serial_getc(struct udevice *dev)
  282. {
  283. struct NS16550 *const com_port = dev_get_priv(dev);
  284. if (!(serial_in(&com_port->lsr) & UART_LSR_DR))
  285. return -EAGAIN;
  286. return serial_in(&com_port->rbr);
  287. }
  288. static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
  289. {
  290. struct NS16550 *const com_port = dev_get_priv(dev);
  291. struct ns16550_platdata *plat = com_port->plat;
  292. int clock_divisor;
  293. clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate);
  294. NS16550_setbrg(com_port, clock_divisor);
  295. return 0;
  296. }
  297. int ns16550_serial_probe(struct udevice *dev)
  298. {
  299. struct NS16550 *const com_port = dev_get_priv(dev);
  300. com_port->plat = dev_get_platdata(dev);
  301. NS16550_init(com_port, -1);
  302. return 0;
  303. }
  304. #if CONFIG_IS_ENABLED(OF_CONTROL)
  305. enum {
  306. PORT_NS16550 = 0,
  307. PORT_JZ4780,
  308. };
  309. #endif
  310. #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
  311. int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
  312. {
  313. struct ns16550_platdata *plat = dev->platdata;
  314. const u32 port_type = dev_get_driver_data(dev);
  315. fdt_addr_t addr;
  316. struct clk clk;
  317. int err;
  318. /* try Processor Local Bus device first */
  319. addr = dev_get_addr(dev);
  320. #if defined(CONFIG_PCI) && defined(CONFIG_DM_PCI)
  321. if (addr == FDT_ADDR_T_NONE) {
  322. /* then try pci device */
  323. struct fdt_pci_addr pci_addr;
  324. u32 bar;
  325. int ret;
  326. /* we prefer to use a memory-mapped register */
  327. ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset,
  328. FDT_PCI_SPACE_MEM32, "reg",
  329. &pci_addr);
  330. if (ret) {
  331. /* try if there is any i/o-mapped register */
  332. ret = fdtdec_get_pci_addr(gd->fdt_blob,
  333. dev->of_offset,
  334. FDT_PCI_SPACE_IO,
  335. "reg", &pci_addr);
  336. if (ret)
  337. return ret;
  338. }
  339. ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar);
  340. if (ret)
  341. return ret;
  342. addr = bar;
  343. }
  344. #endif
  345. if (addr == FDT_ADDR_T_NONE)
  346. return -EINVAL;
  347. #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
  348. plat->base = addr;
  349. #else
  350. plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE);
  351. #endif
  352. plat->reg_offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
  353. "reg-offset", 0);
  354. plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
  355. "reg-shift", 0);
  356. err = clk_get_by_index(dev, 0, &clk);
  357. if (!err) {
  358. err = clk_get_rate(&clk);
  359. if (!IS_ERR_VALUE(err))
  360. plat->clock = err;
  361. } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
  362. debug("ns16550 failed to get clock\n");
  363. return err;
  364. }
  365. if (!plat->clock)
  366. plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
  367. "clock-frequency",
  368. CONFIG_SYS_NS16550_CLK);
  369. if (!plat->clock) {
  370. debug("ns16550 clock not defined\n");
  371. return -EINVAL;
  372. }
  373. plat->fcr = UART_FCRVAL;
  374. if (port_type == PORT_JZ4780)
  375. plat->fcr |= UART_FCR_UME;
  376. return 0;
  377. }
  378. #endif
  379. const struct dm_serial_ops ns16550_serial_ops = {
  380. .putc = ns16550_serial_putc,
  381. .pending = ns16550_serial_pending,
  382. .getc = ns16550_serial_getc,
  383. .setbrg = ns16550_serial_setbrg,
  384. };
  385. #if !CONFIG_IS_ENABLED(OF_PLATDATA)
  386. #if CONFIG_IS_ENABLED(OF_CONTROL)
  387. /*
  388. * Please consider existing compatible strings before adding a new
  389. * one to keep this table compact. Or you may add a generic "ns16550"
  390. * compatible string to your dts.
  391. */
  392. static const struct udevice_id ns16550_serial_ids[] = {
  393. { .compatible = "ns16550", .data = PORT_NS16550 },
  394. { .compatible = "ns16550a", .data = PORT_NS16550 },
  395. { .compatible = "ingenic,jz4780-uart", .data = PORT_JZ4780 },
  396. { .compatible = "nvidia,tegra20-uart", .data = PORT_NS16550 },
  397. { .compatible = "snps,dw-apb-uart", .data = PORT_NS16550 },
  398. { .compatible = "ti,omap2-uart", .data = PORT_NS16550 },
  399. { .compatible = "ti,omap3-uart", .data = PORT_NS16550 },
  400. { .compatible = "ti,omap4-uart", .data = PORT_NS16550 },
  401. { .compatible = "ti,am3352-uart", .data = PORT_NS16550 },
  402. { .compatible = "ti,am4372-uart", .data = PORT_NS16550 },
  403. { .compatible = "ti,dra742-uart", .data = PORT_NS16550 },
  404. {}
  405. };
  406. #endif
  407. #if CONFIG_IS_ENABLED(SERIAL_PRESENT)
  408. U_BOOT_DRIVER(ns16550_serial) = {
  409. .name = "ns16550_serial",
  410. .id = UCLASS_SERIAL,
  411. #if CONFIG_IS_ENABLED(OF_CONTROL)
  412. .of_match = ns16550_serial_ids,
  413. .ofdata_to_platdata = ns16550_serial_ofdata_to_platdata,
  414. .platdata_auto_alloc_size = sizeof(struct ns16550_platdata),
  415. #endif
  416. .priv_auto_alloc_size = sizeof(struct NS16550),
  417. .probe = ns16550_serial_probe,
  418. .ops = &ns16550_serial_ops,
  419. .flags = DM_FLAG_PRE_RELOC,
  420. };
  421. #endif
  422. #endif /* !OF_PLATDATA */
  423. #endif /* CONFIG_DM_SERIAL */