1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <device/mmio.h>
4 #include <boot/coreboot_tables.h>
5 #include <console/uart.h>
9 * This is a driver for SiFive's own UART, documented in the FU540 manual:
10 * https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
13 struct sifive_uart_registers
{
14 uint32_t txdata
; /* Transmit data register */
15 uint32_t rxdata
; /* Receive data register */
16 uint32_t txctrl
; /* Transmit control register */
17 uint32_t rxctrl
; /* Receive control register */
18 uint32_t ie
; /* UART interrupt enable */
19 uint32_t ip
; /* UART interrupt pending */
20 uint32_t div
; /* Baud rate divisor */
23 #define TXDATA_FULL BIT(31)
24 #define RXDATA_EMPTY BIT(31)
25 #define TXCTRL_TXEN BIT(0)
26 #define TXCTRL_NSTOP_SHIFT 1
27 #define TXCTRL_NSTOP(x) (((x)-1) << TXCTRL_NSTOP_SHIFT)
28 #define TXCTRL_TXCNT_SHIFT 16
29 #define TXCTRL_TXCNT(x) ((x) << TXCTRL_TXCNT_SHIFT)
30 #define RXCTRL_RXEN BIT(0)
31 #define RXCTRL_RXCNT_SHIFT 16
32 #define RXCTRL_RXCNT(x) ((x) << RXCTRL_RXCNT_SHIFT)
33 #define IP_TXWM BIT(0)
34 #define IP_RXWM BIT(1)
36 static void sifive_uart_init(struct sifive_uart_registers
*regs
, int div
)
38 /* Configure the divisor */
39 write32(®s
->div
, div
);
41 /* Enable transmission, one stop bit, transmit watermark at 1 */
42 write32(®s
->txctrl
, TXCTRL_TXEN
|TXCTRL_NSTOP(1)|TXCTRL_TXCNT(1));
44 /* Enable reception, receive watermark at 0 */
45 write32(®s
->rxctrl
, RXCTRL_RXEN
|RXCTRL_RXCNT(0));
48 void uart_init(unsigned int idx
)
51 * according to FU540/FU740 manual:
52 * f_baud = f_in / (div + 1)
54 * div = (f_in / f_baud) - 1
56 unsigned int div
= uart_baudrate_divisor(get_uart_baudrate(), uart_platform_refclk(),
57 uart_input_clock_divider());
60 sifive_uart_init(uart_platform_baseptr(idx
), div
);
63 static bool uart_can_tx(struct sifive_uart_registers
*regs
)
65 return !(read32(®s
->txdata
) & TXDATA_FULL
);
68 void uart_tx_byte(unsigned int idx
, unsigned char data
)
70 struct sifive_uart_registers
*regs
= uart_platform_baseptr(idx
);
72 while (!uart_can_tx(regs
))
73 ; /* TODO: implement a timeout */
75 write32(®s
->txdata
, data
);
78 void uart_tx_flush(unsigned int idx
)
80 struct sifive_uart_registers
*regs
= uart_platform_baseptr(idx
);
83 /* Use the TX watermark bit to find out if the TX FIFO is empty */
85 ip
= read32(®s
->ip
);
86 } while (!(ip
& IP_TXWM
));
89 unsigned char uart_rx_byte(unsigned int idx
)
91 struct sifive_uart_registers
*regs
= uart_platform_baseptr(idx
);
95 rxdata
= read32(®s
->rxdata
);
96 } while (rxdata
& RXDATA_EMPTY
);
101 unsigned int uart_input_clock_divider(void)
104 * The SiFive UART handles oversampling internally. The divided clock
110 enum cb_err
fill_lb_serial(struct lb_serial
*serial
)