From 3aaa91c61f0c3327b98d30f64b0ac1a650f0ce7e Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Sun, 25 Feb 2024 23:44:16 +1100 Subject: [PATCH] Add support for Odroid-C2 and Odroid-C4 Signed-off-by: Ivan Velickovic --- build_sdk.py | 26 +++++ docs/manual.md | 18 ++++ example/odroidc2/hello/Makefile | 55 +++++++++++ example/odroidc2/hello/hello.c | 16 +++ example/odroidc2/hello/hello.system | 11 +++ example/odroidc4/timer/Makefile | 55 +++++++++++ example/odroidc4/timer/timer.c | 145 ++++++++++++++++++++++++++++ example/odroidc4/timer/timer.system | 16 +++ loader/src/loader.c | 22 +++++ 9 files changed, 364 insertions(+) create mode 100644 example/odroidc2/hello/Makefile create mode 100644 example/odroidc2/hello/hello.c create mode 100644 example/odroidc2/hello/hello.system create mode 100644 example/odroidc4/timer/Makefile create mode 100644 example/odroidc4/timer/timer.c create mode 100644 example/odroidc4/timer/timer.system diff --git a/build_sdk.py b/build_sdk.py index 8bd131857..9a83fe7b1 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -121,6 +121,32 @@ class ConfigInfo: "hello": Path("example/imx8mq_evk/hello") } ), + BoardInfo( + name="odroidc2", + gcc_cpu="cortex-a53", + loader_link_address=0x20000000, + kernel_options={ + "KernelPlatform": "odroidc2", + "KernelIsMCS": True, + "KernelArmExportPCNTUser": True, + }, + examples={ + "hello": Path("example/odroidc2/hello") + } + ), + BoardInfo( + name="odroidc4", + gcc_cpu="cortex-a55", + loader_link_address=0x20000000, + kernel_options={ + "KernelPlatform": "odroidc4", + "KernelIsMCS": True, + "KernelArmExportPCNTUser": True, + }, + examples={ + "timer": Path("example/odroidc4/timer") + } + ), ) SUPPORTED_CONFIGS = ( diff --git a/docs/manual.md b/docs/manual.md index ca23741da..9e137cb60 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -535,6 +535,24 @@ Microkit produces a raw binary file, so when using U-Boot you must execute the i => go 0x40480000 +## Odroid-C2 + +The HardKernel Odroid-C2 is an ARM SBC based on the Amlogic Meson S905 system-on-chip. It +should be noted that the Odroid-C2 is no longer available for purchase but its successor, +the Odroid-C4, is readily available at the time of writing. + +Microkit produces a raw binary file, so when using U-Boot you must execute the image using: + + => go 0x20000000 + +## Odroid-C4 + +The HardKernel Odroid-C4 is an ARM SBC based on the Amlogic Meson S905X3 system-on-chip. + +Microkit produces a raw binary file, so when using U-Boot you must execute the image using: + + => go 0x20000000 + ## TQMa8XQP 1GB The TQMa8XQP is a system-on-module designed by TQ-Systems GmbH. diff --git a/example/odroidc2/hello/Makefile b/example/odroidc2/hello/Makefile new file mode 100644 index 000000000..b71bb74ee --- /dev/null +++ b/example/odroidc2/hello/Makefile @@ -0,0 +1,55 @@ +# +# Copyright 2021, Breakaway Consulting Pty. Ltd. +# +# SPDX-License-Identifier: BSD-2-Clause +# +ifeq ($(strip $(BUILD_DIR)),) +$(error BUILD_DIR must be specified) +endif + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(MICROKIT_BOARD)),) +$(error MICROKIT_BOARD must be specified) +endif + +ifeq ($(strip $(MICROKIT_CONFIG)),) +$(error MICROKIT_CONFIG must be specified) +endif + +TOOLCHAIN := aarch64-none-elf + +CPU := cortex-a55 + +CC := $(TOOLCHAIN)-gcc +LD := $(TOOLCHAIN)-ld +AS := $(TOOLCHAIN)-as +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit + +HELLO_OBJS := hello.o + +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) + +IMAGES := hello.elf +CFLAGS := -mcpu=$(CPU) -mstrict-align -nostdlib -ffreestanding -g -O3 -Wall -Wno-unused-function -Werror -I$(BOARD_DIR)/include +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := -lmicrokit -Tmicrokit.ld + +IMAGE_FILE = $(BUILD_DIR)/loader.img +REPORT_FILE = $(BUILD_DIR)/report.txt + +all: $(IMAGE_FILE) + +$(BUILD_DIR)/%.o: %.c Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/%.o: %.s Makefile + $(AS) -g -mcpu=$(CPU) $< -o $@ + +$(BUILD_DIR)/hello.elf: $(addprefix $(BUILD_DIR)/, $(HELLO_OBJS)) + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(addprefix $(BUILD_DIR)/, $(IMAGES)) hello.system + $(MICROKIT_TOOL) hello.system --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) diff --git a/example/odroidc2/hello/hello.c b/example/odroidc2/hello/hello.c new file mode 100644 index 000000000..83f2113f3 --- /dev/null +++ b/example/odroidc2/hello/hello.c @@ -0,0 +1,16 @@ +/* + * Copyright 2021, Breakaway Consulting Pty. Ltd. + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include + +void init(void) +{ + microkit_dbg_puts("hello, world\n"); +} + +void notified(microkit_channel ch) +{ +} \ No newline at end of file diff --git a/example/odroidc2/hello/hello.system b/example/odroidc2/hello/hello.system new file mode 100644 index 000000000..146da4bab --- /dev/null +++ b/example/odroidc2/hello/hello.system @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/example/odroidc4/timer/Makefile b/example/odroidc4/timer/Makefile new file mode 100644 index 000000000..1880f8da3 --- /dev/null +++ b/example/odroidc4/timer/Makefile @@ -0,0 +1,55 @@ +# +# Copyright 2021, Breakaway Consulting Pty. Ltd. +# +# SPDX-License-Identifier: BSD-2-Clause +# +ifeq ($(strip $(BUILD_DIR)),) +$(error BUILD_DIR must be specified) +endif + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(MICROKIT_BOARD)),) +$(error MICROKIT_BOARD must be specified) +endif + +ifeq ($(strip $(MICROKIT_CONFIG)),) +$(error MICROKIT_CONFIG must be specified) +endif + +TOOLCHAIN := aarch64-none-elf + +CPU := cortex-a55 + +CC := $(TOOLCHAIN)-gcc +LD := $(TOOLCHAIN)-ld +AS := $(TOOLCHAIN)-as +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit + +TIMER_OBJS := timer.o + +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) + +IMAGES := timer.elf +CFLAGS := -mcpu=$(CPU) -mstrict-align -nostdlib -ffreestanding -g -O3 -Wall -Wno-unused-function -Werror -I$(BOARD_DIR)/include +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := -lmicrokit -Tmicrokit.ld + +IMAGE_FILE = $(BUILD_DIR)/loader.img +REPORT_FILE = $(BUILD_DIR)/report.txt + +all: $(IMAGE_FILE) + +$(BUILD_DIR)/%.o: %.c Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +$(BUILD_DIR)/%.o: %.s Makefile + $(AS) -g -mcpu=$(CPU) $< -o $@ + +$(BUILD_DIR)/timer.elf: $(addprefix $(BUILD_DIR)/, $(TIMER_OBJS)) + $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(addprefix $(BUILD_DIR)/, $(IMAGES)) timer.system + $(MICROKIT_TOOL) timer.system --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) diff --git a/example/odroidc4/timer/timer.c b/example/odroidc4/timer/timer.c new file mode 100644 index 000000000..f0346f495 --- /dev/null +++ b/example/odroidc4/timer/timer.c @@ -0,0 +1,145 @@ +/* + * Copyright 2024, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +/* + * This is a very simple timer driver with the intention of showing + * of how to do MMIO and handle interrupts in Microkit. + */ + +uintptr_t timer_regs; + +#define TIMER_IRQ_CH 0 + +#define TIMER_REG_START 0x140 + +#define TIMER_A_INPUT_CLK 0 +#define TIMER_E_INPUT_CLK 8 +#define TIMER_A_EN (1 << 16) +#define TIMER_A_MODE (1 << 12) + +#define TIMESTAMP_TIMEBASE_SYSTEM 0b000 +#define TIMESTAMP_TIMEBASE_1_US 0b001 +#define TIMESTAMP_TIMEBASE_10_US 0b010 +#define TIMESTAMP_TIMEBASE_100_US 0b011 +#define TIMESTAMP_TIMEBASE_1_MS 0b100 + +#define TIMEOUT_TIMEBASE_1_US 0b00 +#define TIMEOUT_TIMEBASE_10_US 0b01 +#define TIMEOUT_TIMEBASE_100_US 0b10 +#define TIMEOUT_TIMEBASE_1_MS 0b11 + +#define NS_IN_US 1000ULL +#define NS_IN_MS 1000000ULL + +typedef struct { + uint32_t mux; + uint32_t timer_a; + uint32_t timer_b; + uint32_t timer_c; + uint32_t timer_d; + uint32_t unused[13]; + uint32_t timer_e; + uint32_t timer_e_hi; + uint32_t mux1; + uint32_t timer_f; + uint32_t timer_g; + uint32_t timer_h; + uint32_t timer_i; +} meson_timer_reg_t; + +typedef struct { + volatile meson_timer_reg_t *regs; + bool disable; +} meson_timer_t; + +meson_timer_t timer; + +static char hexchar(unsigned int v) +{ + return v < 10 ? '0' + v : ('a' - 10) + v; +} + +static void puthex64(uint64_t val) +{ + char buffer[16 + 3]; + buffer[0] = '0'; + buffer[1] = 'x'; + buffer[16 + 3 - 1] = 0; + for (unsigned i = 16 + 1; i > 1; i--) { + buffer[i] = hexchar(val & 0xf); + val >>= 4; + } + microkit_dbg_puts(buffer); +} + +uint64_t meson_get_time() +{ + uint64_t initial_high = timer.regs->timer_e_hi; + uint64_t low = timer.regs->timer_e; + uint64_t high = timer.regs->timer_e_hi; + if (high != initial_high) { + low = timer.regs->timer_e; + } + + uint64_t ticks = (high << 32) | low; + uint64_t time = ticks * NS_IN_US; + return time; +} + +void meson_set_timeout(uint16_t timeout, bool periodic) +{ + if (periodic) { + timer.regs->mux |= TIMER_A_MODE; + } else { + timer.regs->mux &= ~TIMER_A_MODE; + } + + timer.regs->timer_a = timeout; + + if (timer.disable) { + timer.regs->mux |= TIMER_A_EN; + timer.disable = false; + } +} + +void meson_stop_timer() +{ + timer.regs->mux &= ~TIMER_A_EN; + timer.disable = true; +} + +void init() +{ + timer.regs = (void *)(timer_regs + TIMER_REG_START); + + timer.regs->mux = TIMER_A_EN | (TIMESTAMP_TIMEBASE_1_US << TIMER_E_INPUT_CLK) | + (TIMEOUT_TIMEBASE_1_MS << TIMER_A_INPUT_CLK); + + timer.regs->timer_e = 0; + + // Have a timeout of 1 second, and have it be periodic so that it will keep recurring. + microkit_dbg_puts("Setting a timeout of 1 second.\n"); + meson_set_timeout(1000, true); +} + +void notified(microkit_channel ch) +{ + switch (ch) { + case TIMER_IRQ_CH: + microkit_dbg_puts("Got timer interrupt!\n"); + microkit_irq_ack(ch); + microkit_dbg_puts("Current time is: "); + puthex64(meson_get_time()); + microkit_dbg_puts("\n"); + break; + default: + microkit_dbg_puts("TIMER|ERROR: unexpected channel!\n"); + } +} diff --git a/example/odroidc4/timer/timer.system b/example/odroidc4/timer/timer.system new file mode 100644 index 000000000..84ac4ac47 --- /dev/null +++ b/example/odroidc4/timer/timer.system @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/loader/src/loader.c b/loader/src/loader.c index 255484621..2659df0a3 100644 --- a/loader/src/loader.c +++ b/loader/src/loader.c @@ -157,6 +157,28 @@ static void putc(uint8_t ch) while (!(*UART_REG(STAT) & STAT_TDRE)) { } *UART_REG(TRANSMIT) = ch; } +#elif defined(BOARD_odroidc2) +#define UART_BASE 0xc81004c0 +#define UART_WFIFO 0x0 +#define UART_STATUS 0xC +#define UART_TX_FULL (1 << 21) + +static void putc(uint8_t ch) +{ + while ((*UART_REG(UART_STATUS) & UART_TX_FULL)); + *UART_REG(UART_WFIFO) = ch; +} +#elif defined(BOARD_odroidc4) +#define UART_BASE 0xff803000 +#define UART_WFIFO 0x0 +#define UART_STATUS 0xC +#define UART_TX_FULL (1 << 21) + +static void putc(uint8_t ch) +{ + while ((*UART_REG(UART_STATUS) & UART_TX_FULL)); + *UART_REG(UART_WFIFO) = ch; +} #else #error Board not defined #endif