cadence-uart.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /*
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2022 StarFive Technology Co., Ltd.
  5. *
  6. * Author: Jun Liang Tan <junliang.tan@linux.starfivetech.com>
  7. */
  8. #include <sbi/riscv_io.h>
  9. #include <sbi/sbi_console.h>
  10. #include <sbi_utils/serial/cadence-uart.h>
  11. /* clang-format off */
  12. #define UART_REG_CTRL 0x00
  13. #define UART_REG_MODE 0x04
  14. #define UART_REG_IDR 0x0C
  15. #define UART_REG_BRGR 0x18
  16. #define UART_REG_CSR 0x2C
  17. #define UART_REG_RFIFO_TFIFO 0x30
  18. #define UART_REG_BDIVR 0x34
  19. #define UART_CTRL_RXRES 0x00000001
  20. #define UART_CTRL_TXRES 0x00000002
  21. #define UART_CTRL_RXEN 0x00000004
  22. #define UART_CTRL_RXDIS 0x00000008
  23. #define UART_CTRL_TXEN 0x00000010
  24. #define UART_CTRL_TXDIS 0x00000020
  25. #define UART_MODE_PAR_NONE 0x00000020 /* No parity set */
  26. #define UART_BRGR_CD_CLKDIVISOR 0x00000001 /* baud_sample = sel_clk */
  27. #define UART_CSR_REMPTY 0x00000002
  28. #define UART_CSR_TFUL 0x00000010
  29. /* clang-format on */
  30. static volatile void *uart_base;
  31. static u32 uart_in_freq;
  32. static u32 uart_baudrate;
  33. /*
  34. * Find minimum divisor divides in_freq to max_target_hz;
  35. * Based on SiFive UART driver (sifive-uart.c)
  36. */
  37. static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
  38. uint64_t max_target_hz)
  39. {
  40. uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
  41. /* Avoid underflow */
  42. if (quotient == 0)
  43. return 0;
  44. else
  45. return quotient - 1;
  46. }
  47. static u32 get_reg(u32 offset)
  48. {
  49. return readl(uart_base + offset);
  50. }
  51. static void set_reg(u32 offset, u32 val)
  52. {
  53. writel(val, uart_base + offset);
  54. }
  55. static void cadence_uart_putc(char ch)
  56. {
  57. while (get_reg(UART_REG_CSR) & UART_CSR_TFUL)
  58. ;
  59. set_reg(UART_REG_RFIFO_TFIFO, ch);
  60. }
  61. static int cadence_uart_getc(void)
  62. {
  63. u32 ret = get_reg(UART_REG_CSR);
  64. if (!(ret & UART_CSR_REMPTY))
  65. return get_reg(UART_REG_RFIFO_TFIFO);
  66. return -1;
  67. }
  68. static struct sbi_console_device cadence_console = {
  69. .name = "cadence_uart",
  70. .console_putc = cadence_uart_putc,
  71. .console_getc = cadence_uart_getc
  72. };
  73. int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
  74. {
  75. uart_base = (volatile void *)base;
  76. uart_in_freq = in_freq;
  77. uart_baudrate = baudrate;
  78. /* Disable interrupts */
  79. set_reg(UART_REG_IDR, 0xFFFFFFFF);
  80. /* Disable TX RX */
  81. set_reg(UART_REG_CTRL, UART_CTRL_TXDIS | UART_CTRL_RXDIS);
  82. /* Configure baudrate */
  83. if (in_freq && baudrate) {
  84. set_reg(UART_REG_BRGR, UART_BRGR_CD_CLKDIVISOR);
  85. set_reg(UART_REG_BDIVR,
  86. uart_min_clk_divisor(in_freq, baudrate));
  87. }
  88. /* Software reset TX RX data path and enable TX RX */
  89. set_reg(UART_REG_CTRL, UART_CTRL_TXRES | UART_CTRL_RXRES
  90. | UART_CTRL_TXEN | UART_CTRL_RXEN);
  91. /*
  92. * Set:
  93. * 1 stop bit, bits[07:06] = 0x00,
  94. * no parity set, bits[05:03] = 0x100,
  95. * 8 bits character length, bits[02:01] = 0x00,
  96. * sel_clk = uart_clk, bit[0] = 0x0
  97. */
  98. set_reg(UART_REG_MODE, UART_MODE_PAR_NONE);
  99. sbi_console_set_device(&cadence_console);
  100. return 0;
  101. }