diff options
| author | 3gg <3gg@shellblade.net> | 2025-02-08 17:50:57 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-02-08 17:50:57 -0800 |
| commit | 1b5d7cd40eb1c1f55deedf34d3d6324498b5f000 (patch) | |
| tree | a0bc21168f8270ee5fcb139498131dff884a7450 /src | |
| parent | 0e1595330211351823e68691fca013bb47772aeb (diff) | |
Hello world.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gpio.h | 12 | ||||
| -rw-r--r-- | src/kernel.c | 10 | ||||
| -rw-r--r-- | src/link.ld | 53 | ||||
| -rw-r--r-- | src/mmio.c | 23 | ||||
| -rw-r--r-- | src/mmio.h | 8 | ||||
| -rw-r--r-- | src/raspi.c | 27 | ||||
| -rw-r--r-- | src/raspi.h | 4 | ||||
| -rw-r--r-- | src/uart.c | 125 | ||||
| -rw-r--r-- | src/uart.h | 10 |
9 files changed, 257 insertions, 15 deletions
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 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | enum | ||
| 4 | { | ||
| 5 | // The offsets for reach register. | ||
| 6 | GPIO_BASE = 0x200000, | ||
| 7 | // Controls actuation of pull up/down to ALL GPIO pins. | ||
| 8 | GPPUD = (GPIO_BASE + 0x94), | ||
| 9 | // Controls actuation of pull up/down for specific GPIO pin. | ||
| 10 | GPPUDCLK0 = (GPIO_BASE + 0x98), | ||
| 11 | }; | ||
| 12 | |||
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 @@ | |||
| 1 | #include <mmio.h> | ||
| 2 | #include <raspi.h> | ||
| 3 | #include <uart.h> | ||
| 4 | |||
| 1 | void main() { | 5 | void main() { |
| 6 | const int raspi = raspi_init(); | ||
| 7 | mmio_init(raspi); | ||
| 8 | uart_init(raspi); | ||
| 9 | |||
| 10 | uart_print("Hello world!\n"); | ||
| 11 | |||
| 2 | while (1); | 12 | while (1); |
| 3 | } | 13 | } |
| 4 | 14 | ||
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 @@ | |||
| 1 | ENTRY(_start) | ||
| 2 | |||
| 1 | SECTIONS | 3 | SECTIONS |
| 2 | { | 4 | { |
| 3 | . = 0x80000; /* Kernel load address for AArch64 */ | 5 | /* Starts at LOADER_ADDR. */ |
| 4 | .text (READONLY) : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } | 6 | . = 0x80000; |
| 5 | .rodata (READONLY) : { *(.rodata .rodata.* .gnu.linkonce.r*) } | 7 | __start = .; |
| 6 | PROVIDE(_data = .); | 8 | __text_start = .; |
| 7 | .data : { *(.data .data.* .gnu.linkonce.d*) } | 9 | .text : |
| 8 | .bss (NOLOAD) : { | 10 | { |
| 9 | . = ALIGN(16); | 11 | KEEP(*(.text.boot)) |
| 10 | __bss_start = .; | 12 | *(.text) |
| 11 | *(.bss .bss.*) | ||
| 12 | *(COMMON) | ||
| 13 | __bss_end = .; | ||
| 14 | } | 13 | } |
| 15 | _end = .; | 14 | . = ALIGN(4096); /* align to page size */ |
| 15 | __text_end = .; | ||
| 16 | 16 | ||
| 17 | /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } | 17 | __rodata_start = .; |
| 18 | } | 18 | .rodata : |
| 19 | __bss_size = (__bss_end - __bss_start); | 19 | { |
| 20 | *(.rodata) | ||
| 21 | } | ||
| 22 | . = ALIGN(4096); /* align to page size */ | ||
| 23 | __rodata_end = .; | ||
| 20 | 24 | ||
| 25 | __data_start = .; | ||
| 26 | .data : | ||
| 27 | { | ||
| 28 | *(.data) | ||
| 29 | } | ||
| 30 | . = ALIGN(4096); /* align to page size */ | ||
| 31 | __data_end = .; | ||
| 32 | |||
| 33 | __bss_start = .; | ||
| 34 | .bss : | ||
| 35 | { | ||
| 36 | bss = .; | ||
| 37 | *(.bss) | ||
| 38 | } | ||
| 39 | . = ALIGN(4096); /* align to page size */ | ||
| 40 | __bss_end = .; | ||
| 41 | __bss_size = __bss_end - __bss_start; | ||
| 42 | __end = .; | ||
| 43 | } | ||
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 @@ | |||
| 1 | #include <mmio.h> | ||
| 2 | |||
| 3 | void* MMIO_BASE; | ||
| 4 | |||
| 5 | void mmio_init(int raspi) { | ||
| 6 | switch (raspi) { | ||
| 7 | case 2: | ||
| 8 | case 3: MMIO_BASE = (void*)0x3F000000; break; // raspi2 & 3 | ||
| 9 | case 4: MMIO_BASE = (void*)0xFE000000; break; // raspi4 | ||
| 10 | default: MMIO_BASE = (void*)0x20000000; break; // raspi1, raspi zero, etc. | ||
| 11 | } | ||
| 12 | } | ||
| 13 | |||
| 14 | #define REG_ADDR(reg) ((volatile uint32_t*)(MMIO_BASE + reg)) | ||
| 15 | |||
| 16 | uint32_t mmio_read(uint32_t reg) { | ||
| 17 | return *REG_ADDR(reg); | ||
| 18 | } | ||
| 19 | |||
| 20 | void mmio_write(uint32_t reg, uint32_t val) { | ||
| 21 | *REG_ADDR(reg) = val; | ||
| 22 | } | ||
| 23 | |||
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 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | |||
| 5 | void mmio_init(int raspi); | ||
| 6 | uint32_t mmio_read(uint32_t reg); | ||
| 7 | void mmio_write(uint32_t reg, uint32_t val); | ||
| 8 | |||
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 @@ | |||
| 1 | #include <raspi.h> | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | |||
| 5 | int raspi_init() { | ||
| 6 | int raspi; | ||
| 7 | uint32_t reg; | ||
| 8 | |||
| 9 | // Read the system register. | ||
| 10 | #if __aarch64__ | ||
| 11 | asm volatile ("mrs %x0, midr_el1" : "=r" (reg)); | ||
| 12 | #else | ||
| 13 | asm volatile ("mrc p15,0,%0,c0,c0,0" : "=r" (reg)); | ||
| 14 | #endif | ||
| 15 | |||
| 16 | // Get the PartNum and detect the board. | ||
| 17 | switch ((reg >> 4) & 0xFFF) { | ||
| 18 | case 0xB76: raspi = 1; break; | ||
| 19 | case 0xC07: raspi = 2; break; | ||
| 20 | case 0xD03: raspi = 3; break; | ||
| 21 | case 0xD08: raspi = 4; break; | ||
| 22 | default: raspi = 0; break; | ||
| 23 | } | ||
| 24 | |||
| 25 | return raspi; | ||
| 26 | } | ||
| 27 | |||
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 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | int raspi_init(); | ||
| 4 | |||
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 @@ | |||
| 1 | #include <uart.h> | ||
| 2 | |||
| 3 | #include <gpio.h> | ||
| 4 | #include <mmio.h> | ||
| 5 | |||
| 6 | enum | ||
| 7 | { | ||
| 8 | // The base address for UART. | ||
| 9 | UART0_BASE = (GPIO_BASE + 0x1000), // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1 | ||
| 10 | // The offsets for reach register for the UART. | ||
| 11 | UART0_DR = (UART0_BASE + 0x00), | ||
| 12 | UART0_RSRECR = (UART0_BASE + 0x04), | ||
| 13 | UART0_FR = (UART0_BASE + 0x18), | ||
| 14 | UART0_ILPR = (UART0_BASE + 0x20), | ||
| 15 | UART0_IBRD = (UART0_BASE + 0x24), | ||
| 16 | UART0_FBRD = (UART0_BASE + 0x28), | ||
| 17 | UART0_LCRH = (UART0_BASE + 0x2C), | ||
| 18 | UART0_CR = (UART0_BASE + 0x30), | ||
| 19 | UART0_IFLS = (UART0_BASE + 0x34), | ||
| 20 | UART0_IMSC = (UART0_BASE + 0x38), | ||
| 21 | UART0_RIS = (UART0_BASE + 0x3C), | ||
| 22 | UART0_MIS = (UART0_BASE + 0x40), | ||
| 23 | UART0_ICR = (UART0_BASE + 0x44), | ||
| 24 | UART0_DMACR = (UART0_BASE + 0x48), | ||
| 25 | UART0_ITCR = (UART0_BASE + 0x80), | ||
| 26 | UART0_ITIP = (UART0_BASE + 0x84), | ||
| 27 | UART0_ITOP = (UART0_BASE + 0x88), | ||
| 28 | UART0_TDR = (UART0_BASE + 0x8C), | ||
| 29 | // The offsets for Mailbox registers. | ||
| 30 | MBOX_BASE = 0xB880, | ||
| 31 | MBOX_READ = (MBOX_BASE + 0x00), | ||
| 32 | MBOX_STATUS = (MBOX_BASE + 0x18), | ||
| 33 | MBOX_WRITE = (MBOX_BASE + 0x20) | ||
| 34 | }; | ||
| 35 | |||
| 36 | // A mailbox message with set clock rate of PL011 to 3MHz tag. | ||
| 37 | static volatile unsigned int __attribute__((aligned(16))) mbox[9] = { | ||
| 38 | 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 | ||
| 39 | }; | ||
| 40 | |||
| 41 | // Loop <delay> times in a way that the compiler won't optimize away. | ||
| 42 | static inline void delay(int32_t count) { | ||
| 43 | asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" | ||
| 44 | : "=r"(count): [count]"0"(count) : "cc"); | ||
| 45 | } | ||
| 46 | |||
| 47 | void uart_init(int raspi) { | ||
| 48 | // Disable UART0. | ||
| 49 | mmio_write(UART0_CR, 0x00000000); | ||
| 50 | |||
| 51 | // Setup the GPIO pin 14 && 15. | ||
| 52 | |||
| 53 | // Disable pull up/down for all GPIO pins & delay for 150 cycles. | ||
| 54 | mmio_write(GPPUD, 0x00000000); | ||
| 55 | delay(150); | ||
| 56 | |||
| 57 | // Disable pull up/down for pin 14,15 & delay for 150 cycles. | ||
| 58 | mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); | ||
| 59 | delay(150); | ||
| 60 | |||
| 61 | // Write 0 to GPPUDCLK0 to make it take effect. | ||
| 62 | mmio_write(GPPUDCLK0, 0x00000000); | ||
| 63 | |||
| 64 | // Clear pending interrupts. | ||
| 65 | mmio_write(UART0_ICR, 0x7FF); | ||
| 66 | |||
| 67 | // Set integer & fractional part of baud rate. | ||
| 68 | // Divider = UART_CLOCK/(16 * Baud) | ||
| 69 | // Fraction part register = (Fractional part * 64) + 0.5 | ||
| 70 | // Baud = 115200. | ||
| 71 | |||
| 72 | // For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. | ||
| 73 | // Set it to 3Mhz so that we can consistently set the baud rate | ||
| 74 | if (raspi >= 3) { | ||
| 75 | // UART_CLOCK = 30000000; | ||
| 76 | unsigned int r = (unsigned int) (((uint64_t)(&mbox) & ~0xF) | 8); | ||
| 77 | // Wait until we can talk to the VC. | ||
| 78 | while (mmio_read(MBOX_STATUS) & 0x80000000); | ||
| 79 | // Send our message to property channel and wait for the response. | ||
| 80 | mmio_write(MBOX_WRITE, r); | ||
| 81 | while ((mmio_read(MBOX_STATUS) & 0x40000000) || | ||
| 82 | (mmio_read(MBOX_READ) != r)); | ||
| 83 | } | ||
| 84 | |||
| 85 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. | ||
| 86 | mmio_write(UART0_IBRD, 1); | ||
| 87 | // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. | ||
| 88 | mmio_write(UART0_FBRD, 40); | ||
| 89 | |||
| 90 | // Enable FIFO & 8 bit data transmission (1 stop bit, no parity). | ||
| 91 | mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); | ||
| 92 | |||
| 93 | // Mask all interrupts. | ||
| 94 | mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | | ||
| 95 | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)); | ||
| 96 | |||
| 97 | // Enable UART0, receive & transfer part of UART. | ||
| 98 | mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); | ||
| 99 | } | ||
| 100 | |||
| 101 | void uart_putc(unsigned char c) { | ||
| 102 | // Wait for UART to become ready to transmit. | ||
| 103 | while (mmio_read(UART0_FR) & (1 << 5)); | ||
| 104 | mmio_write(UART0_DR, c); | ||
| 105 | } | ||
| 106 | |||
| 107 | unsigned char uart_getc() { | ||
| 108 | // Wait for UART to have received something. | ||
| 109 | while (mmio_read(UART0_FR) & (1 << 4)); | ||
| 110 | return mmio_read(UART0_DR); | ||
| 111 | } | ||
| 112 | |||
| 113 | void uart_write(const void* buffer, size_t length) { | ||
| 114 | const uint8_t* bytes = buffer; | ||
| 115 | for (size_t i = 0; i < length; ++i) { | ||
| 116 | uart_putc(*bytes++); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | void uart_print(const char* string) { | ||
| 121 | while (*string) { | ||
| 122 | uart_putc(*string++); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
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 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stddef.h> | ||
| 4 | |||
| 5 | void uart_init(int raspi); | ||
| 6 | void uart_putc(unsigned char c); | ||
| 7 | unsigned char uart_getc(); | ||
| 8 | void uart_write(const void* buffer, size_t length); | ||
| 9 | void uart_print(const char* string); | ||
| 10 | |||
