1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2025 SiFive, Inc
4 */
5
6 #include <drivers/sifive_uart.h>
7 #include <io.h>
8
9 /* SiFive UART register defines */
10 #define UART_REG_TXFIFO 0x0
11 #define UART_REG_RXFIFO 0x4
12 #define UART_REG_TXCTRL 0x8
13 #define UART_REG_RXCTRL 0xC
14 #define UART_REG_IE 0x10
15 #define UART_REG_IP 0x14
16 #define UART_REG_DIV 0x18
17
18 /* SiFive UART status register bits */
19 #define UART_TXFIFO_FULL 0x80000000
20 #define UART_RXFIFO_EMPTY 0x80000000
21 #define UART_RXFIFO_DATA 0x000000FF
22 #define UART_TXCTRL_TXEN 0x1
23 #define UART_RXCTRL_RXEN 0x1
24
chip_to_base(struct serial_chip * chip)25 static vaddr_t chip_to_base(struct serial_chip *chip)
26 {
27 struct sifive_uart_data *pd =
28 container_of(chip, struct sifive_uart_data, chip);
29
30 return io_pa_or_va(&pd->base, SIFIVE_UART_REG_SIZE);
31 }
32
sifive_uart_flush(struct serial_chip * chip)33 static void sifive_uart_flush(struct serial_chip *chip)
34 {
35 vaddr_t base = chip_to_base(chip);
36
37 while (io_read32(base + UART_REG_TXFIFO) & UART_TXFIFO_FULL) {
38 /* Wait until transmit FIFO is not full */
39 ;
40 }
41 }
42
sifive_uart_have_rx_data(struct serial_chip * chip)43 static bool sifive_uart_have_rx_data(struct serial_chip *chip)
44 {
45 vaddr_t base = chip_to_base(chip);
46
47 return !(io_read32(base + UART_REG_RXFIFO) & UART_RXFIFO_EMPTY);
48 }
49
sifive_uart_getchar(struct serial_chip * chip)50 static int sifive_uart_getchar(struct serial_chip *chip)
51 {
52 vaddr_t base = chip_to_base(chip);
53
54 while (!sifive_uart_have_rx_data(chip)) {
55 /* Wait until data is available in the receive FIFO */
56 ;
57 }
58 return io_read32(base + UART_REG_RXFIFO) & UART_RXFIFO_DATA;
59 }
60
sifive_uart_putc(struct serial_chip * chip,int ch)61 static void sifive_uart_putc(struct serial_chip *chip, int ch)
62 {
63 vaddr_t base = chip_to_base(chip);
64
65 sifive_uart_flush(chip);
66
67 /* Write out character to transmit FIFO */
68 io_write32(base + UART_REG_TXFIFO, ch);
69 }
70
71 static const struct serial_ops sifive_uart_ops = {
72 .flush = sifive_uart_flush,
73 .getchar = sifive_uart_getchar,
74 .have_rx_data = sifive_uart_have_rx_data,
75 .putc = sifive_uart_putc,
76 };
77
sifive_uart_init(struct sifive_uart_data * pd,paddr_t base,uint32_t uart_clk,uint32_t baud_rate)78 void sifive_uart_init(struct sifive_uart_data *pd, paddr_t base,
79 uint32_t uart_clk, uint32_t baud_rate)
80 {
81 uint32_t divisor = 0;
82
83 pd->base.pa = base;
84 pd->chip.ops = &sifive_uart_ops;
85
86 /* Configure baudrate */
87 if (uart_clk && baud_rate) {
88 divisor = (uart_clk + baud_rate - 1) / baud_rate - 1;
89 io_write32(base + UART_REG_DIV, divisor);
90 }
91
92 /* Disable interrupts */
93 io_write32(base + UART_REG_IE, 0);
94
95 /* Enable TX */
96 io_write32(base + UART_REG_TXCTRL, UART_TXCTRL_TXEN);
97
98 /* Enable RX */
99 io_write32(base + UART_REG_RXCTRL, UART_RXCTRL_RXEN);
100 }
101