thermosphere: pl011 uart refactor

This commit is contained in:
TuxSH 2020-01-10 19:45:31 +00:00
parent 018260645a
commit 8dc9be9f8e
10 changed files with 224 additions and 135 deletions

View file

@ -145,7 +145,8 @@ ifeq ($(PLATFORM), qemu)
export QEMU := qemu-system-aarch64 export QEMU := qemu-system-aarch64
QEMUFLAGS := -nographic -machine virt,virtualization=on,accel=tcg,gic-version=2 -cpu cortex-a57 -smp 4 -m 1024\ 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: all
@$(QEMU) $(QEMUFLAGS) @$(QEMU) $(QEMUFLAGS)

View file

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

View file

@ -98,7 +98,7 @@ static void initGic(void)
currentCoreCtx->gicInterfaceMask = gicd->itargetsr[0]; 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; volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
gicd->icenabler[id / 32] = BIT(id % 32); gicd->icenabler[id / 32] = BIT(id % 32);
@ -129,6 +129,9 @@ void initIrq(void)
configureInterrupt(GIC_IRQID_MAINTENANCE, IRQ_PRIORITY_HOST, true); 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); recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
} }
@ -159,7 +162,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
u32 irqId = iar & 0x3FF; u32 irqId = iar & 0x3FF;
u32 srcCore = (iar >> 10) & 7; 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) { if (irqId == GIC_IRQID_SPURIOUS) {
// Spurious interrupt received // Spurious interrupt received

View file

@ -21,6 +21,7 @@
#include "exceptions.h" #include "exceptions.h"
#include "utils.h" #include "utils.h"
#include "platform/interrupt_config.h" #include "platform/interrupt_config.h"
#include "platform/uart.h"
#define IRQ_PRIORITY_HOST 0 #define IRQ_PRIORITY_HOST 0
#define IRQ_PRIORITY_GUEST 1 #define IRQ_PRIORITY_GUEST 1
@ -49,6 +50,7 @@ extern IrqManager g_irqManager;
void initIrq(void); void initIrq(void);
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32); void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32);
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive);
static inline void generateSgiForAllOthers(ThermosphereSgi id) 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; 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 #if GIC_IRQID_NS_VIRT_HYP_TIMER != GIC_IRQID_SPURIOUS
ret = ret && id != GIC_IRQID_NS_VIRT_HYP_TIMER; ret = ret && id != GIC_IRQID_NS_VIRT_HYP_TIMER;
#endif #endif

View file

@ -45,7 +45,8 @@ void thermosphereMain(ExceptionStackFrame *frame)
initIrq(); initIrq();
if (currentCoreCtx->isBootCore) { 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); 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_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_SEC_VIRT_HYP_TIMER GIC_IRQID_SPURIOUS // SBSA: 19. Unimplemented
#define GIC_IRQID_UART 33
static inline void initGicV2Pointers(ArmGicV2 *gic) static inline void initGicV2Pointers(ArmGicV2 *gic)
{ {
gic->gicd = (volatile ArmGicV2Distributor *)0x08000000ull; gic->gicd = (volatile ArmGicV2Distributor *)0x08000000ull;

View file

@ -15,9 +15,9 @@
*/ */
#include "uart.h" #include "uart.h"
#include "../../utils.h" #include "../../irq.h"
// Adapted from ARM TF asm code // Adapted from TF asm code
// AMBA PL101 driver // AMBA PL101 driver
/* /*
@ -28,52 +28,116 @@
//115200 //115200
void uartInit(u32 baudRate) static inline volatile PL011UartRegisters *uartGetRegisters(UartDevice dev)
{ {
// First, disable UART switch (dev) {
g_uartRegs->CR &= ~PL011_UARTCR_UARTEN; case UART_A:
return (volatile PL011UartRegisters *)0x09000000;
// Set baudrate, Divisor = (Uart clock * 4) / baudrate; stored in IBRD|FBRD default:
u32 divisor = (4 * UART0_CLK_IN_HZ) / baudRate; return NULL;
g_uartRegs->IBRD = divisor >> 6;
g_uartRegs->FBRD = divisor & 0x3F;
g_uartRegs->LCR_H = PL011_LINE_CONTROL;
// Clear any pending errors
g_uartRegs->ECR = 0;
// Enable tx, rx, and uart overall
g_uartRegs->CR = PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN;
}
void uartWriteData(const void *buffer, size_t size)
{
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];
} }
} }
void uartReadData(void *buffer, size_t size) 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 * 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
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
uart->cr = PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN;
uart->imsc = PL011_RTI | PL011_RXI | PL011_RXI;
}
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 (uart->fr & PL011_UARTFR_TXFF); // while TX FIFO full
uart->dr = buf8[i];
}
}
void uartReadData(UartDevice dev, void *buffer, size_t size)
{
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
u8 *buf8 = (u8 *)buffer; u8 *buf8 = (u8 *)buffer;
size_t i; size_t i;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
while (g_uartRegs->FR & PL011_UARTFR_RXFE); while (uart->fr & PL011_UARTFR_RXFE);
buf8[i] = g_uartRegs->DR; 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; u8 *buf8 = (u8 *)buffer;
size_t i; size_t i;
for (i = 0; i < maxSize && !(g_uartRegs->FR & PL011_UARTFR_RXFE); i++) { for (i = 0; i < maxSize && !(uart->fr & PL011_UARTFR_RXFE); i++) {
buf8[i] = g_uartRegs->DR; buf8[i] = uart->dr;
} }
return 1 + i; 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 #pragma once
#include "../../types.h" #include "../../utils.h"
#include "interrupt_config.h"
// AMBA PL011 driver // AMBA PL011 driver
// Originally from
/* /*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
#if 0 typedef enum UartDevice {
/* PL011 Registers */ UART_A = 0,
#define UARTDR 0x000
#define UARTRSR 0x004
#define UARTECR 0x004
#define UARTFR 0x018
#define UARTIMSC 0x038
#define UARTRIS 0x03C
#define UARTICR 0x044
/* PL011 registers (out of the SBSA specification) */ UART_MAX,
#if !PL011_GENERIC_UART } UartDevice;
#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 */
#endif
typedef struct PL011UartRegisters { typedef struct PL011UartRegisters {
u32 DR; u32 dr;
union { union {
u32 SR; u32 sr;
u32 ECR; u32 ecr;
}; };
u32 _0x08, _0x0C, _0x10, _0x14; u32 _0x08, _0x0C, _0x10, _0x14;
u32 FR; u32 fr;
u32 _0x1C; u32 _0x1C;
u32 ILPR; u32 ilpr;
u32 IBRD; u32 ibrd;
u32 FBRD; u32 fbrd;
u32 LCR_H; u32 lcr_h;
u32 CR; u32 cr;
u32 IFLS; u32 ifls;
u32 IMSC; u32 imsc;
u32 TRIS; u32 ris;
u32 TMIS; u32 mis;
u32 TICR; u32 icr;
u32 TDMACR; u32 dmacr;
} PL011UartRegisters; } PL011UartRegisters;
/* Data status bits */ // Data status bits
#define UART_DATA_ERROR_MASK 0x0F00 #define UART_DATA_ERROR_MASK 0x0F00
/* Status reg bits */ // Status reg bits
#define UART_STATUS_ERROR_MASK 0x0F #define UART_STATUS_ERROR_MASK 0x0F
/* Flag reg bits */ // Errors
#define PL011_UARTFR_RI (1 << 8) /* Ring indicator */ #define PL011_OE BIT(3) // Overrun error
#define PL011_UARTFR_TXFE (1 << 7) /* Transmit FIFO empty */ #define PL011_BE BIT(2) // Break error
#define PL011_UARTFR_RXFF (1 << 6) /* Receive FIFO full */ #define PL011_PE BIT(1) // Parity error
#define PL011_UARTFR_TXFF (1 << 5) /* Transmit FIFO full */ #define PL011_FE BIT(0) // Framing error
#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 */
#define PL011_UARTFR_TXFF_BIT 5 /* Transmit FIFO full bit in UARTFR register */ // Interrupts
#define PL011_UARTFR_RXFE_BIT 4 /* Receive FIFO empty bit in UARTFR register */ #define PL011_OEI BIT(10) // Overrun error interrupt
#define PL011_UARTFR_BUSY_BIT 3 /* UART busy bit in UARTFR register */ #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 */ // Flag reg bits
#if !PL011_GENERIC_UART #define PL011_UARTFR_RI BIT(8) // Ring indicator
#define PL011_UARTCR_CTSEN (1 << 15) /* CTS hardware flow control enable */ #define PL011_UARTFR_TXFE BIT(7) // Transmit FIFO empty
#define PL011_UARTCR_RTSEN (1 << 14) /* RTS hardware flow control enable */ #define PL011_UARTFR_RXFF BIT(6) // Receive FIFO full
#define PL011_UARTCR_RTS (1 << 11) /* Request to send */ #define PL011_UARTFR_TXFF BIT(5) // Transmit FIFO full
#define PL011_UARTCR_DTR (1 << 10) /* Data transmit ready. */ #define PL011_UARTFR_RXFE BIT(4) // Receive FIFO empty
#define PL011_UARTCR_RXE (1 << 9) /* Receive enable */ #define PL011_UARTFR_BUSY BIT(3) // UART busy
#define PL011_UARTCR_TXE (1 << 8) /* Transmit enable */ #define PL011_UARTFR_DCD BIT(2) // Data carrier detect
#define PL011_UARTCR_LBE (1 << 7) /* Loopback enable */ #define PL011_UARTFR_DSR BIT(1) // Data set ready
#define PL011_UARTCR_UARTEN (1 << 0) /* UART Enable */ #define PL011_UARTFR_CTS BIT(0) // Clear to send
#if !defined(PL011_LINE_CONTROL) // Control reg bits
/* FIFO Enabled / No Parity / 8 Data bit / One Stop Bit */ #define PL011_UARTCR_CTSEN BIT(15) // CTS hardware flow control enable
#define PL011_LINE_CONTROL (PL011_UARTLCR_H_FEN | PL011_UARTLCR_H_WLEN_8) #define PL011_UARTCR_RTSEN BIT(14) // RTS hardware flow control enable
#endif #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 */ // Line Control Register Bits
#define PL011_UARTLCR_H_SPS (1 << 7) /* Stick parity select */ #define PL011_UARTLCR_H_SPS BIT(7) // Stick parity select
#define PL011_UARTLCR_H_WLEN_8 (3 << 5) #define PL011_UARTLCR_H_WLEN_8 (3 << 5)
#define PL011_UARTLCR_H_WLEN_7 (2 << 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_WLEN_5 (0 << 5)
#define PL011_UARTLCR_H_FEN (1 << 4) /* FIFOs Enable */ #define PL011_UARTLCR_H_FEN BIT(4) // FIFOs Enable
#define PL011_UARTLCR_H_STP2 (1 << 3) /* Two stop bits select */ #define PL011_UARTLCR_H_STP2 BIT(3) // Two stop bits select
#define PL011_UARTLCR_H_EPS (1 << 2) /* Even parity select */ #define PL011_UARTLCR_H_EPS BIT(2) // Even parity select
#define PL011_UARTLCR_H_PEN (1 << 1) /* Parity Enable */ #define PL011_UARTLCR_H_PEN BIT(1) // Parity Enable
#define PL011_UARTLCR_H_BRK (1 << 0) /* Send break */ #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 UART_CLK_IN_HZ 1
#define UART1_BASE 0x09040000
#define UART0_CLK_IN_HZ 1
#define UART1_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); static inline u16 uartGetIrqId(UartDevice dev)
void uartWriteData(const void *buffer, size_t size); {
void uartReadData(void *buffer, size_t size); switch (dev) {
size_t uartReadDataMax(void *buffer, size_t maxSize); case UART_A:
return GIC_IRQID_UART;
default:
return GIC_IRQID_SPURIOUS;
}
}

View file

@ -16,8 +16,10 @@
#pragma once #pragma once
#ifdef PLATFORM_TEGRA #if PLATFORM_TEGRA
#include "tegra/uart.h" // TODO
/*#include "tegra/uart.h"
#define DEFAULT_UART UART_C #define DEFAULT_UART UART_C
#define DEFAULT_UARTINV_STATUS true #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); return uart_recv_max(DEFAULT_UART, buffer, maxSize);
} }
*/
#elif defined(PLATFORM_QEMU) #elif defined(PLATFORM_QEMU)
#define DEFAULT_UART UART_A
#include "qemu/uart.h" #include "qemu/uart.h"
#else #else