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/uart.c | |
| parent | 0e1595330211351823e68691fca013bb47772aeb (diff) | |
Hello world.
Diffstat (limited to 'src/uart.c')
| -rw-r--r-- | src/uart.c | 125 |
1 files changed, 125 insertions, 0 deletions
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 | |||
