uart8250.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2019 Western Digital Corporation or its affiliates.
  5. *
  6. * Authors:
  7. * Anup Patel <anup.patel@wdc.com>
  8. */
  9. #include <sbi/riscv_io.h>
  10. #include <sbi/sbi_console.h>
  11. #include <sbi_utils/serial/uart8250.h>
  12. /* clang-format off */
  13. #define UART_RBR_OFFSET 0 /* In: Recieve Buffer Register */
  14. #define UART_THR_OFFSET 0 /* Out: Transmitter Holding Register */
  15. #define UART_DLL_OFFSET 0 /* Out: Divisor Latch Low */
  16. #define UART_IER_OFFSET 1 /* I/O: Interrupt Enable Register */
  17. #define UART_DLM_OFFSET 1 /* Out: Divisor Latch High */
  18. #define UART_FCR_OFFSET 2 /* Out: FIFO Control Register */
  19. #define UART_IIR_OFFSET 2 /* I/O: Interrupt Identification Register */
  20. #define UART_LCR_OFFSET 3 /* Out: Line Control Register */
  21. #define UART_MCR_OFFSET 4 /* Out: Modem Control Register */
  22. #define UART_LSR_OFFSET 5 /* In: Line Status Register */
  23. #define UART_MSR_OFFSET 6 /* In: Modem Status Register */
  24. #define UART_SCR_OFFSET 7 /* I/O: Scratch Register */
  25. #define UART_MDR1_OFFSET 8 /* I/O: Mode Register */
  26. #define UART_LSR_FIFOE 0x80 /* Fifo error */
  27. #define UART_LSR_TEMT 0x40 /* Transmitter empty */
  28. #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
  29. #define UART_LSR_BI 0x10 /* Break interrupt indicator */
  30. #define UART_LSR_FE 0x08 /* Frame error indicator */
  31. #define UART_LSR_PE 0x04 /* Parity error indicator */
  32. #define UART_LSR_OE 0x02 /* Overrun error indicator */
  33. #define UART_LSR_DR 0x01 /* Receiver data ready */
  34. #define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */
  35. /* clang-format on */
  36. static volatile char *uart8250_base;
  37. static u32 uart8250_in_freq;
  38. static u32 uart8250_baudrate;
  39. static u32 uart8250_reg_width;
  40. static u32 uart8250_reg_shift;
  41. static u32 get_reg(u32 num)
  42. {
  43. u32 offset = num << uart8250_reg_shift;
  44. if (uart8250_reg_width == 1)
  45. return readb(uart8250_base + offset);
  46. else if (uart8250_reg_width == 2)
  47. return readw(uart8250_base + offset);
  48. else
  49. return readl(uart8250_base + offset);
  50. }
  51. static void set_reg(u32 num, u32 val)
  52. {
  53. u32 offset = num << uart8250_reg_shift;
  54. if (uart8250_reg_width == 1)
  55. writeb(val, uart8250_base + offset);
  56. else if (uart8250_reg_width == 2)
  57. writew(val, uart8250_base + offset);
  58. else
  59. writel(val, uart8250_base + offset);
  60. }
  61. static void uart8250_putc(char ch)
  62. {
  63. while ((get_reg(UART_LSR_OFFSET) & UART_LSR_THRE) == 0)
  64. ;
  65. set_reg(UART_THR_OFFSET, ch);
  66. }
  67. static int uart8250_getc(void)
  68. {
  69. if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR)
  70. return get_reg(UART_RBR_OFFSET);
  71. return -1;
  72. }
  73. static struct sbi_console_device uart8250_console = {
  74. .name = "uart8250",
  75. .console_putc = uart8250_putc,
  76. .console_getc = uart8250_getc
  77. };
  78. int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
  79. u32 reg_width, u32 reg_offset)
  80. {
  81. u16 bdiv = 0;
  82. uart8250_base = (volatile char *)base + reg_offset;
  83. uart8250_reg_shift = reg_shift;
  84. uart8250_reg_width = reg_width;
  85. uart8250_in_freq = in_freq;
  86. uart8250_baudrate = baudrate;
  87. if (uart8250_baudrate) {
  88. bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
  89. (16 * uart8250_baudrate);
  90. }
  91. /* Disable all interrupts */
  92. set_reg(UART_IER_OFFSET, 0x00);
  93. /* Enable DLAB */
  94. set_reg(UART_LCR_OFFSET, 0x80);
  95. if (bdiv) {
  96. /* Set divisor low byte */
  97. set_reg(UART_DLL_OFFSET, bdiv & 0xff);
  98. /* Set divisor high byte */
  99. set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff);
  100. }
  101. /* 8 bits, no parity, one stop bit */
  102. set_reg(UART_LCR_OFFSET, 0x03);
  103. /* Enable FIFO */
  104. set_reg(UART_FCR_OFFSET, 0x01);
  105. /* No modem control DTR RTS */
  106. set_reg(UART_MCR_OFFSET, 0x00);
  107. /* Clear line status */
  108. get_reg(UART_LSR_OFFSET);
  109. /* Read receive buffer */
  110. get_reg(UART_RBR_OFFSET);
  111. /* Set scratchpad */
  112. set_reg(UART_SCR_OFFSET, 0x00);
  113. sbi_console_set_device(&uart8250_console);
  114. return 0;
  115. }