ns16550.c 14 KB

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