xref: /aosp_15_r20/external/coreboot/src/drivers/uart/sifive.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <device/mmio.h>
4 #include <boot/coreboot_tables.h>
5 #include <console/uart.h>
6 #include <types.h>
7 
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/
11  */
12 
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;
22 
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)
35 
sifive_uart_init(struct sifive_uart_registers * regs,int div)36 static void sifive_uart_init(struct sifive_uart_registers *regs, int div)
37 {
38 	/* Configure the divisor */
39 	write32(&regs->div, div);
40 
41 	/* Enable transmission, one stop bit, transmit watermark at 1 */
42 	write32(&regs->txctrl, TXCTRL_TXEN|TXCTRL_NSTOP(1)|TXCTRL_TXCNT(1));
43 
44 	/* Enable reception, receive watermark at 0 */
45 	write32(&regs->rxctrl, RXCTRL_RXEN|RXCTRL_RXCNT(0));
46 }
47 
uart_init(unsigned int idx)48 void uart_init(unsigned int idx)
49 {
50 	/*
51 	 * according to FU540/FU740 manual:
52 	 * f_baud = f_in / (div + 1)
53 	 * <=>
54 	 * div = (f_in / f_baud) - 1
55 	 */
56 	unsigned int div = uart_baudrate_divisor(get_uart_baudrate(), uart_platform_refclk(),
57 			uart_input_clock_divider());
58 	div -= 1;
59 
60 	sifive_uart_init(uart_platform_baseptr(idx), div);
61 }
62 
uart_can_tx(struct sifive_uart_registers * regs)63 static bool uart_can_tx(struct sifive_uart_registers *regs)
64 {
65 	return !(read32(&regs->txdata) & TXDATA_FULL);
66 }
67 
uart_tx_byte(unsigned int idx,unsigned char data)68 void uart_tx_byte(unsigned int idx, unsigned char data)
69 {
70 	struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
71 
72 	while (!uart_can_tx(regs))
73 		; /* TODO: implement a timeout */
74 
75 	write32(&regs->txdata, data);
76 }
77 
uart_tx_flush(unsigned int idx)78 void uart_tx_flush(unsigned int idx)
79 {
80 	struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
81 	uint32_t ip;
82 
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));
87 }
88 
uart_rx_byte(unsigned int idx)89 unsigned char uart_rx_byte(unsigned int idx)
90 {
91 	struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
92 	uint32_t rxdata;
93 
94 	do {
95 		rxdata = read32(&regs->rxdata);
96 	} while (rxdata & RXDATA_EMPTY);
97 
98 	return rxdata & 0xff;
99 }
100 
uart_input_clock_divider(void)101 unsigned int uart_input_clock_divider(void)
102 {
103 	/*
104 	 * The SiFive UART handles oversampling internally. The divided clock
105 	 * is the baud clock.
106 	 */
107 	return 1;
108 }
109 
fill_lb_serial(struct lb_serial * serial)110 enum cb_err fill_lb_serial(struct lb_serial *serial)
111 {
112 	return CB_ERR;
113 	/* TODO */
114 }
115