From 894e3a9ec807661a99dff426b3e07188d3507d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Thu, 19 Apr 2018 16:23:54 +0200 Subject: [PATCH] drivers/uart: Add a driver for SiFive's UART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-on: https://review.coreboot.org/25768 Tested-by: build bot (Jenkins) Reviewed-by: Kyösti Mälkki --- src/drivers/uart/Kconfig | 5 ++ src/drivers/uart/Makefile.inc | 7 +++ src/drivers/uart/sifive.c | 114 ++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 src/drivers/uart/sifive.c diff --git a/src/drivers/uart/Kconfig b/src/drivers/uart/Kconfig index 54f591d9ad..bfc5ccee61 100644 --- a/src/drivers/uart/Kconfig +++ b/src/drivers/uart/Kconfig @@ -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 diff --git a/src/drivers/uart/Makefile.inc b/src/drivers/uart/Makefile.inc index ebaa5d4a0b..c7aa1ae784 100644 --- a/src/drivers/uart/Makefile.inc +++ b/src/drivers/uart/Makefile.inc @@ -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 diff --git a/src/drivers/uart/sifive.c b/src/drivers/uart/sifive.c new file mode 100644 index 0000000000..dd47cafcf1 --- /dev/null +++ b/src/drivers/uart/sifive.c @@ -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 +#include +#include +#include + +/* + * 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