thermosphere: pl011 uart refactor

This commit is contained in:
TuxSH 2020-01-10 19:45:31 +00:00
parent 57548e67fb
commit a552c254e0
10 changed files with 224 additions and 135 deletions

View file

@ -145,7 +145,8 @@ ifeq ($(PLATFORM), qemu)
export QEMU := qemu-system-aarch64
QEMUFLAGS := -nographic -machine virt,virtualization=on,accel=tcg,gic-version=2 -cpu cortex-a57 -smp 4 -m 1024\
-kernel thermosphere.elf -d unimp,guest_errors -semihosting-config enable,target=native -serial mon:stdio
-kernel thermosphere.elf -d unimp,guest_errors -semihosting-config enable,target=native\
-chardev stdio,id=uart -serial chardev:uart -monitor none
qemu: all
@$(QEMU) $(QEMUFLAGS)

View file

@ -37,7 +37,7 @@ int debugLog(const char *fmt, ...)
if (DLOG_USE_SEMIHOSTING_WRITE0 && semihosting_connection_supported()) {
semihosting_write_string(buf);
} else {
uartWriteData(buf, (size_t)res);
uartWriteData(DEFAULT_UART, buf, (size_t)res);
}
return res;

View file

@ -98,7 +98,7 @@ static void initGic(void)
currentCoreCtx->gicInterfaceMask = gicd->itargetsr[0];
}
static void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
{
volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
gicd->icenabler[id / 32] = BIT(id % 32);
@ -129,6 +129,9 @@ void initIrq(void)
configureInterrupt(GIC_IRQID_MAINTENANCE, IRQ_PRIORITY_HOST, true);
for(u32 i=32; i < 256+32; i++) {
configureInterrupt(i, IRQ_PRIORITY_HOST, true);
}
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
}
@ -159,7 +162,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
u32 irqId = iar & 0x3FF;
u32 srcCore = (iar >> 10) & 7;
//DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId);
DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId);
if (irqId == GIC_IRQID_SPURIOUS) {
// Spurious interrupt received

View file

@ -21,6 +21,7 @@
#include "exceptions.h"
#include "utils.h"
#include "platform/interrupt_config.h"
#include "platform/uart.h"
#define IRQ_PRIORITY_HOST 0
#define IRQ_PRIORITY_GUEST 1
@ -49,6 +50,7 @@ extern IrqManager g_irqManager;
void initIrq(void);
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32);
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive);
static inline void generateSgiForAllOthers(ThermosphereSgi id)
{
@ -78,6 +80,7 @@ static inline bool irqIsGuest(u16 id)
}
bool ret = id != GIC_IRQID_MAINTENANCE && id != GIC_IRQID_NS_PHYS_HYP_TIMER;
ret = ret && id != uartGetIrqId(DEFAULT_UART); // FIXME
#if GIC_IRQID_NS_VIRT_HYP_TIMER != GIC_IRQID_SPURIOUS
ret = ret && id != GIC_IRQID_NS_VIRT_HYP_TIMER;
#endif

View file

@ -45,7 +45,8 @@ void thermosphereMain(ExceptionStackFrame *frame)
initIrq();
if (currentCoreCtx->isBootCore) {
uartInit(115200);
uartInit(DEFAULT_UART, 115200, 0);
uartSetInterruptStatus(DEFAULT_UART, false, false);
DEBUG("EL2: core %u reached main first!\n", currentCoreCtx->coreId);
}

View file

@ -35,6 +35,8 @@
#define GIC_IRQID_SEC_PHYS_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 20. Unimplemented
#define GIC_IRQID_SEC_VIRT_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 19. Unimplemented
#define GIC_IRQID_UART 33
static inline void initGicV2Pointers(ArmGicV2 *gic)
{
gic->gicd = (volatile ArmGicV2Distributor *)0x08000000ull;

View file

@ -15,9 +15,9 @@
*/
#include "uart.h"
#include "../../utils.h"
#include "../../irq.h"
// Adapted from ARM TF asm code
// Adapted from TF asm code
// AMBA PL101 driver
/*
@ -28,52 +28,116 @@
//115200
void uartInit(u32 baudRate)
static inline volatile PL011UartRegisters *uartGetRegisters(UartDevice dev)
{
// First, disable UART
g_uartRegs->CR &= ~PL011_UARTCR_UARTEN;
switch (dev) {
case UART_A:
return (volatile PL011UartRegisters *)0x09000000;
default:
return NULL;
}
}
void uartInit(UartDevice dev, u32 baudRate, u32 flags)
{
/* The TRM (DDI0183) reads:
Program the control registers as follows:
1. Disable the UART.
2. Wait for the end of transmission or reception of the current character.
3. Flush the transmit FIFO by disabling bit 4 (FEN) in the line control register
(UARTCLR_H).
4. Reprogram the control register.
5. Enable the UART.
*/
(void)flags;
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
// First, disable the UART. Flush the receive FIFO, wait for tx to complete, and disable both FIFOs.
uart->cr &= ~PL011_UARTCR_UARTEN;
while (!(uart->fr & PL011_UARTFR_RXFE)) {
uart->dr;
}
while (uart->fr & PL011_UARTFR_BUSY);
// This flushes the transmit FIFO:
uart->lcr_h &= ~PL011_UARTLCR_H_FEN;
// Set baudrate, Divisor = (Uart clock * 4) / baudrate; stored in IBRD|FBRD
u32 divisor = (4 * UART0_CLK_IN_HZ) / baudRate;
g_uartRegs->IBRD = divisor >> 6;
g_uartRegs->FBRD = divisor & 0x3F;
g_uartRegs->LCR_H = PL011_LINE_CONTROL;
u32 divisor = (4 * UART_CLK_IN_HZ) / baudRate;
uart->ibrd = divisor >> 6;
uart->fbrd = divisor & 0x3F;
// Select FIFO fill levels for interrupts
uart->ifls = PL011_IFLS_RX4_8 | PL011_IFLS_TX4_8;
// FIFO Enabled / No Parity / 8 Data bit / One Stop Bit
uart->lcr_h = PL011_UARTLCR_H_FEN | PL011_UARTLCR_H_WLEN_8;
// Select the interrupts we want to have
// RX timeout and TX/RX fill interrupts
uart->imsc = PL011_RTI | PL011_RXI | PL011_RXI;
// Clear any pending errors
g_uartRegs->ECR = 0;
uart->ecr = 0;
// Clear all interrupts
uart->icr = PL011_ALL_INTERRUPTS;
// Register the interrupt ID
//configureInterrupt(uartGetIrqId(dev), IRQ_PRIORITY_HOST, true);
// Enable tx, rx, and uart overall
g_uartRegs->CR = PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN;
uart->cr = PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN;
uart->imsc = PL011_RTI | PL011_RXI | PL011_RXI;
}
void uartWriteData(const void *buffer, size_t size)
void uartWriteData(UartDevice dev, const void *buffer, size_t size)
{
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
const u8 *buf8 = (const u8 *)buffer;
for (size_t i = 0; i < size; i++) {
while (g_uartRegs->FR & PL011_UARTFR_TXFF); // while TX FIFO full
g_uartRegs->DR = buf8[i];
while (uart->fr & PL011_UARTFR_TXFF); // while TX FIFO full
uart->dr = buf8[i];
}
}
void uartReadData(void *buffer, size_t size)
void uartReadData(UartDevice dev, void *buffer, size_t size)
{
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
u8 *buf8 = (u8 *)buffer;
size_t i;
for (i = 0; i < size; i++) {
while (g_uartRegs->FR & PL011_UARTFR_RXFE);
buf8[i] = g_uartRegs->DR;
while (uart->fr & PL011_UARTFR_RXFE);
buf8[i] = uart->dr;
}
}
size_t uartReadDataMax(void *buffer, size_t maxSize)
size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize)
{
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
u8 *buf8 = (u8 *)buffer;
size_t i;
for (i = 0; i < maxSize && !(g_uartRegs->FR & PL011_UARTFR_RXFE); i++) {
buf8[i] = g_uartRegs->DR;
for (i = 0; i < maxSize && !(uart->fr & PL011_UARTFR_RXFE); i++) {
buf8[i] = uart->dr;
}
return 1 + i;
}
void uartSetInterruptStatus(UartDevice dev, bool read, bool enable)
{
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
u32 mask = read ? PL011_RTI | PL011_RXI : PL011_RTI;
if (enable) {
uart->imsc |= mask;
} else {
uart->icr = mask;
uart->imsc &= ~mask;
}
}

View file

@ -16,120 +16,130 @@
#pragma once
#include "../../types.h"
#include "../../utils.h"
#include "interrupt_config.h"
// AMBA PL011 driver
// Originally from
/*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#if 0
/* PL011 Registers */
#define UARTDR 0x000
#define UARTRSR 0x004
#define UARTECR 0x004
#define UARTFR 0x018
#define UARTIMSC 0x038
#define UARTRIS 0x03C
#define UARTICR 0x044
typedef enum UartDevice {
UART_A = 0,
/* PL011 registers (out of the SBSA specification) */
#if !PL011_GENERIC_UART
#define UARTILPR 0x020
#define UARTIBRD 0x024
#define UARTFBRD 0x028
#define UARTLCR_H 0x02C
#define UARTCR 0x030
#define UARTIFLS 0x034
#define UARTMIS 0x040
#define UARTDMACR 0x048
#endif /* !PL011_GENERIC_UART */
UART_MAX,
} UartDevice;
#endif
typedef struct PL011UartRegisters {
u32 DR;
u32 dr;
union {
u32 SR;
u32 ECR;
u32 sr;
u32 ecr;
};
u32 _0x08, _0x0C, _0x10, _0x14;
u32 FR;
u32 fr;
u32 _0x1C;
u32 ILPR;
u32 IBRD;
u32 FBRD;
u32 LCR_H;
u32 CR;
u32 IFLS;
u32 IMSC;
u32 TRIS;
u32 TMIS;
u32 TICR;
u32 TDMACR;
u32 ilpr;
u32 ibrd;
u32 fbrd;
u32 lcr_h;
u32 cr;
u32 ifls;
u32 imsc;
u32 ris;
u32 mis;
u32 icr;
u32 dmacr;
} PL011UartRegisters;
/* Data status bits */
// Data status bits
#define UART_DATA_ERROR_MASK 0x0F00
/* Status reg bits */
// Status reg bits
#define UART_STATUS_ERROR_MASK 0x0F
/* Flag reg bits */
#define PL011_UARTFR_RI (1 << 8) /* Ring indicator */
#define PL011_UARTFR_TXFE (1 << 7) /* Transmit FIFO empty */
#define PL011_UARTFR_RXFF (1 << 6) /* Receive FIFO full */
#define PL011_UARTFR_TXFF (1 << 5) /* Transmit FIFO full */
#define PL011_UARTFR_RXFE (1 << 4) /* Receive FIFO empty */
#define PL011_UARTFR_BUSY (1 << 3) /* UART busy */
#define PL011_UARTFR_DCD (1 << 2) /* Data carrier detect */
#define PL011_UARTFR_DSR (1 << 1) /* Data set ready */
#define PL011_UARTFR_CTS (1 << 0) /* Clear to send */
// Errors
#define PL011_OE BIT(3) // Overrun error
#define PL011_BE BIT(2) // Break error
#define PL011_PE BIT(1) // Parity error
#define PL011_FE BIT(0) // Framing error
#define PL011_UARTFR_TXFF_BIT 5 /* Transmit FIFO full bit in UARTFR register */
#define PL011_UARTFR_RXFE_BIT 4 /* Receive FIFO empty bit in UARTFR register */
#define PL011_UARTFR_BUSY_BIT 3 /* UART busy bit in UARTFR register */
// Interrupts
#define PL011_OEI BIT(10) // Overrun error interrupt
#define PL011_BEI BIT(9) // Break error interrupt
#define PL011_PEI BIT(8) // Parity error interrupt
#define PL011_FEI BIT(7) // Framing error interrupt
#define PL011_RTI BIT(6) // Receive timeout interrupt
#define PL011_TXI BIT(5) // Transmit interrupt
#define PL011_RXI BIT(4) // Receive interrupt
#define PL011_DSRMI BIT(3) // DSR modem interrupt
#define PL011_DCDMI BIT(2) // DCD modem interrupt
#define PL011_CTSMI BIT(1) // CTS modem interrupt
#define PL011_RIMI BIT(0) // RI modem interrupt
#define PL011_ALL_INTERRUPTS MASK(11)
/* Control reg bits */
#if !PL011_GENERIC_UART
#define PL011_UARTCR_CTSEN (1 << 15) /* CTS hardware flow control enable */
#define PL011_UARTCR_RTSEN (1 << 14) /* RTS hardware flow control enable */
#define PL011_UARTCR_RTS (1 << 11) /* Request to send */
#define PL011_UARTCR_DTR (1 << 10) /* Data transmit ready. */
#define PL011_UARTCR_RXE (1 << 9) /* Receive enable */
#define PL011_UARTCR_TXE (1 << 8) /* Transmit enable */
#define PL011_UARTCR_LBE (1 << 7) /* Loopback enable */
#define PL011_UARTCR_UARTEN (1 << 0) /* UART Enable */
// Flag reg bits
#define PL011_UARTFR_RI BIT(8) // Ring indicator
#define PL011_UARTFR_TXFE BIT(7) // Transmit FIFO empty
#define PL011_UARTFR_RXFF BIT(6) // Receive FIFO full
#define PL011_UARTFR_TXFF BIT(5) // Transmit FIFO full
#define PL011_UARTFR_RXFE BIT(4) // Receive FIFO empty
#define PL011_UARTFR_BUSY BIT(3) // UART busy
#define PL011_UARTFR_DCD BIT(2) // Data carrier detect
#define PL011_UARTFR_DSR BIT(1) // Data set ready
#define PL011_UARTFR_CTS BIT(0) // Clear to send
#if !defined(PL011_LINE_CONTROL)
/* FIFO Enabled / No Parity / 8 Data bit / One Stop Bit */
#define PL011_LINE_CONTROL (PL011_UARTLCR_H_FEN | PL011_UARTLCR_H_WLEN_8)
#endif
// Control reg bits
#define PL011_UARTCR_CTSEN BIT(15) // CTS hardware flow control enable
#define PL011_UARTCR_RTSEN BIT(14) // RTS hardware flow control enable
#define PL011_UARTCR_RTS BIT(11) // Request to send
#define PL011_UARTCR_DTR BIT(10) // Data transmit ready.
#define PL011_UARTCR_RXE BIT(9) // Receive enable
#define PL011_UARTCR_TXE BIT(8) // Transmit enable
#define PL011_UARTCR_LBE BIT(7) // Loopback enable
#define PL011_UARTCR_UARTEN BIT(0) // UART Enable
/* Line Control Register Bits */
#define PL011_UARTLCR_H_SPS (1 << 7) /* Stick parity select */
// Line Control Register Bits
#define PL011_UARTLCR_H_SPS BIT(7) // Stick parity select
#define PL011_UARTLCR_H_WLEN_8 (3 << 5)
#define PL011_UARTLCR_H_WLEN_7 (2 << 5)
#define PL011_UARTLCR_H_WLEN_6 (1 << 5)
#define PL011_UARTLCR_H_WLEN_6 BIT(5)
#define PL011_UARTLCR_H_WLEN_5 (0 << 5)
#define PL011_UARTLCR_H_FEN (1 << 4) /* FIFOs Enable */
#define PL011_UARTLCR_H_STP2 (1 << 3) /* Two stop bits select */
#define PL011_UARTLCR_H_EPS (1 << 2) /* Even parity select */
#define PL011_UARTLCR_H_PEN (1 << 1) /* Parity Enable */
#define PL011_UARTLCR_H_BRK (1 << 0) /* Send break */
#define PL011_UARTLCR_H_FEN BIT(4) // FIFOs Enable
#define PL011_UARTLCR_H_STP2 BIT(3) // Two stop bits select
#define PL011_UARTLCR_H_EPS BIT(2) // Even parity select
#define PL011_UARTLCR_H_PEN BIT(1) // Parity Enable
#define PL011_UARTLCR_H_BRK BIT(0) // Send break
#endif /* !PL011_GENERIC_UART */
// FIFO level select register
#define PL011_IFLS_RX1_8 (0 << 3)
#define PL011_IFLS_RX2_8 (1 << 3)
#define PL011_IFLS_RX4_8 (2 << 3)
#define PL011_IFLS_RX6_8 (3 << 3)
#define PL011_IFLS_RX7_8 (4 << 3)
#define PL011_IFLS_TX1_8 (0 << 0)
#define PL011_IFLS_TX2_8 (1 << 0)
#define PL011_IFLS_TX4_8 (2 << 0)
#define PL011_IFLS_TX6_8 (3 << 0)
#define PL011_IFLS_TX7_8 (4 << 0)
#define UART0_BASE 0x09000000
#define UART1_BASE 0x09040000
#define UART0_CLK_IN_HZ 1
#define UART1_CLK_IN_HZ 1
#define UART_CLK_IN_HZ 1
static volatile PL011UartRegisters *const g_uartRegs = (volatile PL011UartRegisters *)UART0_BASE;
void uartInit(UartDevice dev, u32 baudRate, u32 flags);
void uartWriteData(UartDevice dev, const void *buffer, size_t size);
void uartReadData(UartDevice dev, void *buffer, size_t size);
size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize);
void uartSetInterruptStatus(UartDevice dev, bool read, bool enable);
void uartInit(u32 baudRate);
void uartWriteData(const void *buffer, size_t size);
void uartReadData(void *buffer, size_t size);
size_t uartReadDataMax(void *buffer, size_t maxSize);
static inline u16 uartGetIrqId(UartDevice dev)
{
switch (dev) {
case UART_A:
return GIC_IRQID_UART;
default:
return GIC_IRQID_SPURIOUS;
}
}

View file

@ -16,8 +16,10 @@
#pragma once
#ifdef PLATFORM_TEGRA
#include "tegra/uart.h"
#if PLATFORM_TEGRA
// TODO
/*#include "tegra/uart.h"
#define DEFAULT_UART UART_C
#define DEFAULT_UARTINV_STATUS true
@ -42,9 +44,12 @@ static inline size_t uartReadDataMax(void *buffer, size_t maxSize)
{
return uart_recv_max(DEFAULT_UART, buffer, maxSize);
}
*/
#elif defined(PLATFORM_QEMU)
#define DEFAULT_UART UART_A
#include "qemu/uart.h"
#else