drivers/uart: Add a driver for SiFive's UART
This UART is used in the SiFive FU540 SoC, and will probably be used in other SoCs in the future. Change-Id: I915edf39666b7a5f9550e3b7e743e97fe3cacfd3 Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Reviewed-on: https://review.coreboot.org/25768 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
This commit is contained in:
parent
f35cc4d60f
commit
894e3a9ec8
|
@ -61,6 +61,11 @@ config DRIVERS_UART_PL011
|
|||
default n
|
||||
select HAVE_UART_SPECIAL
|
||||
|
||||
config DRIVERS_UART_SIFIVE
|
||||
bool
|
||||
select HAVE_UART_SPECIAL
|
||||
select UART_OVERRIDE_INPUT_CLOCK_DIVIDER
|
||||
|
||||
config UART_USE_REFCLK_AS_INPUT_CLOCK
|
||||
bool
|
||||
default n
|
||||
|
|
|
@ -42,4 +42,11 @@ ramstage-y += pl011.c
|
|||
verstage-y += pl011.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DRIVERS_UART_SIFIVE),y)
|
||||
bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += sifive.c
|
||||
romstage-y += sifive.c
|
||||
postcar-y += sifive.c
|
||||
ramstage-y += sifive.c
|
||||
endif
|
||||
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2018 Jonathan Neuschäfer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <arch/io.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)
|
||||
|
||||
void uart_init(int idx)
|
||||
{
|
||||
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
|
||||
|
||||
/* TODO: Configure the divisor */
|
||||
|
||||
/* 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));
|
||||
}
|
||||
|
||||
static bool uart_can_tx(struct sifive_uart_registers *regs)
|
||||
{
|
||||
return !(read32(®s->txdata) & TXDATA_FULL);
|
||||
}
|
||||
|
||||
void uart_tx_byte(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(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(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;
|
||||
}
|
||||
|
||||
#ifndef __PRE_RAM__
|
||||
void uart_fill_lb(void *data)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue