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 | |||