From a8a39ba24e36119dce48a01c2139e0e5a25674e1 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Tue, 25 Feb 2025 19:29:33 -0800 Subject: Add mailbox. --- src/kernel.c | 5 +++- src/mailbox.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mailbox.h | 38 +++++++++++++++++++++++++++++++ src/mmio.c | 4 ++++ src/mmio.h | 5 ++-- src/uart.c | 19 ++++------------ 6 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 src/mailbox.c create mode 100644 src/mailbox.h diff --git a/src/kernel.c b/src/kernel.c index a9a2d11..4296aa7 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,7 +11,9 @@ static void halt() { void main() { const int raspi = raspi_init(); - mmio_init(raspi); + mmio_init(raspi); // Must be initialized before other peripherals. + + mbox_init(); uart_init(raspi); uart_print("Hello world!\n"); diff --git a/src/mailbox.c b/src/mailbox.c new file mode 100644 index 0000000..eaf0955 --- /dev/null +++ b/src/mailbox.c @@ -0,0 +1,73 @@ +#include + +#include + +#include +#include + +enum +{ + MAILBOX = 0xB880 // Mailbox address relative to MMIO base address. +}; + +typedef uint32_t Message; + +static inline uint8_t msg_channel(Message msg) { + return msg & 0xf; +} + +static inline const void* msg_data(Message msg) { + // The data field is actually a pointer to the data. + return (const void*)((uintptr_t)(msg >> 4)); +} + +static inline Message msg_make(uint8_t channel, const void* data) { + return ((uintptr_t)(data) << 4) | (channel & 0xf); +} + +typedef struct Mailbox { + Message inbox; // 0x00 + uint32_t unused_1; // 0x04 + uint32_t unused_2; // 0x08 + uint32_t unused_3; // 0x0C + uint32_t unused_4; // 0x10 + uint32_t unused_5; // 0x14 + uint32_t status; // 0x18 + uint32_t unused_6; // 0x1C + Message outbox; // 0x20 +} Mailbox; + +static inline bool inbox_empty(const volatile Mailbox* pMailbox) { + return (pMailbox->status & 0x40000000) != 0; +} + +static inline bool outbox_full(const volatile Mailbox* pMailbox) { + return (pMailbox->status & 0x80000000) != 0; +} + +static volatile Mailbox* pMailbox; + +void mbox_init() { + pMailbox = mmio_get_peripheral(MAILBOX); +} + +const Mail* mbox_read(uint8_t channel) { + Message msg; + // Read until we get a message from the desired channel. + // Discard any other messages. + do { + // Wait until there is mail to receive. + while (inbox_empty(pMailbox)); + // Read the mail. + msg = pMailbox->inbox; + } while (msg_channel(msg) != channel); + return (const Mail*)(msg_data(msg)); +} + +void mbox_write(uint8_t channel, const void* mail) { + // Wait until the outbox is clear. + while (outbox_full(pMailbox)); + // Send the mail. + pMailbox->outbox = msg_make(channel, mail); +} + diff --git a/src/mailbox.h b/src/mailbox.h new file mode 100644 index 0000000..e96a4ed --- /dev/null +++ b/src/mailbox.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +// The channel takes the lower 4 bits; the data pointer the upper 28. So a mail +// must be aligned to a 16-byte boundary. +#define MAIL_ALIGN 16 + +enum +{ + PROPERTY_CHANNEL = 8, +}; + +typedef struct Tag { + union { + uint32_t all; + struct { + uint16_t command : 12; // Command. + uint8_t type : 4; // Command type. + uint8_t device : 4; // Hardware device. + uint16_t zeroes : 12; // Reserved. + } id; + }; + uint32_t size; // Buffer size. + uint32_t code; // Request/response code. + uint32_t data[1]; // Buffer data. +} Tag; + +typedef struct __attribute__((aligned(MAIL_ALIGN))) Mail { + uint32_t size; // Buffer size. + uint32_t code; // Request/response code. + Tag tags[1]; // Variable quantity. +} Mail; + +void mbox_init(); +const Mail* mbox_read(uint8_t channel); +void mbox_write(uint8_t channel, const void* mail); + diff --git a/src/mmio.c b/src/mmio.c index e082d66..b2abe98 100644 --- a/src/mmio.c +++ b/src/mmio.c @@ -30,3 +30,7 @@ void mmio_write(uint32_t reg, uint32_t val) { *REG_ADDR(reg) = val; } +void* mmio_get_peripheral(uint32_t peripheral_base) { + return MMIO_BASE + peripheral_base; +} + diff --git a/src/mmio.h b/src/mmio.h index b566bbe..4d111d6 100644 --- a/src/mmio.h +++ b/src/mmio.h @@ -2,7 +2,8 @@ #include -void mmio_init(int raspi); +void mmio_init(int raspi); uint32_t mmio_read(uint32_t reg); -void mmio_write(uint32_t reg, uint32_t val); +void mmio_write(uint32_t reg, uint32_t val); +void* mmio_get_peripheral(uint32_t peripheral_base); diff --git a/src/uart.c b/src/uart.c index bb2a0ea..3b107f4 100644 --- a/src/uart.c +++ b/src/uart.c @@ -5,6 +5,7 @@ References: #include #include +#include #include enum @@ -31,15 +32,10 @@ enum 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] = { +static const uint32_t __attribute__((aligned(MAIL_ALIGN))) UART_SET_CLK[9] = { 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 }; @@ -77,14 +73,9 @@ void uart_init(int raspi) { // 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)); + // Send message over property channel to configure UART clock. + mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); + mbox_read(PROPERTY_CHANNEL); } // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. -- cgit v1.2.3