blob: d1b89dc7452e058ab62d1044b3d54bcbd45d429e [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +02002
Kyösti Mälkki13f66502019-03-03 08:01:05 +02003#include <device/mmio.h>
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +02004#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
13struct 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
Philipp Hug75244002018-07-07 21:34:31 +020036static void sifive_uart_init(struct sifive_uart_registers *regs, int div)
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +020037{
Philipp Hug75244002018-07-07 21:34:31 +020038 /* Configure the divisor */
39 write32(&regs->div, div);
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +020040
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
Felix Helde3a12472020-09-11 15:47:09 +020048void uart_init(unsigned int idx)
Philipp Hug75244002018-07-07 21:34:31 +020049{
50 unsigned int div;
51 div = uart_baudrate_divisor(get_uart_baudrate(),
52 uart_platform_refclk(), uart_input_clock_divider());
53 sifive_uart_init(uart_platform_baseptr(idx), div);
54}
55
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +020056static bool uart_can_tx(struct sifive_uart_registers *regs)
57{
58 return !(read32(&regs->txdata) & TXDATA_FULL);
59}
60
Felix Helde3a12472020-09-11 15:47:09 +020061void uart_tx_byte(unsigned int idx, unsigned char data)
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +020062{
63 struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
64
65 while (!uart_can_tx(regs))
66 ; /* TODO: implement a timeout */
67
68 write32(&regs->txdata, data);
69}
70
Felix Helde3a12472020-09-11 15:47:09 +020071void uart_tx_flush(unsigned int idx)
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +020072{
73 struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
74 uint32_t ip;
75
76 /* Use the TX watermark bit to find out if the TX FIFO is empty */
77 do {
78 ip = read32(&regs->ip);
79 } while (!(ip & IP_TXWM));
80}
81
Felix Helde3a12472020-09-11 15:47:09 +020082unsigned char uart_rx_byte(unsigned int idx)
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +020083{
84 struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
85 uint32_t rxdata;
86
87 do {
88 rxdata = read32(&regs->rxdata);
89 } while (rxdata & RXDATA_EMPTY);
90
91 return rxdata & 0xff;
92}
93
94unsigned int uart_input_clock_divider(void)
95{
96 /*
97 * The SiFive UART handles oversampling internally. The divided clock
98 * is the baud clock.
99 */
100 return 1;
101}
102
Arthur Heymans9948c522022-10-24 14:37:40 +0200103enum cb_err fill_lb_serial(struct lb_serial *serial)
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +0200104{
Arthur Heymans9948c522022-10-24 14:37:40 +0200105 return CB_ERR;
Jonathan Neuschäfer894e3a92018-04-19 16:23:54 +0200106 /* TODO */
107}