Jonathan Neuschäfer has uploaded this change for review. ( https://review.coreboot.org/25768
Change subject: drivers/uart: Add a driver for SiFive's UART ......................................................................
drivers/uart: Add a driver for SiFive's UART
This UART is used in the SiFive FU540 SoC.
Change-Id: I915edf39666b7a5f9550e3b7e743e97fe3cacfd3 Signed-off-by: Jonathan Neuschäfer j.neuschaefer@gmx.net --- M src/drivers/uart/Kconfig M src/drivers/uart/Makefile.inc A src/drivers/uart/sifive.c 3 files changed, 125 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/68/25768/1
diff --git a/src/drivers/uart/Kconfig b/src/drivers/uart/Kconfig index 54f591d..bfc5cce 100644 --- a/src/drivers/uart/Kconfig +++ b/src/drivers/uart/Kconfig @@ -61,6 +61,11 @@ 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 diff --git a/src/drivers/uart/Makefile.inc b/src/drivers/uart/Makefile.inc index ebaa5d4..c7aa1ae 100644 --- a/src/drivers/uart/Makefile.inc +++ b/src/drivers/uart/Makefile.inc @@ -42,4 +42,11 @@ 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 diff --git a/src/drivers/uart/sifive.c b/src/drivers/uart/sifive.c new file mode 100644 index 0000000..8e54d16 --- /dev/null +++ b/src/drivers/uart/sifive.c @@ -0,0 +1,113 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 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 */ +}; + +#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 = read32(®s->rxdata); + + if (rxdata & RXDATA_EMPTY) + return 0; + else + 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