diff --git a/include/sbi_utils/serial/alveo_uart.h b/include/sbi_utils/serial/alveo_uart.h new file mode 100644 index 0000000..0b6408c --- /dev/null +++ b/include/sbi_utils/serial/alveo_uart.h @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __SERIAL_ALVEO_UART_H__ +#define __SERIAL_ALVEO_UART_H__ + +#include + +int alveo_uart_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift, + u32 reg_width, u32 reg_offset); + +#endif diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig index e3589ca..b754c7c 100644 --- a/lib/utils/serial/Kconfig +++ b/lib/utils/serial/Kconfig @@ -84,6 +84,10 @@ config SERIAL_UART8250 bool "8250 UART support" default n +config SERIAL_ALVEO_UART + bool "ALveo UART support" + default n + config SERIAL_XILINX_UARTLITE bool "Xilinx UART Lite support" default n diff --git a/lib/utils/serial/alveo_uart.c b/lib/utils/serial/alveo_uart.c new file mode 100644 index 0000000..a351741 --- /dev/null +++ b/lib/utils/serial/alveo_uart.c @@ -0,0 +1,123 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +/* clang-format off */ + +#define UART_RBR_OFFSET 0 /* In: Recieve Buffer Register */ +#define UART_THR_OFFSET 0 /* Out: Transmitter Holding Register */ +#define UART_DLL_OFFSET 0 /* Out: Divisor Latch Low */ +#define UART_IER_OFFSET 1 /* I/O: Interrupt Enable Register */ +#define UART_DLM_OFFSET 1 /* Out: Divisor Latch High */ +#define UART_FCR_OFFSET 2 /* Out: FIFO Control Register */ +#define UART_IIR_OFFSET 2 /* I/O: Interrupt Identification Register */ +#define UART_LCR_OFFSET 3 /* Out: Line Control Register */ +#define UART_MCR_OFFSET 4 /* Out: Modem Control Register */ +#define UART_LSR_OFFSET 5 /* In: Line Status Register */ +#define UART_MSR_OFFSET 6 /* In: Modem Status Register */ +#define UART_SCR_OFFSET 7 /* I/O: Scratch Register */ +#define UART_MDR1_OFFSET 8 /* I/O: Mode Register */ + +#define UART_LSR_FIFOE 0x80 /* Fifo error */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */ + +/* clang-format on */ + +static volatile char *alveo_uart_base; +static u32 alveo_uart_in_freq; +static u32 alveo_uart_baudrate; +static u32 alveo_uart_reg_width; +static u32 alveo_uart_reg_shift; + +static u32 get_reg(u32 num) +{ + u32 offset = num << alveo_uart_reg_shift; + + if (alveo_uart_reg_width == 1) + return readb(alveo_uart_base + offset); + else if (alveo_uart_reg_width == 2) + return readw(alveo_uart_base + offset); + else + return readl(alveo_uart_base + offset); +} + +static void set_reg(u32 num, u32 val) +{ + u32 offset = num << alveo_uart_reg_shift; + + if (alveo_uart_reg_width == 1) + writeb(val, alveo_uart_base + offset); + else if (alveo_uart_reg_width == 2) + writew(val, alveo_uart_base + offset); + else + writel(val, alveo_uart_base + offset); +} + +static void alveo_uart_putc(char ch) +{ + while (!(get_reg(UART_LSR_OFFSET) & UART_LSR_TEMT)) + ; + + set_reg(UART_THR_OFFSET, ch); +} + +static int alveo_uart_getc(void) +{ + if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR) + return get_reg(UART_RBR_OFFSET); + return -1; +} + +static struct sbi_console_device alveo_uart_console = { + .name = "alveo_uart", + .console_putc = alveo_uart_putc, + .console_getc = alveo_uart_getc +}; + +int alveo_uart_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift, + u32 reg_width, u32 reg_offset) +{ + u16 bdiv = 0; + + alveo_uart_base = (volatile char *)base + reg_offset; + alveo_uart_reg_shift = reg_shift; + alveo_uart_reg_width = reg_width; + alveo_uart_in_freq = in_freq; + alveo_uart_baudrate = baudrate; + + if (alveo_uart_baudrate) { + bdiv = alveo_uart_in_freq / (16 * alveo_uart_baudrate); + } + + /* Disable all interrupts */ + set_reg(UART_IER_OFFSET, 0x00); + /* Enable DLAB */ + set_reg(UART_LCR_OFFSET, 0x80); + + if (bdiv) { + /* Set divisor low byte */ + set_reg(UART_DLL_OFFSET, bdiv & 0xff); + /* Set divisor high byte */ + set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff); + } + + /* 8 bits, no parity, one stop bit */ + set_reg(UART_LCR_OFFSET, 0x03); // previous was 0x0B + /* Disable all interrupts*/ + set_reg(UART_IER_OFFSET, 0x00); + + sbi_console_set_device(&alveo_uart_console); + + return 0; +} diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk index 1e6bd2e..0268bdf 100644 --- a/lib/utils/serial/objects.mk +++ b/lib/utils/serial/objects.mk @@ -44,5 +44,6 @@ libsbiutils-objs-$(CONFIG_SERIAL_SHAKTI) += serial/shakti-uart.o libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o +libsbiutils-objs-$(CONFIG_SERIAL_ALVEO_UART) += serial/alveo_uart.o libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o libsbiutils-objs-$(CONFIG_SERIAL_SEMIHOSTING) += serial/semihosting.o diff --git a/platform/fpga/sargantana_alveo/Kconfig b/platform/fpga/sargantana_alveo/Kconfig new file mode 100644 index 0000000..bf3e7e6 --- /dev/null +++ b/platform/fpga/sargantana_alveo/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: BSD-2-Clause + +config PLATFORM_SARGANTANA_ALVEO_FPGA + bool + select FDT + select IPI_MSWI + select IRQCHIP_PLIC + select SERIAL_ALVEO_UART + select TIMER_MTIMER + default y diff --git a/platform/fpga/sargantana_alveo/configs/defconfig b/platform/fpga/sargantana_alveo/configs/defconfig new file mode 100644 index 0000000..e69de29 diff --git a/platform/fpga/sargantana_alveo/objects.mk b/platform/fpga/sargantana_alveo/objects.mk new file mode 100644 index 0000000..d444abe --- /dev/null +++ b/platform/fpga/sargantana_alveo/objects.mk @@ -0,0 +1,87 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Western Digital Corporation or its affiliates. +# + +# Compiler pre-processor flags +platform-cppflags-y = + +# C Compiler and assembler flags. +platform-cflags-y = +platform-asflags-y = + +# Linker flags: additional libraries and object files that the platform +# code needs can be added here +platform-ldflags-y = + +# +# Command for platform specific "make run" +# Useful for development and debugging on plaftform simulator (such as QEMU) +# +# platform-runcmd = your_platform_run.sh + +# +# Platform RISC-V XLEN, ABI, ISA and Code Model configuration. +# These are optional parameters but platforms can optionaly provide it. +# Some of these are guessed based on GCC compiler capabilities +# +PLATFORM_RISCV_XLEN = 64 +PLATFORM_RISCV_ABI = lp64d +PLATFORM_RISCV_ISA = rv64imafd +PLATFORM_RISCV_CODE_MODEL = medany + +# Space separated list of object file names to be compiled for the platform +platform-objs-y += platform.o + +# +# If the platform support requires a builtin device tree file, the name of +# the device tree compiled file should be specified here. The device tree +# source file be in the form
.dts +# +# platform-objs-y +=
.o + +# Firmware load address configuration. This is mandatory. +FW_TEXT_START=0x80000000 + +# Optional parameter for path to external FDT +# FW_FDT_PATH="path to platform flattened device tree file" + +# +# Dynamic firmware configuration. +# Optional parameters are commented out. Uncomment and define these parameters +# as needed. +# +FW_DYNAMIC=n + +# +# Jump firmware configuration. +# Optional parameters are commented out. Uncomment and define these parameters +# as needed. +# +FW_JUMP=n +# This needs to be 4MB aligned for 32-bit support +# This needs to be 2MB aligned for 64-bit support +# ifeq ($(PLATFORM_RISCV_XLEN), 32) +# FW_JUMP_ADDR=0x80400000 +# else +# FW_JUMP_ADDR=0x80200000 +# endif +# FW_JUMP_FDT_ADDR=0x82200000 + +# +# Firmware with payload configuration. +# Optional parameters are commented out. Uncomment and define these parameters +# as needed. +# +FW_PAYLOAD=y +# This needs to be 4MB aligned for 32-bit support +# This needs to be 2MB aligned for 64-bit support +ifeq ($(PLATFORM_RISCV_XLEN), 32) +FW_PAYLOAD_OFFSET=0x400000 +else +FW_PAYLOAD_OFFSET=0x200000 +endif +FW_PAYLOAD_ALIGN=0x1000 +# FW_PAYLOAD_PATH="path to next boot stage binary image file" +# FW_PAYLOAD_FDT_ADDR=0x82200000 diff --git a/platform/fpga/sargantana_alveo/platform.c b/platform/fpga/sargantana_alveo/platform.c new file mode 100644 index 0000000..a359b34 --- /dev/null +++ b/platform/fpga/sargantana_alveo/platform.c @@ -0,0 +1,146 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + */ + +#include +#include +#include +#include + +/* + * Include these files as needed. + * See objects.mk SARGANTANA_ALVEO_xxx configuration parameters. + */ + +#include +#include + +#define SARGANTANA_ALVEO_HART_COUNT 1 + +#define SARGANTANA_ALVEO_UART_BASE_ADDR 0x40000000 +#define SARGANTANA_ALVEO_UART_OFFSET 0x1000 +//#define SARGANTANA_ALVEO_UART_ADDR SARGANTANA_ALVEO_UART_BASE_ADDR + SARGANTANA_ALVEO_UART_XLNX_OFFSET +#define SARGANTANA_ALVEO_UART_INPUT_FREQ 50000000 +#define SARGANTANA_ALVEO_UART_BAUDRATE 115200 + +#define SARGANTANA_ALVEO_TIMER_BASE 0x40170000 +#define ADDR_TIME_L 0x0u // 32 lower bits of the time register +#define ADDR_TIME_H 0x1u // 32 higher bits of the time register +#define ADDR_TIMECMP_L 0x2u // 32 lower bits of the time comparator +#define ADDR_TIMECMP_H 0x3u // 32 higher bits of the time comparator + +volatile uint32_t *timer_base_ptr = (uint32_t *)(SARGANTANA_ALVEO_TIMER_BASE); + +/* + * Platform early initialization. + */ +static int sargantana_alveo_early_init(bool cold_boot) +{ + return 0; +} + +/* + * Platform final initialization. + */ +static int sargantana_alveo_final_init(bool cold_boot) +{ + return 0; +} + +/* + * Initialize the platform console. + */ +static int sargantana_alveo_console_init(void) +{ + return alveo_uart_init(SARGANTANA_ALVEO_UART_BASE_ADDR, + SARGANTANA_ALVEO_UART_INPUT_FREQ, + SARGANTANA_ALVEO_UART_BAUDRATE, + 2, 4, + SARGANTANA_ALVEO_UART_OFFSET); +} + +/* + * Initialize the platform interrupt controller for current HART. + */ +static int sargantana_alveo_irqchip_init(bool cold_boot) +{ + u32 hartid = current_hartid(); + return hartid; +} + +/* + * Initialize IPI for current HART. + */ +static int sargantana_alveo_ipi_init(bool cold_boot) +{ + return 0; +} + +/* + * Get platform timer value. + */ +static u64 sargantana_alveo_timer_value(void) +{ + return ((u64)*(timer_base_ptr + ADDR_TIME_H) << 32) + *(timer_base_ptr + ADDR_TIME_L); +} + +/* + * Start platform timer event for current HART. + */ +static void sargantana_alveo_timer_event_start(u64 next_event) +{ + *(timer_base_ptr + ADDR_TIMECMP_H) = next_event >> 32; + *(timer_base_ptr + ADDR_TIMECMP_L) = next_event; +} + +/* + * Stop platform timer event for current HART. + */ +static void sargantana_alveo_timer_event_stop(void) +{ + + *(timer_base_ptr + ADDR_TIMECMP_H) = 0; + *(timer_base_ptr + ADDR_TIMECMP_L) = 0; +} + +static struct sbi_timer_device mtimer = { + .name = "generic_timer", // TODO Where the timer comes from? I would prefer a better name :p + .timer_freq = SARGANTANA_ALVEO_UART_INPUT_FREQ, + .timer_value = sargantana_alveo_timer_value, + .timer_event_start = sargantana_alveo_timer_event_start, + .timer_event_stop = sargantana_alveo_timer_event_stop +}; + +/* + * Initialize platform timer for current HART. + */ +static int sargantana_alveo_timer_init(bool cold_boot) +{ + *(timer_base_ptr + ADDR_TIMECMP_H) = 0; + *(timer_base_ptr + ADDR_TIMECMP_L) = 0; + sbi_timer_set_device(&mtimer); + return 0; +} + +/* + * Platform descriptor. + */ +const struct sbi_platform_operations sargantana_alveo_ops = { + .early_init = sargantana_alveo_early_init, + .final_init = sargantana_alveo_final_init, + .console_init = sargantana_alveo_console_init, + .irqchip_init = sargantana_alveo_irqchip_init, + .ipi_init = sargantana_alveo_ipi_init, + .timer_init = sargantana_alveo_timer_init +}; +const struct sbi_platform platform = { + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "Sargantana (for Xilinx Alveo FPGA)", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = SARGANTANA_ALVEO_HART_COUNT, + .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, + .platform_ops_addr = (unsigned long)&sargantana_alveo_ops +};