diff --git a/devices/flash-mps3an536/Makefile b/devices/flash-mps3an536/Makefile new file mode 100644 index 00000000..42c57caf --- /dev/null +++ b/devices/flash-mps3an536/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for flash-mps3an536 +# +# Copyright 2024 Phoenix Systems +# +# %LICENSE% +# + +OBJS += $(addprefix $(PREFIX_O)devices/flash-mps3an536/, flashdrv.o) diff --git a/devices/flash-mps3an536/flashdrv.c b/devices/flash-mps3an536/flashdrv.c new file mode 100644 index 00000000..37e3a87a --- /dev/null +++ b/devices/flash-mps3an536/flashdrv.c @@ -0,0 +1,154 @@ +/* + * Phoenix-RTOS + * + * plo - operating system loader + * + * MPS3 AN536 Flash driver + * + * Copyright 2020, 2024 Phoenix Systems + * Author: Aleksander Kaminski, Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include + + +#define FLASH_START_ADDR 0x08000000 +#define FLASH_SIZE 0x00800000 + + +/* On real target there's QSPI flash, but QEMU emulates it as a simple ROM */ + + +static const struct { + u32 start; + u32 end; +} flashParams = { + FLASH_START_ADDR, FLASH_START_ADDR + FLASH_SIZE +}; + + +static int flashdrv_isValidAddress(u32 off, size_t size) +{ + size_t fsize = flashParams.end - flashParams.start; + + if ((off < fsize) && ((off + size) <= fsize)) { + return 1; + } + + return 0; +} + + +/* Device interface */ +static ssize_t flashdrv_read(unsigned int minor, addr_t offs, void *buff, size_t len, time_t timeout) +{ + char *memptr; + ssize_t ret = -EINVAL; + + (void)timeout; + + if ((minor == 0) && (flashdrv_isValidAddress(offs, len) != 0)) { + memptr = (void *)flashParams.start; + + hal_memcpy(buff, memptr + offs, len); + ret = (ssize_t)len; + } + + return ret; +} + + +static ssize_t flashdrv_write(unsigned int minor, addr_t offs, const void *buff, size_t len) +{ + /* Not supported */ + return -ENOSYS; +} + + +static int flashdrv_done(unsigned int minor) +{ + if (minor != 0) { + return -EINVAL; + } + + /* Nothing to do */ + + return EOK; +} + + +static int flashdrv_sync(unsigned int minor) +{ + if (minor != 0) { + return -EINVAL; + } + + /* Nothing to do */ + + return EOK; +} + + +static int flashdrv_map(unsigned int minor, addr_t addr, size_t sz, int mode, addr_t memaddr, size_t memsz, int memmode, addr_t *a) +{ + size_t fSz; + addr_t fStart; + + if (minor != 0) { + return -EINVAL; + } + + fStart = flashParams.start; + fSz = flashParams.end - flashParams.start; + *a = fStart; + + /* Check if region is located on flash */ + if ((addr + sz) >= fSz) { + return -EINVAL; + } + + /* Check if flash is mappable to map region */ + if ((fStart <= memaddr) && ((fStart + fSz) >= (memaddr + memsz))) { + return dev_isMappable; + } + + /* Device mode cannot be higher than map mode to copy data */ + if ((mode & memmode) != mode) { + return -EINVAL; + } + + /* Data can be copied from device to map */ + return dev_isNotMappable; +} + + +static int flashdrv_init(unsigned int minor) +{ + if (minor != 0) { + return -EINVAL; + } + + return EOK; +} + + +__attribute__((constructor)) static void flashdrv_reg(void) +{ + static const dev_handler_t h = { + .init = flashdrv_init, + .done = flashdrv_done, + .read = flashdrv_read, + .write = flashdrv_write, + .erase = NULL, + .sync = flashdrv_sync, + .map = flashdrv_map + }; + + devs_register(DEV_STORAGE, 1, &h); +} diff --git a/devices/uart-cmsdk-apb/Makefile b/devices/uart-cmsdk-apb/Makefile new file mode 100644 index 00000000..dc49d1f1 --- /dev/null +++ b/devices/uart-cmsdk-apb/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for uart-cmsdk-apb +# +# Copyright 2024 Phoenix Systems +# +# %LICENSE% +# + +OBJS += $(addprefix $(PREFIX_O)devices/uart-cmsdk-apb/, uart.o) diff --git a/devices/uart-cmsdk-apb/uart.c b/devices/uart-cmsdk-apb/uart.c new file mode 100644 index 00000000..56160f29 --- /dev/null +++ b/devices/uart-cmsdk-apb/uart.c @@ -0,0 +1,299 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * ARM CMSDK APBUART Serial driver + * + * Copyright 2024 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include + + +#define BUFFER_SIZE 0x200 + +#define UART_ACTIVE_CNT (UART0_ACTIVE + UART1_ACTIVE + UART2_ACTIVE + UART3_ACTIVE + UART4_ACTIVE + UART5_ACTIVE) + +/* UART state bits */ +#define TX_BUF_FULL (1 << 0) +#define RX_BUF_FULL (1 << 1) + +/* UART control bits */ +#define TX_EN (1 << 0) +#define RX_EN (1 << 1) +#define TX_INT_EN (1 << 2) +#define RX_INT_EN (1 << 3) + +/* UART intstatus bits */ +#define TX_INT (1 << 0) +#define RX_INT (1 << 1) + + +/* UART registers */ +/* clang-format off */ +enum { data = 0, state, ctrl, intstatus, bauddiv }; +/* clang-format on */ + + +typedef struct { + vu32 *const base; + const unsigned int rxirq; + const unsigned int active; + + cbuffer_t cbuffRx; +} uart_t; + + +static struct { + u8 dataRx[UART_ACTIVE_CNT][BUFFER_SIZE]; + uart_t uarts[UART_MAX_CNT]; +} uart_common = { + .uarts = { + { .base = UART0_BASE, .rxirq = UART0_RX_IRQ, .active = UART0_ACTIVE }, + { .base = UART1_BASE, .rxirq = UART1_RX_IRQ, .active = UART1_ACTIVE }, + { .base = UART2_BASE, .rxirq = UART2_RX_IRQ, .active = UART2_ACTIVE }, + { .base = UART3_BASE, .rxirq = UART3_RX_IRQ, .active = UART3_ACTIVE }, + { .base = UART4_BASE, .rxirq = UART4_RX_IRQ, .active = UART4_ACTIVE }, + { .base = UART5_BASE, .rxirq = UART5_RX_IRQ, .active = UART5_ACTIVE }, + } +}; + + +static inline void uart_rxData(uart_t *uart) +{ + u8 c; + + while ((*(uart->base + state) & RX_BUF_FULL) != 0) { + c = *(uart->base + data) & 0xff; + lib_cbufWrite(&uart->cbuffRx, &c, 1); + } + *(uart->base + intstatus) = RX_INT; +} + + +static inline void uart_txData(uart_t *uart, const void *buff, size_t len) +{ + size_t i; + const u8 *c = buff; + + for (i = 0; i < len; i++) { + /* No hardware FIFO, wait until TX buffer is empty */ + while ((*(uart->base + state) & TX_BUF_FULL) != 0) { + } + *(uart->base + data) = c[i]; + } +} + + +static int uart_irqHandler(unsigned int n, void *data) +{ + uart_t *uart = (uart_t *)data; + u32 status = *(uart->base + state); + + if ((status & RX_BUF_FULL) != 0) { + uart_rxData(uart); + } + + return 0; +} + +/* Device interface */ + +static ssize_t uart_read(unsigned int minor, addr_t offs, void *buff, size_t len, time_t timeout) +{ + ssize_t res; + uart_t *uart; + time_t start; + + if (minor >= UART_MAX_CNT) { + return -EINVAL; + } + + uart = &uart_common.uarts[minor]; + + if (uart->active == 0) { + return -ENOSYS; + } + + start = hal_timerGet(); + while (lib_cbufEmpty(&uart->cbuffRx) != 0) { + if (hal_timerGet() - start > timeout) { + return -ETIME; + } + hal_cpuHalt(); + } + hal_interruptsDisableAll(); + res = lib_cbufRead(&uart->cbuffRx, buff, len); + hal_interruptsEnableAll(); + + return res; +} + + +static ssize_t uart_write(unsigned int minor, const void *buff, size_t len) +{ + uart_t *uart; + + if (minor >= UART_MAX_CNT) { + return -EINVAL; + } + + uart = &uart_common.uarts[minor]; + + if (uart->active == 0) { + return -ENOSYS; + } + + uart_txData(uart, buff, len); + + return len; +} + + +static ssize_t uart_safeWrite(unsigned int minor, addr_t offs, const void *buff, size_t len) +{ + ssize_t res = 0; + size_t wrote = 0; + + while (wrote < len) { + res = uart_write(minor, buff + wrote, len - wrote); + if (res < 0) { + return -ENXIO; + } + wrote += res; + } + + return len; +} + + +static int uart_sync(unsigned int minor) +{ + uart_t *uart; + + if (minor >= UART_MAX_CNT) { + return -EINVAL; + } + + uart = &uart_common.uarts[minor]; + + if (uart->active == 0) { + return -ENOSYS; + } + + /* Wait until Tx shift register is empty */ + while ((*(uart->base + state) & TX_BUF_FULL) != 0) { } + + return EOK; +} + + +static int uart_done(unsigned int minor) +{ + uart_t *uart; + + if (minor >= UART_MAX_CNT) { + return -EINVAL; + } + + uart = &uart_common.uarts[minor]; + + if (uart->active == 0) { + return -ENOSYS; + } + + (void)uart_sync(minor); + + *(uart->base + ctrl) = 0; + + hal_interruptsSet(uart->rxirq, NULL, NULL); + + return EOK; +} + + +static int uart_map(unsigned int minor, addr_t addr, size_t sz, int mode, addr_t memaddr, size_t memsz, int memmode, addr_t *a) +{ + if (minor >= UART_MAX_CNT) { + return -EINVAL; + } + + if (uart_common.uarts[minor].active == 0) { + return -ENOSYS; + } + + /* Device mode cannot be higher than map mode to copy data */ + if ((mode & memmode) != mode) { + return -EINVAL; + } + /* UART is not mappable to any region */ + return dev_isNotMappable; +} + + +static int uart_getActiveIdx(int minor) +{ + int i, idx = 0; + + for (i = 0; i < minor; i++) { + if (uart_common.uarts[i].active != 0) { + idx++; + } + } + + return idx; +} + + +static int uart_init(unsigned int minor) +{ + uart_t *uart; + void *buf; + + if (minor >= UART_MAX_CNT) { + return -EINVAL; + } + + uart = &uart_common.uarts[minor]; + + if (uart->active == 0) { + return -ENOSYS; + } + + buf = uart_common.dataRx[uart_getActiveIdx(minor)]; + + lib_cbufInit(&uart->cbuffRx, buf, BUFFER_SIZE); + + *(uart->base + bauddiv) = SYSCLK_FREQ / UART_BAUDRATE; + hal_cpuDataSyncBarrier(); + + hal_interruptsSet(uart->rxirq, uart_irqHandler, (void *)uart); + + /* Enable UART */ + *(uart->base + ctrl) = TX_EN | RX_EN | RX_INT_EN; + + return EOK; +} + + +__attribute__((constructor)) static void uart_reg(void) +{ + static const dev_handler_t h = { + .init = uart_init, + .done = uart_done, + .read = uart_read, + .write = uart_safeWrite, + .sync = uart_sync, + .map = uart_map, + }; + + devs_register(DEV_UART, UART_MAX_CNT, &h); +} diff --git a/hal/armv8r/Makefile b/hal/armv8r/Makefile new file mode 100644 index 00000000..a04a9c33 --- /dev/null +++ b/hal/armv8r/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Phoenix-RTOS loader (ARMv8M HAL) +# +# Copyright 2022 Phoenix Systems +# +# %LICENSE% +# + +ifneq (, $(findstring mps3an536, $(TARGET_SUBFAMILY))) + include hal/armv8r/mps3an536/Makefile +endif + +OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/, _exceptions.o _interrupts.o \ + cpu.o exceptions.o string.o) diff --git a/hal/armv8r/_exceptions.S b/hal/armv8r/_exceptions.S new file mode 100644 index 00000000..6a4cacdd --- /dev/null +++ b/hal/armv8r/_exceptions.S @@ -0,0 +1,114 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Exceptions handlers for Cortex-R (ARMv8) + * + * Copyright 2018, 2020, 2021, 2024 Phoenix Systems + * Author: Pawel Pisarczyk, Aleksander Kaminski, Maciej Purski, Hubert Buczynski, Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#define __ASSEMBLY__ + + +#include "cpu.h" + +.arm + + +.globl _exception_undef +.type _exception_undef, %function +_exception_undef: + cpsid if + stmfd sp, {r0-r4} + mov r0, #1 + mrs r3, spsr + tst r3, #0x20 + subeq r2, lr, #4 + subne r2, lr, #2 + b _exceptions_dispatch +.size _exception_undef, .-_exception_undef + + +.globl _exception_prefetch +.type _exception_prefetch, %function +_exception_prefetch: + cpsid if + stmfd sp, {r0-r4} + mov r0, #3 + sub r2, lr, #4 + b _exceptions_dispatch +.size _exception_prefetch, .-_exception_prefetch + + +.globl _exception_abort +.type _exception_abort, %function +_exception_abort: + cpsid if + stmfd sp, {r0-r4} + mov r0, #4 + sub r2, lr, #8 + b _exceptions_dispatch +.size _exception_abort, .-_exception_abort + + +.globl _syscalls_dispatch +.type _syscalls_dispatch, %function +_syscalls_dispatch: + cpsid if + stmfd sp, {r0-r4} + mov r0, #4 + sub r2, lr, #8 + b _exceptions_dispatch +.size _syscalls_dispatch, .-_syscalls_dispatch + + +.globl _exceptions_dispatch +.type _exceptions_dispatch, %function +_exceptions_dispatch: + mrs r3, spsr + sub r1, sp, #0x14 + mrc p15, 0, r4, c13, c0, 4 + cps #MODE_SYS + tst r3, #0x0f + movne r4, sp + stmfd r4!, {r2} + stmfd r4!, {r5-r14} + mov sp, r4 + ldmfd r1, {r4-r8} + push {r3-r8} + mrc p15, 0, r1, c6, c0, 2 + push {r1} + mrc p15, 0, r1, c5, c0, 1 + push {r1} + mrc p15, 0, r1, c6, c0, 0 + push {r1} + mrc p15, 0, r1, c5, c0, 0 + push {r1} + sub r1, sp, #4 + push {r1} + + ldr lr, =exceptions_dispatch + blx lr + + ldr sp, [sp] + add sp, sp, #20 + + pop {r11} + pop {r0-r10} + mov r12, sp + ldr sp, [r12, #0x8] + ldr lr, [r12, #0xc] + cps #MODE_IRQ + push {r11} + ldr r11, [r12, #0x0] + ldr lr, [r12, #0x10] + push {lr} + ldr r12, [r12, #0x4] + rfefd sp! +.size _exceptions_dispatch, .-_exceptions_dispatch diff --git a/hal/armv8r/_interrupts.S b/hal/armv8r/_interrupts.S new file mode 100644 index 00000000..9607ece7 --- /dev/null +++ b/hal/armv8r/_interrupts.S @@ -0,0 +1,64 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Interrupts handlers for Cortex-R (ARMv8) + * + * Copyright 2021, 2024 Phoenix Systems + * Author: Hubert Buczynski, Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#define __ASSEMBLY__ + + +#include "cpu.h" + +.arm + +.globl _interrupts_dispatch +.type _interrupts_dispatch, %function +_interrupts_dispatch: + stmfd sp, {r0-r3} /* Store AAPCS registers and lr on to the IRQ stack */ + mrs r2, spsr + sub r1, lr, #4 /* Back lr to previous instruction */ + sub r0, sp, #0x10 /* Keep address of AAPCS registers store on IRQ stack */ + + cpsie af, #MODE_SYS /* Return to SYS mode with no interrupts */ + + mov r3, sp + stmfd r3!, {r1} /* Save lr address */ + + /* Store original r4-r14 registers (original r0-r3 are still on IRQ stack) */ + stmfd r3!, {r4-r14} + mov sp, r3 + + /* Fetch original r0-r3 from IRQ stack and store on local one + * including SPSR stored in current r2 */ + ldmfd r0, {r3-r6} + push {r2-r6} + + blx interrupts_dispatch + + pop {r11} /* Load cpsr to r11 */ + pop {r0-r10} + mov r12, sp /* r12 points to r11, r12, sp, lr, pc */ + ldr sp, [r12, #0x8] + ldr lr, [r12, #0xc] + + cps #MODE_IRQ /* Change to IRQ state */ + + push {r11} /* Push cpsr to IRQ stack */ + ldr r11, [r12, #0x0] + ldr lr, [r12, #0x10] + push {lr} + ldr r12, [r12, #0x4] + + /* Return from exception - restores pc and cpsr for current mode */ + rfefd sp! + +.size _interrupts_dispatch, .-_interrupts_dispatch diff --git a/hal/armv8r/cpu.c b/hal/armv8r/cpu.c new file mode 100644 index 00000000..3820de55 --- /dev/null +++ b/hal/armv8r/cpu.c @@ -0,0 +1,41 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * ARMv8 Cortex-R + * + * Copyright 2021, 2024 Phoenix Systems + * Author: Hubert Buczynski, Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + + +void hal_interruptsDisableAll(void) +{ + __asm__ volatile("cpsid if"); +} + + +void hal_interruptsEnableAll(void) +{ + __asm__ volatile("cpsie if"); +} + + +void hal_cpuInvCache(unsigned int type, addr_t addr, size_t sz) +{ + switch (type) { + case hal_cpuDCache: + /* TODO */ + case hal_cpuICache: + /* TODO */ + default: + break; + } +} diff --git a/hal/armv8r/cpu.h b/hal/armv8r/cpu.h new file mode 100644 index 00000000..f1c27104 --- /dev/null +++ b/hal/armv8r/cpu.h @@ -0,0 +1,69 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * ARMv8 Cortex-R + * + * Copyright 2021, 2024 Phoenix Systems + * Author: Hubert Buczynski, Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + + +/* ARMv8 processor modes */ +#define MODE_USR 0x10 /* unprivileged mode in which most applications run */ +#define MODE_FIQ 0x11 /* entered on an FIQ interrupt exception */ +#define MODE_IRQ 0x12 /* entered on an IRQ interrupt exception */ +#define MODE_SVC 0x13 /* entered on reset or when a Supervisor Call instruction ( SVC ) is executed */ +#define MODE_MON 0x16 /* security extensions */ +#define MODE_ABT 0x17 /* entered on a memory access exception */ +#define MODE_HYP 0x1a /* virtualization extensions */ +#define MODE_UND 0x1b /* entered when an undefined instruction executed */ +#define MODE_SYS 0x1f /* privileged mode, sharing the register view with User mode */ + +#define MODE_MASK 0x1f +#define NO_ABORT 0x100 /* mask to disable Abort Exception */ +#define NO_IRQ 0x80 /* mask to disable IRQ */ +#define NO_FIQ 0x40 /* mask to disable FIQ */ +#define NO_INT (NO_IRQ | NO_FIQ) /* mask to disable IRQ and FIQ */ +#define THUMB_STATE 0x20 + + +#ifndef __ASSEMBLY__ + + +static inline void hal_cpuDataMemoryBarrier(void) +{ + __asm__ volatile("dmb"); +} + + +static inline void hal_cpuDataSyncBarrier(void) +{ + __asm__ volatile("dsb"); +} + + +static inline void hal_cpuInstrBarrier(void) +{ + __asm__ volatile("isb"); +} + + +static inline void hal_cpuHalt(void) +{ + __asm__ volatile("wfi"); +} + + +#endif + + +#endif diff --git a/hal/armv8r/exceptions.c b/hal/armv8r/exceptions.c new file mode 100644 index 00000000..d8c5088c --- /dev/null +++ b/hal/armv8r/exceptions.c @@ -0,0 +1,143 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Exception handling + * + * Copyright 2017, 2018, 2021 Phoenix Systems + * Author: Pawel Pisarczyk, Jakub Sejdak, Aleksander Kaminski, Hubert Buczynski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + + +typedef struct _exc_context_t { + u32 savesp; + + u32 dfsr; + u32 dfar; + u32 ifsr; + u32 ifar; + + u32 psr; + + u32 r0; + u32 r1; + u32 r2; + u32 r3; + u32 r4; + u32 r5; + u32 r6; + u32 r7; + u32 r8; + u32 r9; + u32 r10; + + u32 fp; + u32 ip; + u32 sp; + u32 lr; + + u32 pc; +} exc_context_t; + + +static const char digits[] = "0123456789abcdef"; + + +static int exceptions_i2s(const char *prefix, char *s, unsigned int i, unsigned char b, char zero) +{ + char c; + unsigned int l, k, m; + + m = hal_strlen(prefix); + hal_memcpy(s, prefix, m); + + for (k = m, l = (unsigned int)-1; l; i /= b, l /= b) { + if (!zero && !i) + break; + s[k++] = digits[i % b]; + } + + l = k--; + + while (k > m) { + c = s[m]; + s[m++] = s[k]; + s[k--] = c; + } + + return l; +} + + +void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +{ + static const char *const mnemonics[] = { + "0 #Reset", "1 #Undef", "2 #Syscall", "3 #Prefetch", + "4 #Abort", "5 #Reserved", "6 #FIRQ", "7 #IRQ" + }; + size_t i = 0; + + n &= 0x7; + + hal_strcpy(buff, "\nException: "); + hal_strcpy(buff += hal_strlen(buff), mnemonics[n]); + hal_strcpy(buff += hal_strlen(buff), "\n"); + buff += hal_strlen(buff); + + i += exceptions_i2s(" r0=", &buff[i], ctx->r0, 16, 1); + i += exceptions_i2s(" r1=", &buff[i], ctx->r1, 16, 1); + i += exceptions_i2s(" r2=", &buff[i], ctx->r2, 16, 1); + i += exceptions_i2s(" r3=", &buff[i], ctx->r3, 16, 1); + + i += exceptions_i2s("\n r4=", &buff[i], ctx->r4, 16, 1); + i += exceptions_i2s(" r5=", &buff[i], ctx->r5, 16, 1); + i += exceptions_i2s(" r6=", &buff[i], ctx->r6, 16, 1); + i += exceptions_i2s(" r7=", &buff[i], ctx->r7, 16, 1); + + i += exceptions_i2s("\n r8=", &buff[i], ctx->r8, 16, 1); + i += exceptions_i2s(" r9=", &buff[i], ctx->r9, 16, 1); + i += exceptions_i2s(" r10=", &buff[i], ctx->r10, 16, 1); + i += exceptions_i2s(" fp=", &buff[i], ctx->fp, 16, 1); + + i += exceptions_i2s("\n ip=", &buff[i], ctx->ip, 16, 1); + i += exceptions_i2s(" sp=", &buff[i], (u32)ctx + 21 * 4, 16, 1); + i += exceptions_i2s(" lr=", &buff[i], ctx->lr, 16, 1); + i += exceptions_i2s(" pc=", &buff[i], ctx->pc, 16, 1); + + i += exceptions_i2s("\npsr=", &buff[i], ctx->psr, 16, 1); + i += exceptions_i2s(" dfs=", &buff[i], ctx->dfsr, 16, 1); + i += exceptions_i2s(" dfa=", &buff[i], ctx->dfar, 16, 1); + i += exceptions_i2s(" ifs=", &buff[i], ctx->ifsr, 16, 1); + + i += exceptions_i2s("\nifa=", &buff[i], ctx->ifar, 16, 1); + + buff[i++] = '\n'; + + buff[i] = 0; +} + + +void exceptions_dispatch(unsigned int n, exc_context_t *ctx) +{ + char buff[512]; + + hal_interruptsDisableAll(); + + hal_exceptionsDumpContext(buff, ctx, n); + hal_consolePrint(buff); + +#ifdef NDEBUG + // hal_cpuReboot(); +#endif + + for (;;) { + hal_cpuHalt(); + } +} diff --git a/hal/armv8r/mps3an536/Makefile b/hal/armv8r/mps3an536/Makefile new file mode 100644 index 00000000..dd517441 --- /dev/null +++ b/hal/armv8r/mps3an536/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for Phoenix-RTOS loader (ARMv8R HAL mps3-an536) +# +# Copyright 2024 Phoenix Systems +# +# %LICENSE% +# + +LDFLAGS := $(filter-out -Tbss% , $(LDFLAGS)) +LDFLAGS := $(filter-out -Tdata% , $(LDFLAGS)) + +CFLAGS += -Ihal/armv8r/mps3an536 + +# Limited set of commands because of memory constraints on QEMU +PLO_COMMANDS ?= alias app call console dump go help kernel map mem phfs script wait + +PLO_ALLDEVICES := flash-mps3an536 uart-cmsdk-apb + +OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/$(TARGET_SUBFAMILY)/, _init.o console.o hal.o interrupts.o timer.o) diff --git a/hal/armv8r/mps3an536/_init.S b/hal/armv8r/mps3an536/_init.S new file mode 100644 index 00000000..e9b4a2d6 --- /dev/null +++ b/hal/armv8r/mps3an536/_init.S @@ -0,0 +1,107 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Low-level initialization for Cortex-R52 (ARMv8) architecture + * + * Copyright 2024 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#define __ASSEMBLY__ + +#include "hal/armv8r/cpu.h" + +.arm + +.section .init, "ax" +.globl _vector_table +.type _vector_table, %object + +.org 0 +_vector_table: + b _start + b _exception_undef + b _syscalls_dispatch + b _exception_prefetch + b _exception_abort + .word 0 + b _interrupts_dispatch + b _interrupts_dispatch + + +.global _start +.type _start, %function +_start: + /* Check if we are in HYP mode */ + mrs r1, cpsr + and r0, r1, #MODE_MASK + cmp r0, #MODE_HYP + bne .L_EL1_start + + /* Enter EL1 as decribed in section 7.3 Cortex-R52 TRM */ + /* Write HACTLR to enable accesses to various registers from EL1 */ + ldr r0, =0xb783 + mcr p15, 4, r0, c1, c0, 1 + + /* Set SPSR to SVC mode */ + bic r0, r1, #MODE_MASK + orr r0, r0, #MODE_SVC + msr SPSR_cxsf, r0 + + /* Disable HVC instruction in HCR register */ + mrc p15, 4, r0, c1, c1, 0 + ldr r1, =(1 << 29) + orr r0, r0, r1 + mcr p15, 4, r0, c1, c1, 0 + + /* Set EL1 entry point */ + ldr r0, =.L_EL1_start + msr elr_hyp, r0 + dsb + isb + eret + +.L_EL1_start: + /* Setup initial SP */ + ldr r0, =_stack + bic r0, #7 + + /* FIQ mode stack */ + msr CPSR_c, #(MODE_FIQ | NO_INT) + mov sp, r0 + sub r0, r0, #0x20 + + /* IRQ mode stack */ + msr CPSR_c, #(MODE_IRQ | NO_INT) + mov sp, r0 + sub r0, r0, #0x100 + + /* Supervisor mode stack */ + msr CPSR_c, #(MODE_SVC | NO_INT) + mov sp, r0 + sub r0, r0, #0x40 + + /* Undefined mode stack */ + msr CPSR_c, #(MODE_UND | NO_INT) + mov sp, r0 + sub r0, r0, #0x40 + + /* Abort mode stack */ + msr CPSR_c, #(MODE_ABT | NO_INT) + mov sp, r0 + sub r0, r0, #0x40 + + /* System mode stack */ + msr CPSR_c, #(MODE_SYS | NO_INT) + mov sp, r0 + + /* Jump to plo */ + ldr r8, =_startc + bx r8 +.size _start, .-_start diff --git a/hal/armv8r/mps3an536/config.h b/hal/armv8r/mps3an536/config.h new file mode 100644 index 00000000..99c3dd09 --- /dev/null +++ b/hal/armv8r/mps3an536/config.h @@ -0,0 +1,39 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Platform configuration file + * + * Copyright 2021 Phoenix Systems + * Author: Hubert Buczynski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#ifndef __ASSEMBLY__ + +#include "../types.h" +#include "../cpu.h" + +#include +#include + +#include + + +#define PATH_KERNEL "phoenix-armv8r52-mps3an536.elf" + +#endif + + +/* Import platform specific definitions */ +#include "ld/armv8r52-mps3an536.ldt" + +#endif diff --git a/hal/armv8r/mps3an536/console.c b/hal/armv8r/mps3an536/console.c new file mode 100644 index 00000000..afc4a27a --- /dev/null +++ b/hal/armv8r/mps3an536/console.c @@ -0,0 +1,74 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Console + * + * Copyright 2024 Phoenix Systems + * Authors: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + +#include + + +#define TX_BUF_FULL (1 << 0) + +#define CONCAT_(a, b) a##b +#define CONCAT(a, b) CONCAT_(a, b) + +#define UART_CONSOLE_BASE CONCAT(UART, CONCAT(UART_CONSOLE_PLO, _BASE)) + + +static struct { + volatile u32 *uart; + ssize_t (*writeHook)(int, const void *, size_t); +} halconsole_common; + + +/* UART registers */ +/* clang-format off */ +enum { data = 0, state, ctrl, intstatus, bauddiv }; +/* clang-format on */ + + +void hal_consoleSetHooks(ssize_t (*writeHook)(int, const void *, size_t)) +{ + halconsole_common.writeHook = writeHook; +} + + +void hal_consolePrint(const char *s) +{ + const char *ptr; + + for (ptr = s; *ptr != '\0'; ++ptr) { + /* No hardware FIFO, wait until TX buffer is empty */ + while ((*(halconsole_common.uart + state) & TX_BUF_FULL) != 0) { + } + *(halconsole_common.uart + data) = *ptr; + } + + if (halconsole_common.writeHook != NULL) { + (void)halconsole_common.writeHook(0, s, ptr - s); + } +} + + +void console_init(void) +{ + /* Set scaler */ + u32 scaler = SYSCLK_FREQ / UART_BAUDRATE; + halconsole_common.uart = (void *)UART_CONSOLE_BASE; + *(halconsole_common.uart + bauddiv) = scaler; + hal_cpuDataSyncBarrier(); + + /* Enable TX */ + *(halconsole_common.uart + ctrl) = 0x1; +} diff --git a/hal/armv8r/mps3an536/hal.c b/hal/armv8r/mps3an536/hal.c new file mode 100644 index 00000000..accc5a94 --- /dev/null +++ b/hal/armv8r/mps3an536/hal.c @@ -0,0 +1,185 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Hardware Abstraction Layer (ARMv8 Cortex-R) + * + * Copyright 2024 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + + +static struct { + hal_syspage_t *hs; + addr_t entry; +} hal_common; + + +/* Linker symbols */ +extern char __init_start[], __init_end[]; +extern char __text_start[], __etext[]; +extern char __rodata_start[], __rodata_end[]; +extern char __init_array_start[], __init_array_end[]; +extern char __fini_array_start[], __fini_array_end[]; +extern char __ramtext_start[], __ramtext_end[]; +extern char __data_start[], __data_end[]; +extern char __bss_start[], __bss_end[]; +extern char __heap_base[], __heap_limit[]; +extern char __stack_top[], __stack_limit[]; + + +extern void console_init(void); +extern void interrupts_init(void); +extern void timer_init(void); + + +void hal_init(void) +{ + console_init(); + interrupts_init(); + timer_init(); + + hal_common.entry = (addr_t)-1; +} + + +void hal_done(void) +{ +} + + +void hal_syspageSet(hal_syspage_t *hs) +{ + hal_common.hs = hs; +} + + +const char *hal_cpuInfo(void) +{ + return "Cortex-R52 MPS3-AN536"; +} + + +addr_t hal_kernelGetAddress(addr_t addr) +{ + return addr; +} + + +void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) +{ + *off = sizeof(unsigned); + *indirect = 1; +} + + +void hal_kernelEntryPoint(addr_t addr) +{ + hal_common.entry = addr; +} + + +int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +{ + return 0; +} + + +static void hal_getMinOverlappedRange(addr_t start, addr_t end, mapent_t *entry, mapent_t *minEntry) +{ + if ((start < entry->end) && (end > entry->start)) { + if (start > entry->start) { + entry->start = start; + } + + if (end < entry->end) { + entry->end = end; + } + + if (entry->start < minEntry->start) { + minEntry->start = entry->start; + minEntry->end = entry->end; + minEntry->type = entry->type; + } + } +} + + +int hal_memoryGetNextEntry(addr_t start, addr_t end, mapent_t *entry) +{ + int i; + mapent_t tempEntry, minEntry; + + static const mapent_t entries[] = { + { .start = (addr_t)__init_start, .end = (addr_t)__init_end, .type = hal_entryTemp }, + { .start = (addr_t)__text_start, .end = (addr_t)__etext, .type = hal_entryTemp }, + { .start = (addr_t)__rodata_start, .end = (addr_t)__rodata_end, .type = hal_entryTemp }, + { .start = (addr_t)__init_array_start, .end = (addr_t)__init_array_end, .type = hal_entryTemp }, + { .start = (addr_t)__fini_array_start, .end = (addr_t)__fini_array_end, .type = hal_entryTemp }, + { .start = (addr_t)__ramtext_start, .end = (addr_t)__ramtext_end, .type = hal_entryTemp }, + { .start = (addr_t)__data_start, .end = (addr_t)__data_end, .type = hal_entryTemp }, + { .start = (addr_t)__bss_start, .end = (addr_t)__bss_end, .type = hal_entryTemp }, + { .start = (addr_t)__heap_base, .end = (addr_t)__heap_limit, .type = hal_entryTemp }, + { .start = (addr_t)__stack_limit, .end = (addr_t)__stack_top, .type = hal_entryTemp }, + }; + + if (start == end) { + return -1; + } + + minEntry.start = (addr_t)-1; + minEntry.end = 0; + minEntry.type = 0; + + /* Syspage entry */ + tempEntry.start = (addr_t)hal_common.hs; + tempEntry.end = (addr_t)__heap_limit; + tempEntry.type = hal_entryReserved; + hal_getMinOverlappedRange(start, end, &tempEntry, &minEntry); + + for (i = 0; i < sizeof(entries) / sizeof(entries[0]); ++i) { + if (entries[i].start >= entries[i].end) { + continue; + } + tempEntry.start = entries[i].start; + tempEntry.end = entries[i].end; + tempEntry.type = entries[i].type; + hal_getMinOverlappedRange(start, end, &tempEntry, &minEntry); + } + + if (minEntry.start != (addr_t)-1) { + entry->start = minEntry.start; + entry->end = minEntry.end; + entry->type = minEntry.type; + + return 0; + } + + return -1; +} + + +int hal_cpuJump(void) +{ + if (hal_common.entry == (addr_t)-1) { + return -1; + } + + hal_interruptsDisableAll(); + + /* clang-format off */ + __asm__ volatile( + "mov r9, %1\n\t" + "bx %0" + : + : "r"(hal_common.entry), "r"((addr_t)hal_common.hs)); + /* clang-format on */ + __builtin_unreachable(); +} diff --git a/hal/armv8r/mps3an536/interrupts.c b/hal/armv8r/mps3an536/interrupts.c new file mode 100644 index 00000000..d1c06d19 --- /dev/null +++ b/hal/armv8r/mps3an536/interrupts.c @@ -0,0 +1,355 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GIC v3 driver + * + * Copyright 2024 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + + +#define SIZE_INTERRUPTS 126 +#define PPI_FIRST_IRQID 16 +#define SPI_FIRST_IRQID 32 + +#define INTCFGR_LEVEL 0 +#define INTCFGR_EDGE (1 << 1) + + +/* Cortex R52 target ID mapping + * 0 : CPU0 + ... + * NUM_CPUS - 1 : CPU(NUM_CPUS - 1) + * NUM_CPUS : Export port + */ + +/* clang-format off */ + +/* GIC memory map */ +enum { + gicd_base = 0, /* Distributor registers : 0x000000-0x00FFFF */ + gicr_ctlr_tgt0 = 262144, /* Redistributor registers for Control target 0 : 0x100000-0x10FFFF */ + gicr_sgi_ppi_tgt0 = 278528, /* Redistributor registers for SGIs and PPIs target 0 : 0x110000-0x11FFFF */ + gicr_ctlr_tgt1 = 294912, /* Redistributor registers for Control target 1 : 0x120000-0x12FFFF */ + gicr_sgi_ppi_tgt1 = 311296, /* Redistributor registers for SGIs and PPIs target 1 : 0x130000-0x13FFFF */ + gicr_ctlr_tgt2 = 327680, /* Redistributor registers for Control target 2 : 0x140000-0x14FFFF */ + gicr_sgi_ppi_tgt2 = 344064, /* Redistributor registers for SGIs and PPIs target 2 : 0x150000-0x15FFFF */ + gicr_ctlr_tgt3 = 360448, /* Redistributor registers for Control target 3 : 0x160000-0x16FFFF */ + gicr_sgi_ppi_tgt3 = 376832, /* Redistributor registers for SGIs and PPIs target 3 : 0x170000-0x17FFFF */ + gicr_ctlr_tgt4 = 393216, /* Redistributor registers for Control target 4 : 0x180000-0x18FFFF */ + gicr_sgi_ppi_tgt4 = 409600, /* Redistributor registers for SGIs and PPIs target 4 : 0x190000-0x19FFFF */ +}; + +/* Distributor register map */ +enum { + gicd_ctlr = gicd_base + 0, /* Distributor Control Register : 0x0000 */ + gicd_typer = gicd_base + 1, /* Interrupt Controller Type Register : 0x0004 */ + gicd_iidr = gicd_base + 2, /* Distributor Implementer Identification Register : 0x0008 */ + gicd_igroupr1 = gicd_base + 33, /* Interrupt Group Registers 1-30 : 0x0084 - 0x00f8 */ + gicd_isenabler1 = gicd_base + 65, /* Interrupt Set-Enable Registers 1-30 : 0x0104 - 0x0178 */ + gicd_icenabler1 = gicd_base + 97, /* Interrupt Clear-Enable Registers 1-30 : 0x0184 - 0x01f8 */ + gicd_ispendr1 = gicd_base + 129, /* Interrupt Set-Pending Registers 1-30 : 0x0204 - 0x0278 */ + gicd_icpendr1 = gicd_base + 161, /* Interrupt Clear-Pending Registers 1-30 : 0x0284 - 0x02f8 */ + gicd_isactiver1 = gicd_base + 193, /* Interrupt Set-Active Registers 1-30 : 0x0304 - 0x0378 */ + gicd_icactiver1 = gicd_base + 225, /* Interrupt Clear-Active Registers 1-30 : 0x0384 - 0x03f8 */ + gicd_ipriorityr8 = gicd_base + 264, /* Interrupt Priority Registers 8-247 : 0x0420 - 0x07df */ + gicd_icfgr2 = gicd_base + 770, /* Interrupt Configuration Registers 2-61 : 0x0c08 - 0x0cf4 */ + gicd_irouter32 = gicd_base + 6208, /* Interrupt Routing Registers 32-991 : 0x6100 - 0x7ef8 */ + gicd_pidr0 = gicd_base + 16376, /* Identification Registers 0-7 : 0xffe0 - 0xffdc */ + gicd_cidr0 = gicd_base + 16380 /* Component Identification Registers 0-3 : 0xfff0 - 0xfffc */ +}; + + +/* Redistributor register map */ +enum { + gicr_ctlr = gicr_ctlr_tgt0 + 0, /* Redistributor Control Register : 0x0000 */ + gicr_iidr = gicr_ctlr_tgt0 + 1, /* Redistributor Implementer Identification Register : 0x0004 */ + gicr_typer = gicr_ctlr_tgt0 + 2, /* Redistributor Type Register : 0x0008 - 0x000c */ + gicr_waker = gicr_ctlr_tgt0 + 5, /* Redistributor Wake Register : 0x0014 */ + gicr_igroupr0 = gicr_sgi_ppi_tgt0 + 32, /* Interrupt Group Register 0 : 0x0080 */ + gicr_isenabler0 = gicr_sgi_ppi_tgt0 + 64, /* Interrupt Set-Enable Register 0 : 0x0100 */ + gicr_icenabler0 = gicr_sgi_ppi_tgt0 + 96, /* Interrupt Clear-Enable Register 0 : 0x0180 */ + gicr_ispendr0 = gicr_sgi_ppi_tgt0 + 128, /* Interrupt Set-Pending Register 0 : 0x0200 */ + gicr_icpendr0 = gicr_sgi_ppi_tgt0 + 160, /* Interrupt Clear-Pending Register 0 : 0x0280 */ + gicr_isactiver0 = gicr_sgi_ppi_tgt0 + 192, /* Interrupt Set-Active Register 0 : 0x0300 */ + gicr_icactiver0 = gicr_sgi_ppi_tgt0 + 224, /* Interrupt Clear-Active Register 0 : 0x0380 */ + gicr_ipriorityr0 = gicr_sgi_ppi_tgt0 + 256, /* Interrupt Priority Register 0-7 : 0x0400 - 0x41c */ + gicr_icfgr0 = gicr_sgi_ppi_tgt0 + 768, /* Interrupt Configuration Register 0 : 0x0c00 */ + gicr_icfgr1 = gicr_sgi_ppi_tgt0 + 769, /* Interrupt Configuration Register 1 : 0x0c04 */ + gicr_pidr0 = gicr_ctlr_tgt0 + 16376, /* Redistributor Identification Regs 0-7 : 0xffe0 - 0xffdc */ + gicr_cidr0 = gicr_ctlr_tgt0 + 16380 /* Redistributor Component Identification Regs 0-3 : 0xfff0 - 0xfffc */ +}; + +/* clang-format on */ + + +typedef struct { + int (*f)(unsigned int, void *); + void *data; +} intr_handler_t; + + +static struct { + volatile u32 *gic; + intr_handler_t handlers[SIZE_INTERRUPTS]; +} interrupts_common; + + +static u32 gic_acknowledge(void) +{ + u32 irqn; + + /* Read the Interrupt Acknowledge Register (Group 1) */ + /* clang-format off */ + __asm__ volatile ( + "mrc p15, 0, %0, c12, c12, 0" + : "=r"(irqn) + ); + /* clang-format on */ + + return irqn & 0x3ff; +} + + +static void gic_EOI(u32 irqn) +{ + /* Update End of Interrupt register */ + /* clang-format off */ + __asm__ volatile ( + "mcr p15, 0, %0, c12, c12, 1" + : : "r"(irqn) + ); + /* clang-format on */ +} + + +void interrupts_dispatch(void) +{ + u32 irqn = gic_acknowledge(); + + if (irqn >= SIZE_INTERRUPTS) { + /* Spurious interrupt */ + return; + } + + if (interrupts_common.handlers[irqn].f != NULL) { + interrupts_common.handlers[irqn].f(irqn, interrupts_common.handlers[irqn].data); + } + + gic_EOI(irqn); +} + + +static void gic_waitRWP(int reg) +{ + /* Wait for register write to complete */ + if (reg == gicd_ctlr) { + while ((*(interrupts_common.gic + reg) & (1 << 31)) != 0) { } + } + else if (reg == gicr_ctlr) { + while ((*(interrupts_common.gic + reg) & (1 << 3)) != 0) { } + } +} + + +void hal_interruptsEnable(unsigned int irqn) +{ + if (irqn < SPI_FIRST_IRQID) { + *(interrupts_common.gic + gicr_isenabler0) = 1 << irqn; + } + else { + *(interrupts_common.gic + gicd_isenabler1 + ((irqn - SPI_FIRST_IRQID) / 32)) = 1 << (irqn & 0x1f); + } +} + + +void hal_interruptsDisable(unsigned int irqn) +{ + if (irqn < SPI_FIRST_IRQID) { + *(interrupts_common.gic + gicr_icenabler0) = 1 << irqn; + gic_waitRWP(gicr_ctlr); + } + else { + *(interrupts_common.gic + gicd_icenabler1 + ((irqn - SPI_FIRST_IRQID) / 32)) = 1 << (irqn & 0x1f); + gic_waitRWP(gicd_ctlr); + } +} + + +static void interrupts_setConfig(unsigned int irqn, u8 conf) +{ + u32 mask; + + if (irqn < PPI_FIRST_IRQID) { + mask = *(interrupts_common.gic + gicr_icfgr0 + (irqn / 16)) & ~(0x3 << ((irqn & 0xf) * 2)); + + *(interrupts_common.gic + gicr_icfgr0 + (irqn / 16)) = mask | ((conf & 0x3) << ((irqn & 0xf) * 2)); + } + else if (irqn < SPI_FIRST_IRQID) { + mask = *(interrupts_common.gic + gicr_icfgr1 + ((irqn - PPI_FIRST_IRQID) / 16)) & ~(0x3 << ((irqn & 0xf) * 2)); + + *(interrupts_common.gic + gicr_icfgr1 + ((irqn - PPI_FIRST_IRQID) / 16)) = mask | ((conf & 0x3) << ((irqn & 0xf) * 2)); + } + else { + mask = *(interrupts_common.gic + gicd_icfgr2 + ((irqn - SPI_FIRST_IRQID) / 16)) & ~(0x3 << ((irqn & 0xf) * 2)); + + *(interrupts_common.gic + gicd_icfgr2 + ((irqn - SPI_FIRST_IRQID) >> 4)) = mask | ((conf & 0x3) << ((irqn & 0xf) * 2)); + } +} + + +static void interrupts_setPriority(unsigned int irqn, u32 priority) +{ + u32 mask; + + if (irqn < SPI_FIRST_IRQID) { + mask = *(interrupts_common.gic + gicr_ipriorityr0 + (irqn / 4)) & ~(0xff << ((irqn & 0x3) * 8)); + + *(interrupts_common.gic + gicr_ipriorityr0 + (irqn / 4)) = mask | ((priority & 0xff) << ((irqn & 0x3) * 8)); + } + else { + mask = *(interrupts_common.gic + gicd_ipriorityr8 + ((irqn - SPI_FIRST_IRQID) / 4)) & ~(0xff << ((irqn & 0x3) * 8)); + + *(interrupts_common.gic + gicd_ipriorityr8 + ((irqn - SPI_FIRST_IRQID) / 4)) = mask | ((priority & 0xff) << ((irqn & 0x3) * 8)); + } +} + + +static void interrupts_setGroup(unsigned int irqn, u32 group) +{ + if (irqn < SPI_FIRST_IRQID) { + if (group == 0) { + *(interrupts_common.gic + gicr_igroupr0) &= ~(1 << irqn); + } + else { + *(interrupts_common.gic + gicr_igroupr0) |= 1 << irqn; + } + } + else { + if (group == 0) { + *(interrupts_common.gic + gicd_igroupr1 + ((irqn - SPI_FIRST_IRQID) / 32)) &= ~(1 << (irqn & 0x1f)); + } + else { + *(interrupts_common.gic + gicd_igroupr1 + ((irqn - SPI_FIRST_IRQID) / 32)) |= 1 << (irqn & 0x1f); + } + } +} + + +int hal_interruptsSet(unsigned int n, int (*f)(unsigned int, void *), void *data) +{ + if (n >= SIZE_INTERRUPTS) { + return -1; + } + + hal_interruptsDisableAll(); + interrupts_common.handlers[n].data = data; + interrupts_common.handlers[n].f = f; + + if (f == NULL) { + hal_interruptsDisable(n); + } + else { + interrupts_setGroup(n, 1); + interrupts_setPriority(n, 0xa); + interrupts_setConfig(n, INTCFGR_EDGE); + hal_interruptsEnable(n); + } + + hal_interruptsEnableAll(); + + return 0; +} + + +void interrupts_init(void) +{ + u32 i, val; + + /* Read GIC base address (IMP_CBAR) */ + /* clang-format off */ + __asm__ volatile ( + "mrc p15, 1, %0, c15, c3, 0" + : "=r"(interrupts_common.gic) + ); + /* clang-format on */ + + /* Cortex-R52: + * - Only supports GICv3 accesses + * - No security support + */ + + /*------------- Configure interrupt controller -------------*/ + + /* Enable Group 1 interrupts */ + *(interrupts_common.gic + gicd_ctlr) = (1 << 1); + gic_waitRWP(gicd_ctlr); + + /* Clear ProcessorSleep */ + *(interrupts_common.gic + gicr_waker) &= ~(1 << 1); + + /* Wait for ChildrenAsleep to become 0 */ + while ((*(interrupts_common.gic + gicr_waker) & (1 << 2)) != 0) { } + + /* ICC_SRE SRE bit fixed 1, no need to write */ + + /* Set priority mask (ICC_PMR register) */ + /* clang-format off */ + __asm__ volatile ( + "mcr p15, 0, %0, c4, c6, 0" + : : "r"(0xff) + ); + + /* Setup ICC_CTLR register */ + __asm__ volatile ( + "mrc p15, 0, %0, c12, c12, 4" + : "=r"(val) + ); + + /* Set EOI mode 0 */ + val &= ~(1 << 1); + + __asm__ volatile ( + "mcr p15, 0, %0, c12, c12, 4" + : : "r"(val) + ); + + /* Enable Group 1 interrupts (ICC_IGRPEN1 register) */ + __asm__ volatile ( + "mcr p15, 0, %0, c12, c12, 7" + : : "r"(1) + ); + /* clang-format on */ + + /*------------- Configure interrupt sources -------------*/ + + for (i = SPI_FIRST_IRQID; i < SIZE_INTERRUPTS; i++) { + hal_interruptsDisable(i); + } + + gic_waitRWP(gicd_ctlr); + + for (i = SPI_FIRST_IRQID; i < SIZE_INTERRUPTS; i += 4) { + /* Set default priority (SPIs) */ + *(interrupts_common.gic + gicd_ipriorityr8 + ((i - SPI_FIRST_IRQID) / 4)) = 0xa0a0a0a0; + } + + for (i = 0; i < SPI_FIRST_IRQID; i += 4) { + /* Set default priority (SGIs/PPIs) */ + *(interrupts_common.gic + gicr_ipriorityr0 + (i / 4)) = 0xa0a0a0a0; + } + + /* Disable PPIs/SGIs */ + *(interrupts_common.gic + gicr_icenabler0) = 0xffffffff; + gic_waitRWP(gicr_ctlr); + + hal_interruptsEnableAll(); +} diff --git a/hal/armv8r/mps3an536/timer.c b/hal/armv8r/mps3an536/timer.c new file mode 100644 index 00000000..0477b64c --- /dev/null +++ b/hal/armv8r/mps3an536/timer.c @@ -0,0 +1,87 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * ARM Dual Timer driver + * + * Copyright 2024 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + +/* Timer registers */ +/* clang-format off */ +enum { + timer1_load = 0, timer1_value, timer1_ctrl, timer1_intclr, timer1_ris, timer1_mis, timer1_bgload, + timer2_load = 8, timer2_value, timer2_ctrl, timer2_intclr, timer2_ris, timer2_mis, timer2_bgload +}; +/* clang-format on */ + + +static struct { + volatile u32 *base; + volatile time_t time; +} timer_common = { + .base = (volatile u32 *)TIMER_BASE +}; + + +static int timer_isr(unsigned int irq, void *data) +{ + (void)irq; + (void)data; + + if ((*(timer_common.base + timer1_mis) & 0x1) != 0) { + *(timer_common.base + timer1_intclr) = 0; + timer_common.time++; + hal_cpuDataSyncBarrier(); + } + + return 0; +} + + +time_t hal_timerGet(void) +{ + time_t val; + + hal_interruptsDisableAll(); + val = timer_common.time; + hal_interruptsEnableAll(); + + return val; +} + + +void timer_done(void) +{ + hal_interruptsSet(TIMER_IRQ, NULL, NULL); + + /* Disable timer */ + *(timer_common.base + timer1_ctrl) &= ~(1 << 7); +} + + +void timer_init(void) +{ + /* Reset timer */ + *(timer_common.base + timer1_ctrl) &= ~(1 << 7); + *(timer_common.base + timer1_value) = 0; + + /* Periodic mode, 32-bit, enable interrupt */ + *(timer_common.base + timer1_ctrl) = (1 << 6) | (1 << 5) | (1 << 1); + hal_cpuDataSyncBarrier(); + *(timer_common.base + timer1_load) = (SYSCLK_FREQ / 1000) - 1; + hal_cpuDataSyncBarrier(); + + hal_interruptsSet(TIMER_IRQ, timer_isr, NULL); + + /* Enable timer */ + *(timer_common.base + timer1_ctrl) |= (1 << 7); +} diff --git a/hal/armv8r/string.c b/hal/armv8r/string.c new file mode 100644 index 00000000..d8982a1d --- /dev/null +++ b/hal/armv8r/string.c @@ -0,0 +1,271 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * HAL basic routines + * + * Copyright 2017, 2022, 2024 Phoenix Systems + * Copyright 2001, 2005-2006 Pawel Pisarczyk + * Author: Pawel Pisarczyk, Artur Wodejko, Aleksander Kaminski, Damian Loewnau + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +/* Copy of armv7 Cortex-M architecture code */ + +#include + + +/* clang-format off */ +void *hal_memcpy(void *dst, const void *src, size_t l) +{ + void *ret = dst; + + __asm__ volatile + (" \ + orr r3, %0, %1; \ + lsls r3, r3, #30; \ + bne 2f; \ + 1: \ + cmp %2, #4; \ + ittt hs; \ + ldrhs r3, [%1], #4; \ + strhs r3, [%0], #4; \ + subshs %2, #4; \ + bhs 1b; \ + 2: \ + cmp %2, #0; \ + ittt ne; \ + ldrbne r3, [%1], #1; \ + strbne r3, [%0], #1; \ + subsne %2, #1; \ + bne 2b" + : "+r" (dst), "+r" (src), "+r" (l) + : + : "r3", "memory", "cc"); + return ret; +} + + +int hal_memcmp(const void *ptr1, const void *ptr2, size_t num) +{ + int res = 0; + + __asm__ volatile + (" \ + 1: \ + cmp %3, #0; \ + beq 3f; \ + sub %3, #1; \ + ldrb r3, [%1], #1; \ + ldrb r4, [%2], #1; \ + cmp r3, r4; \ + beq 1b; \ + blo 2f; \ + mov %0, #1; \ + b 3f; \ + 2: \ + mov %0, #-1; \ + 3: " + : "+r" (res), "+r" (ptr1), "+r" (ptr2), "+r" (num) + : + : "r3", "r4", "memory", "cc"); + + return res; +} + + +void hal_memset(void *dst, int v, size_t l) +{ + unsigned int v1 = v & 0xff; + unsigned int tmp; + + tmp = (v1 << 8) | v1; + tmp |= (tmp << 16); + + __asm__ volatile + (" \ + lsls r3, %0, #30; \ + bne 2f; \ + 1: \ + cmp %2, #4; \ + itt hs; \ + strhs %1, [%0], #4; \ + subshs %2, #4; \ + bhs 1b; \ + 2: \ + cmp %2, #0; \ + itt ne; \ + strbne %1, [%0], #1; \ + subsne %2, #1; \ + bne 2b" + : "+r"(dst), "+r" (tmp), "+r" (l) + : + : "r3", "memory", "cc"); +} + + +size_t hal_strlen(const char *s) +{ + size_t k = 0; + + __asm__ volatile + (" \ + 1: \ + ldrb r1, [%1, %0]; \ + cbz r1, 2f; \ + add %0, #1; \ + b 1b; \ + 2:" + : "+r" (k), "+r" (s) + : + : "r1", "memory", "cc"); + + return k; +} + + +int hal_strcmp(const char *s1, const char *s2) +{ + int res = 0; + + __asm__ volatile + (" \ + 1: \ + ldrb r2, [%1], #1; \ + ldrb r3, [%2], #1; \ + cbz r2, 2f; \ + cmp r2, r3; \ + beq 1b; \ + blo 3f; \ + mov %0, #1; \ + b 4f; \ + 2: \ + cmp r3, #0; \ + beq 4f; \ + 3: \ + mov %0, #-1; \ + 4: " + : "+r" (res), "+r" (s1), "+r" (s2) + : + : "r2", "r3", "memory", "cc"); + + return res; +} + + +int hal_strncmp(const char *s1, const char *s2, size_t count) +{ + int res = 0; + + __asm__ volatile + (" \ + 1: \ + cmp %3, #0; \ + beq 4f; \ + sub %3, #1; \ + ldrb r3, [%1], #1; \ + ldrb r4, [%2], #1; \ + cbz r3, 2f; \ + cmp r3, r4; \ + beq 1b; \ + blo 3f; \ + mov %0, #1; \ + b 4f; \ + 2: \ + cmp r4, #0; \ + beq 4f; \ + 3: \ + mov %0, #-1; \ + 4: " + : "+r" (res), "+r" (s1), "+r" (s2), "+r" (count) + : + : "r3", "r4", "memory", "cc"); + + return res; +} + + +char *hal_strcpy(char *dest, const char *src) +{ + char *p = dest; + + __asm__ volatile + (" \ + 1: \ + ldrb r3, [%1], #1; \ + strb r3, [%0], #1; \ + cmp r3, #0; \ + bne 1b" + : "+r" (p), "+r" (src) + : + : "r3", "memory", "cc"); + + return dest; +} + + +char *hal_strncpy(char *dest, const char *src, size_t n) +{ + char *p = dest; + + __asm__ volatile + (" \ + cmp %2, #0; \ + beq 2f; \ + 1: \ + ldrb r3, [%1], #1; \ + strb r3, [%0], #1; \ + cbz r3, 2f; \ + subs %2, #1; \ + bne 1b; \ + 2:" + : "+r" (p), "+r" (src), "+r" (n) + : + : "r3", "memory", "cc"); + + return dest; +} +/* clang-format on */ + + +char *hal_strchr(const char *s, int c) +{ + do { + if (*s == (char)c) { + return (char *)s; + } + } while (*(s++)); + + return NULL; +} + + +int hal_i2s(char *prefix, char *s, unsigned int i, unsigned char b, char zero) +{ + static const char digits[] = "0123456789abcdef"; + char c; + unsigned int l, k, m; + + m = hal_strlen(prefix); + hal_memcpy(s, prefix, m); + + for (k = m, l = (unsigned int)-1; l; i /= b, l /= b) { + if (!zero && !i) + break; + s[k++] = digits[i % b]; + } + + l = k--; + + while (k > m) { + c = s[m]; + s[m++] = s[k]; + s[k--] = c; + } + + return l; +} diff --git a/hal/armv8r/types.h b/hal/armv8r/types.h new file mode 100644 index 00000000..dab4306d --- /dev/null +++ b/hal/armv8r/types.h @@ -0,0 +1,45 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Types + * + * Copyright 2021, 2024 Phoenix Systems + * Author: Hubert Buczynski, Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _TYPES_H_ +#define _TYPES_H_ + + +#define NULL ((void *)0) + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef long long s64; + +typedef volatile unsigned char vu8; +typedef volatile unsigned short vu16; +typedef volatile unsigned int vu32; + +typedef vu8 *reg8; +typedef vu16 *reg16; +typedef vu32 *reg32; + +typedef unsigned int addr_t; +typedef unsigned int size_t; +typedef int ssize_t; +typedef unsigned long long time_t; + +#endif diff --git a/ld/armv8r52-mps3an536.ldt b/ld/armv8r52-mps3an536.ldt new file mode 100644 index 00000000..971f3bf6 --- /dev/null +++ b/ld/armv8r52-mps3an536.ldt @@ -0,0 +1,50 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Linker Template and Platform Config for MPS3 AN536 + * + * Copyright 2024 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef ARMV8R52_MPS3AN536_LDT +#define ARMV8R52_MPS3AN536_LDT + + +/* Platform specific definitions */ +#define SIZE_PAGE 0x200 +#define SIZE_STACK (8 * SIZE_PAGE) +#define SIZE_HEAP (8 * SIZE_PAGE) +#define AREA_KERNEL 0x40000 + + +#if defined(__LINKER__) + +/* Memory map setup */ +MEMORY +{ + m_atcm (rwx) : ORIGIN = 0x00000000, LENGTH = 32k + m_bram (rwx) : ORIGIN = 0x10000000 + AREA_KERNEL, LENGTH = 512k - AREA_KERNEL +} + +REGION_ALIAS("PLO_IMAGE", m_atcm); +REGION_ALIAS("TCM_TEXT", m_atcm); +REGION_ALIAS("DATA", m_bram); +REGION_ALIAS("BSS", m_bram); +REGION_ALIAS("HEAP", m_bram); +REGION_ALIAS("STACK", m_bram); + +#include "common/plo-arm.lds" + + +#endif /* __LINKER__ */ + + +#endif /* ARMV8M33_NRF9160_LDT */