diff options
| author | 3gg <3gg@shellblade.net> | 2025-02-25 19:29:33 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-02-25 19:29:33 -0800 |
| commit | a8a39ba24e36119dce48a01c2139e0e5a25674e1 (patch) | |
| tree | fbe26e3e9b60f83a7e3404729f64504d64876e82 | |
| parent | 2dd1239ae661a1704c94501bbfc46afd4ca94863 (diff) | |
Add mailbox.
| -rw-r--r-- | src/kernel.c | 5 | ||||
| -rw-r--r-- | src/mailbox.c | 73 | ||||
| -rw-r--r-- | src/mailbox.h | 38 | ||||
| -rw-r--r-- | src/mmio.c | 4 | ||||
| -rw-r--r-- | src/mmio.h | 5 | ||||
| -rw-r--r-- | src/uart.c | 19 |
6 files changed, 127 insertions, 17 deletions
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 @@ | |||
| 1 | #include <mailbox.h> | ||
| 1 | #include <mmio.h> | 2 | #include <mmio.h> |
| 2 | #include <raspi.h> | 3 | #include <raspi.h> |
| 3 | #include <uart.h> | 4 | #include <uart.h> |
| @@ -10,7 +11,9 @@ static void halt() { | |||
| 10 | 11 | ||
| 11 | void main() { | 12 | void main() { |
| 12 | const int raspi = raspi_init(); | 13 | const int raspi = raspi_init(); |
| 13 | mmio_init(raspi); | 14 | mmio_init(raspi); // Must be initialized before other peripherals. |
| 15 | |||
| 16 | mbox_init(); | ||
| 14 | uart_init(raspi); | 17 | uart_init(raspi); |
| 15 | 18 | ||
| 16 | uart_print("Hello world!\n"); | 19 | 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 @@ | |||
| 1 | #include <mailbox.h> | ||
| 2 | |||
| 3 | #include <mmio.h> | ||
| 4 | |||
| 5 | #include <stdbool.h> | ||
| 6 | #include <stdint.h> | ||
| 7 | |||
| 8 | enum | ||
| 9 | { | ||
| 10 | MAILBOX = 0xB880 // Mailbox address relative to MMIO base address. | ||
| 11 | }; | ||
| 12 | |||
| 13 | typedef uint32_t Message; | ||
| 14 | |||
| 15 | static inline uint8_t msg_channel(Message msg) { | ||
| 16 | return msg & 0xf; | ||
| 17 | } | ||
| 18 | |||
| 19 | static inline const void* msg_data(Message msg) { | ||
| 20 | // The data field is actually a pointer to the data. | ||
| 21 | return (const void*)((uintptr_t)(msg >> 4)); | ||
| 22 | } | ||
| 23 | |||
| 24 | static inline Message msg_make(uint8_t channel, const void* data) { | ||
| 25 | return ((uintptr_t)(data) << 4) | (channel & 0xf); | ||
| 26 | } | ||
| 27 | |||
| 28 | typedef struct Mailbox { | ||
| 29 | Message inbox; // 0x00 | ||
| 30 | uint32_t unused_1; // 0x04 | ||
| 31 | uint32_t unused_2; // 0x08 | ||
| 32 | uint32_t unused_3; // 0x0C | ||
| 33 | uint32_t unused_4; // 0x10 | ||
| 34 | uint32_t unused_5; // 0x14 | ||
| 35 | uint32_t status; // 0x18 | ||
| 36 | uint32_t unused_6; // 0x1C | ||
| 37 | Message outbox; // 0x20 | ||
| 38 | } Mailbox; | ||
| 39 | |||
| 40 | static inline bool inbox_empty(const volatile Mailbox* pMailbox) { | ||
| 41 | return (pMailbox->status & 0x40000000) != 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | static inline bool outbox_full(const volatile Mailbox* pMailbox) { | ||
| 45 | return (pMailbox->status & 0x80000000) != 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static volatile Mailbox* pMailbox; | ||
| 49 | |||
| 50 | void mbox_init() { | ||
| 51 | pMailbox = mmio_get_peripheral(MAILBOX); | ||
| 52 | } | ||
| 53 | |||
| 54 | const Mail* mbox_read(uint8_t channel) { | ||
| 55 | Message msg; | ||
| 56 | // Read until we get a message from the desired channel. | ||
| 57 | // Discard any other messages. | ||
| 58 | do { | ||
| 59 | // Wait until there is mail to receive. | ||
| 60 | while (inbox_empty(pMailbox)); | ||
| 61 | // Read the mail. | ||
| 62 | msg = pMailbox->inbox; | ||
| 63 | } while (msg_channel(msg) != channel); | ||
| 64 | return (const Mail*)(msg_data(msg)); | ||
| 65 | } | ||
| 66 | |||
| 67 | void mbox_write(uint8_t channel, const void* mail) { | ||
| 68 | // Wait until the outbox is clear. | ||
| 69 | while (outbox_full(pMailbox)); | ||
| 70 | // Send the mail. | ||
| 71 | pMailbox->outbox = msg_make(channel, mail); | ||
| 72 | } | ||
| 73 | |||
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 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | |||
| 5 | // The channel takes the lower 4 bits; the data pointer the upper 28. So a mail | ||
| 6 | // must be aligned to a 16-byte boundary. | ||
| 7 | #define MAIL_ALIGN 16 | ||
| 8 | |||
| 9 | enum | ||
| 10 | { | ||
| 11 | PROPERTY_CHANNEL = 8, | ||
| 12 | }; | ||
| 13 | |||
| 14 | typedef struct Tag { | ||
| 15 | union { | ||
| 16 | uint32_t all; | ||
| 17 | struct { | ||
| 18 | uint16_t command : 12; // Command. | ||
| 19 | uint8_t type : 4; // Command type. | ||
| 20 | uint8_t device : 4; // Hardware device. | ||
| 21 | uint16_t zeroes : 12; // Reserved. | ||
| 22 | } id; | ||
| 23 | }; | ||
| 24 | uint32_t size; // Buffer size. | ||
| 25 | uint32_t code; // Request/response code. | ||
| 26 | uint32_t data[1]; // Buffer data. | ||
| 27 | } Tag; | ||
| 28 | |||
| 29 | typedef struct __attribute__((aligned(MAIL_ALIGN))) Mail { | ||
| 30 | uint32_t size; // Buffer size. | ||
| 31 | uint32_t code; // Request/response code. | ||
| 32 | Tag tags[1]; // Variable quantity. | ||
| 33 | } Mail; | ||
| 34 | |||
| 35 | void mbox_init(); | ||
| 36 | const Mail* mbox_read(uint8_t channel); | ||
| 37 | void mbox_write(uint8_t channel, const void* mail); | ||
| 38 | |||
| @@ -30,3 +30,7 @@ void mmio_write(uint32_t reg, uint32_t val) { | |||
| 30 | *REG_ADDR(reg) = val; | 30 | *REG_ADDR(reg) = val; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | void* mmio_get_peripheral(uint32_t peripheral_base) { | ||
| 34 | return MMIO_BASE + peripheral_base; | ||
| 35 | } | ||
| 36 | |||
| @@ -2,7 +2,8 @@ | |||
| 2 | 2 | ||
| 3 | #include <stdint.h> | 3 | #include <stdint.h> |
| 4 | 4 | ||
| 5 | void mmio_init(int raspi); | 5 | void mmio_init(int raspi); |
| 6 | uint32_t mmio_read(uint32_t reg); | 6 | uint32_t mmio_read(uint32_t reg); |
| 7 | void mmio_write(uint32_t reg, uint32_t val); | 7 | void mmio_write(uint32_t reg, uint32_t val); |
| 8 | void* mmio_get_peripheral(uint32_t peripheral_base); | ||
| 8 | 9 | ||
| @@ -5,6 +5,7 @@ References: | |||
| 5 | #include <uart.h> | 5 | #include <uart.h> |
| 6 | 6 | ||
| 7 | #include <gpio.h> | 7 | #include <gpio.h> |
| 8 | #include <mailbox.h> | ||
| 8 | #include <mmio.h> | 9 | #include <mmio.h> |
| 9 | 10 | ||
| 10 | enum | 11 | enum |
| @@ -31,15 +32,10 @@ enum | |||
| 31 | UART0_ITIP = (UART0_BASE + 0x84), | 32 | UART0_ITIP = (UART0_BASE + 0x84), |
| 32 | UART0_ITOP = (UART0_BASE + 0x88), | 33 | UART0_ITOP = (UART0_BASE + 0x88), |
| 33 | UART0_TDR = (UART0_BASE + 0x8C), | 34 | UART0_TDR = (UART0_BASE + 0x8C), |
| 34 | // The offsets for Mailbox registers. | ||
| 35 | MBOX_BASE = 0xB880, | ||
| 36 | MBOX_READ = (MBOX_BASE + 0x00), | ||
| 37 | MBOX_STATUS = (MBOX_BASE + 0x18), | ||
| 38 | MBOX_WRITE = (MBOX_BASE + 0x20) | ||
| 39 | }; | 35 | }; |
| 40 | 36 | ||
| 41 | // A mailbox message with set clock rate of PL011 to 3MHz tag. | 37 | // A mailbox message with set clock rate of PL011 to 3MHz tag. |
| 42 | static volatile unsigned int __attribute__((aligned(16))) mbox[9] = { | 38 | static const uint32_t __attribute__((aligned(MAIL_ALIGN))) UART_SET_CLK[9] = { |
| 43 | 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 | 39 | 9*4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0 |
| 44 | }; | 40 | }; |
| 45 | 41 | ||
| @@ -77,14 +73,9 @@ void uart_init(int raspi) { | |||
| 77 | // For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. | 73 | // For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. |
| 78 | // Set it to 3Mhz so that we can consistently set the baud rate | 74 | // Set it to 3Mhz so that we can consistently set the baud rate |
| 79 | if (raspi >= 3) { | 75 | if (raspi >= 3) { |
| 80 | // UART_CLOCK = 30000000; | 76 | // Send message over property channel to configure UART clock. |
| 81 | unsigned int r = (unsigned int) (((uint64_t)(&mbox) & ~0xF) | 8); | 77 | mbox_write(PROPERTY_CHANNEL, UART_SET_CLK); |
| 82 | // Wait until we can talk to the VC. | 78 | mbox_read(PROPERTY_CHANNEL); |
| 83 | while (mmio_read(MBOX_STATUS) & 0x80000000); | ||
| 84 | // Send our message to property channel and wait for the response. | ||
| 85 | mmio_write(MBOX_WRITE, r); | ||
| 86 | while ((mmio_read(MBOX_STATUS) & 0x40000000) || | ||
| 87 | (mmio_read(MBOX_READ) != r)); | ||
| 88 | } | 79 | } |
| 89 | 80 | ||
| 90 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. | 81 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. |
