mb/google/brya: Create rull variant
[coreboot2.git] / src / drivers / uart / sifive.c
blob815a0cfb05c9280605190bf53a4f90ec7a289966
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <device/mmio.h>
4 #include <boot/coreboot_tables.h>
5 #include <console/uart.h>
6 #include <types.h>
8 /*
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 */
21 } __packed;
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(&regs->div, div);
41 /* Enable transmission, one stop bit, transmit watermark at 1 */
42 write32(&regs->txctrl, TXCTRL_TXEN|TXCTRL_NSTOP(1)|TXCTRL_TXCNT(1));
44 /* Enable reception, receive watermark at 0 */
45 write32(&regs->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)
53 * <=>
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());
58 div -= 1;
60 sifive_uart_init(uart_platform_baseptr(idx), div);
63 static bool uart_can_tx(struct sifive_uart_registers *regs)
65 return !(read32(&regs->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(&regs->txdata, data);
78 void uart_tx_flush(unsigned int idx)
80 struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
81 uint32_t ip;
83 /* Use the TX watermark bit to find out if the TX FIFO is empty */
84 do {
85 ip = read32(&regs->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);
92 uint32_t rxdata;
94 do {
95 rxdata = read32(&regs->rxdata);
96 } while (rxdata & RXDATA_EMPTY);
98 return rxdata & 0xff;
101 unsigned int uart_input_clock_divider(void)
104 * The SiFive UART handles oversampling internally. The divided clock
105 * is the baud clock.
107 return 1;
110 enum cb_err fill_lb_serial(struct lb_serial *serial)
112 return CB_ERR;
113 /* TODO */