9948c521a6
Instead of having callbacks into serial console code to set up the coreboot table have the coreboot table code call IP specific code to get serial information. This makes it easier to reuse the information as the return value can be used in a different context (e.g. when filling in a FDT). This also removes boilerplate code to set up lb_console entries by setting entry based on the type in struct lb_uart. Change-Id: I6c08a88fb5fc035eb28d0becf19471c709c8043d Signed-off-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-on: https://review.coreboot.org/c/coreboot/+/68768 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Hung-Te Lin <hungte@chromium.org>
107 lines
2.7 KiB
C
107 lines
2.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#include <device/mmio.h>
|
|
#include <boot/coreboot_tables.h>
|
|
#include <console/uart.h>
|
|
#include <types.h>
|
|
|
|
/*
|
|
* This is a driver for SiFive's own UART, documented in the FU540 manual:
|
|
* https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
|
|
*/
|
|
|
|
struct sifive_uart_registers {
|
|
uint32_t txdata; /* Transmit data register */
|
|
uint32_t rxdata; /* Receive data register */
|
|
uint32_t txctrl; /* Transmit control register */
|
|
uint32_t rxctrl; /* Receive control register */
|
|
uint32_t ie; /* UART interrupt enable */
|
|
uint32_t ip; /* UART interrupt pending */
|
|
uint32_t div; /* Baud rate divisor */
|
|
} __packed;
|
|
|
|
#define TXDATA_FULL BIT(31)
|
|
#define RXDATA_EMPTY BIT(31)
|
|
#define TXCTRL_TXEN BIT(0)
|
|
#define TXCTRL_NSTOP_SHIFT 1
|
|
#define TXCTRL_NSTOP(x) (((x)-1) << TXCTRL_NSTOP_SHIFT)
|
|
#define TXCTRL_TXCNT_SHIFT 16
|
|
#define TXCTRL_TXCNT(x) ((x) << TXCTRL_TXCNT_SHIFT)
|
|
#define RXCTRL_RXEN BIT(0)
|
|
#define RXCTRL_RXCNT_SHIFT 16
|
|
#define RXCTRL_RXCNT(x) ((x) << RXCTRL_RXCNT_SHIFT)
|
|
#define IP_TXWM BIT(0)
|
|
#define IP_RXWM BIT(1)
|
|
|
|
static void sifive_uart_init(struct sifive_uart_registers *regs, int div)
|
|
{
|
|
/* Configure the divisor */
|
|
write32(®s->div, div);
|
|
|
|
/* Enable transmission, one stop bit, transmit watermark at 1 */
|
|
write32(®s->txctrl, TXCTRL_TXEN|TXCTRL_NSTOP(1)|TXCTRL_TXCNT(1));
|
|
|
|
/* Enable reception, receive watermark at 0 */
|
|
write32(®s->rxctrl, RXCTRL_RXEN|RXCTRL_RXCNT(0));
|
|
}
|
|
|
|
void uart_init(unsigned int idx)
|
|
{
|
|
unsigned int div;
|
|
div = uart_baudrate_divisor(get_uart_baudrate(),
|
|
uart_platform_refclk(), uart_input_clock_divider());
|
|
sifive_uart_init(uart_platform_baseptr(idx), div);
|
|
}
|
|
|
|
static bool uart_can_tx(struct sifive_uart_registers *regs)
|
|
{
|
|
return !(read32(®s->txdata) & TXDATA_FULL);
|
|
}
|
|
|
|
void uart_tx_byte(unsigned int idx, unsigned char data)
|
|
{
|
|
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
|
|
|
|
while (!uart_can_tx(regs))
|
|
; /* TODO: implement a timeout */
|
|
|
|
write32(®s->txdata, data);
|
|
}
|
|
|
|
void uart_tx_flush(unsigned int idx)
|
|
{
|
|
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
|
|
uint32_t ip;
|
|
|
|
/* Use the TX watermark bit to find out if the TX FIFO is empty */
|
|
do {
|
|
ip = read32(®s->ip);
|
|
} while (!(ip & IP_TXWM));
|
|
}
|
|
|
|
unsigned char uart_rx_byte(unsigned int idx)
|
|
{
|
|
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
|
|
uint32_t rxdata;
|
|
|
|
do {
|
|
rxdata = read32(®s->rxdata);
|
|
} while (rxdata & RXDATA_EMPTY);
|
|
|
|
return rxdata & 0xff;
|
|
}
|
|
|
|
unsigned int uart_input_clock_divider(void)
|
|
{
|
|
/*
|
|
* The SiFive UART handles oversampling internally. The divided clock
|
|
* is the baud clock.
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
enum cb_err fill_lb_serial(struct lb_serial *serial)
|
|
{
|
|
return CB_ERR;
|
|
/* TODO */
|
|
}
|