From 1b5d7cd40eb1c1f55deedf34d3d6324498b5f000 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 8 Feb 2025 17:50:57 -0800 Subject: Hello world. --- src/gpio.h | 12 ++++++ src/kernel.c | 10 +++++ src/link.ld | 53 ++++++++++++++++++------- src/mmio.c | 23 +++++++++++ src/mmio.h | 8 ++++ src/raspi.c | 27 +++++++++++++ src/raspi.h | 4 ++ src/uart.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/uart.h | 10 +++++ 9 files changed, 257 insertions(+), 15 deletions(-) create mode 100644 src/gpio.h create mode 100644 src/mmio.c create mode 100644 src/mmio.h create mode 100644 src/raspi.c create mode 100644 src/raspi.h create mode 100644 src/uart.c create mode 100644 src/uart.h (limited to 'src') diff --git a/src/gpio.h b/src/gpio.h new file mode 100644 index 0000000..9bb442b --- /dev/null +++ b/src/gpio.h @@ -0,0 +1,12 @@ +#pragma once + +enum +{ + // The offsets for reach register. + GPIO_BASE = 0x200000, + // Controls actuation of pull up/down to ALL GPIO pins. + GPPUD = (GPIO_BASE + 0x94), + // Controls actuation of pull up/down for specific GPIO pin. + GPPUDCLK0 = (GPIO_BASE + 0x98), +}; + diff --git a/src/kernel.c b/src/kernel.c index eb7d832..f1150be 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,4 +1,14 @@ +#include +#include +#include + void main() { + const int raspi = raspi_init(); + mmio_init(raspi); + uart_init(raspi); + + uart_print("Hello world!\n"); + while (1); } diff --git a/src/link.ld b/src/link.ld index f1d1730..1f51675 100644 --- a/src/link.ld +++ b/src/link.ld @@ -1,20 +1,43 @@ +ENTRY(_start) + SECTIONS { - . = 0x80000; /* Kernel load address for AArch64 */ - .text (READONLY) : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } - .rodata (READONLY) : { *(.rodata .rodata.* .gnu.linkonce.r*) } - PROVIDE(_data = .); - .data : { *(.data .data.* .gnu.linkonce.d*) } - .bss (NOLOAD) : { - . = ALIGN(16); - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; + /* Starts at LOADER_ADDR. */ + . = 0x80000; + __start = .; + __text_start = .; + .text : + { + KEEP(*(.text.boot)) + *(.text) } - _end = .; + . = ALIGN(4096); /* align to page size */ + __text_end = .; - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} -__bss_size = (__bss_end - __bss_start); + __rodata_start = .; + .rodata : + { + *(.rodata) + } + . = ALIGN(4096); /* align to page size */ + __rodata_end = .; + __data_start = .; + .data : + { + *(.data) + } + . = ALIGN(4096); /* align to page size */ + __data_end = .; + + __bss_start = .; + .bss : + { + bss = .; + *(.bss) + } + . = ALIGN(4096); /* align to page size */ + __bss_end = .; + __bss_size = __bss_end - __bss_start; + __end = .; +} diff --git a/src/mmio.c b/src/mmio.c new file mode 100644 index 0000000..e545517 --- /dev/null +++ b/src/mmio.c @@ -0,0 +1,23 @@ +#include + +void* MMIO_BASE; + +void mmio_init(int raspi) { + switch (raspi) { + case 2: + case 3: MMIO_BASE = (void*)0x3F000000; break; // raspi2 & 3 + case 4: MMIO_BASE = (void*)0xFE000000; break; // raspi4 + default: MMIO_BASE = (void*)0x20000000; break; // raspi1, raspi zero, etc. + } +} + +#define REG_ADDR(reg) ((volatile uint32_t*)(MMIO_BASE + reg)) + +uint32_t mmio_read(uint32_t reg) { + return *REG_ADDR(reg); +} + +void mmio_write(uint32_t reg, uint32_t val) { + *REG_ADDR(reg) = val; +} + diff --git a/src/mmio.h b/src/mmio.h new file mode 100644 index 0000000..b566bbe --- /dev/null +++ b/src/mmio.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +void mmio_init(int raspi); +uint32_t mmio_read(uint32_t reg); +void mmio_write(uint32_t reg, uint32_t val); + diff --git a/src/raspi.c b/src/raspi.c new file mode 100644 index 0000000..bc76f89 --- /dev/null +++ b/src/raspi.c @@ -0,0 +1,27 @@ +#include + +#include + +int raspi_init() { + int raspi; + uint32_t reg; + + // Read the system register. +#if __aarch64__ + asm volatile ("mrs %x0, midr_el1" : "=r" (reg)); +#else + asm volatile ("mrc p15,0,%0,c0,c0,0" : "=r" (reg)); +#endif + + // Get the PartNum and detect the board. + switch ((reg >> 4) & 0xFFF) { + case 0xB76: raspi = 1; break; + case 0xC07: raspi = 2; break; + case 0xD03: raspi = 3; break; + case 0xD08: raspi = 4; break; + default: raspi = 0; break; + } + + return raspi; +} + diff --git a/src/raspi.h b/src/raspi.h new file mode 100644 index 0000000..13f73e2 --- /dev/null +++ b/src/raspi.h @@ -0,0 +1,4 @@ +#pragma once + +int raspi_init(); + diff --git a/src/uart.c b/src/uart.c new file mode 100644 index 0000000..c5823e8 --- /dev/null +++ b/src/uart.c @@ -0,0 +1,125 @@ +#include + +#include +#include + +enum +{ + // The base address for UART. + UART0_BASE = (GPIO_BASE + 0x1000), // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1 + // The offsets for reach register for the UART. + UART0_DR = (UART0_BASE + 0x00), + UART0_RSRECR = (UART0_BASE + 0x04), + UART0_FR = (UART0_BASE + 0x18), + UART0_ILPR = (UART0_BASE + 0x20), + UART0_IBRD = (UART0_BASE + 0x24), + UART0_FBRD = (UART0_BASE + 0x28), + UART0_LCRH = (UART0_BASE + 0x2C), + UART0_CR = (UART0_BASE + 0x30), + UART0_IFLS = (UART0_BASE + 0x34), + UART0_IMSC = (UART0_BASE + 0x38), + UART0_RIS = (UART0_BASE + 0x3C), + UART0_MIS = (UART0_BASE + 0x40), + UART0_ICR = (UART0_BASE + 0x44), + UART0_DMACR = (UART0_BASE + 0x48), + UART0_ITCR = (UART0_BASE + 0x80), + UART0_ITIP = (UART0_BASE + 0x84), + UART0_ITOP = (UART0_BASE + 0x88), + UART0_TDR = (UART0_BASE + 0x8C), + // The offsets for Mailbox registers. + MBOX_BASE = 0xB880, + MBOX_READ = (MBOX_BASE + 0x00), + MBOX_STATUS = (MBOX_BASE + 0x18), + MBOX_WRITE = (MBOX_BASE + 0x20) +}; + +// A mailbox message with set clock rate of PL011 to 3MHz tag. +static volatile unsigned int __attribute__((aligned(16))) mbox[9] = { + 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 +}; + +// Loop times in a way that the compiler won't optimize away. +static inline void delay(int32_t count) { + asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" + : "=r"(count): [count]"0"(count) : "cc"); +} + +void uart_init(int raspi) { + // Disable UART0. + mmio_write(UART0_CR, 0x00000000); + + // Setup the GPIO pin 14 && 15. + + // Disable pull up/down for all GPIO pins & delay for 150 cycles. + mmio_write(GPPUD, 0x00000000); + delay(150); + + // Disable pull up/down for pin 14,15 & delay for 150 cycles. + mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); + delay(150); + + // Write 0 to GPPUDCLK0 to make it take effect. + mmio_write(GPPUDCLK0, 0x00000000); + + // Clear pending interrupts. + mmio_write(UART0_ICR, 0x7FF); + + // Set integer & fractional part of baud rate. + // Divider = UART_CLOCK/(16 * Baud) + // Fraction part register = (Fractional part * 64) + 0.5 + // Baud = 115200. + + // For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. + // Set it to 3Mhz so that we can consistently set the baud rate + if (raspi >= 3) { + // UART_CLOCK = 30000000; + unsigned int r = (unsigned int) (((uint64_t)(&mbox) & ~0xF) | 8); + // Wait until we can talk to the VC. + while (mmio_read(MBOX_STATUS) & 0x80000000); + // Send our message to property channel and wait for the response. + mmio_write(MBOX_WRITE, r); + while ((mmio_read(MBOX_STATUS) & 0x40000000) || + (mmio_read(MBOX_READ) != r)); + } + + // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. + mmio_write(UART0_IBRD, 1); + // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. + mmio_write(UART0_FBRD, 40); + + // Enable FIFO & 8 bit data transmission (1 stop bit, no parity). + mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); + + // Mask all interrupts. + mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | + (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)); + + // Enable UART0, receive & transfer part of UART. + mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); +} + +void uart_putc(unsigned char c) { + // Wait for UART to become ready to transmit. + while (mmio_read(UART0_FR) & (1 << 5)); + mmio_write(UART0_DR, c); +} + +unsigned char uart_getc() { + // Wait for UART to have received something. + while (mmio_read(UART0_FR) & (1 << 4)); + return mmio_read(UART0_DR); +} + +void uart_write(const void* buffer, size_t length) { + const uint8_t* bytes = buffer; + for (size_t i = 0; i < length; ++i) { + uart_putc(*bytes++); + } +} + +void uart_print(const char* string) { + while (*string) { + uart_putc(*string++); + } +} + diff --git a/src/uart.h b/src/uart.h new file mode 100644 index 0000000..bc4669e --- /dev/null +++ b/src/uart.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +void uart_init(int raspi); +void uart_putc(unsigned char c); +unsigned char uart_getc(); +void uart_write(const void* buffer, size_t length); +void uart_print(const char* string); + -- cgit v1.2.3