aboutsummaryrefslogtreecommitdiff
path: root/src/utils/uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/uart.c')
-rw-r--r--src/utils/uart.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/utils/uart.c b/src/utils/uart.c
new file mode 100644
index 0000000..4dd1c2b
--- /dev/null
+++ b/src/utils/uart.c
@@ -0,0 +1,103 @@
+#include <stddef.h>
+#include <stdint.h>
+#include "uart.h"
+#include "global.h"
+
+// Loop <delay> times in a way that the compiler won't optimize away
+static inline void delay(int32_t count)
+{
+ asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n"
+ : "=r"(count): [count]"0"(count) : "cc");
+}
+
+void uart_init()
+{
+ // Disable PL011_UART.
+ wr32(PL011_UART_CR, 0);
+
+ // Setup the GPIO pin 14 && 15.
+
+ // Disable pull up/down for all GPIO pins & delay for 150 cycles.
+ wr32(GPPUD, 0);
+ delay(150);
+
+ // Disable pull up/down for pin 14,15 & delay for 150 cycles.
+ wr32(GPPUDCLK0, (1 << 14) | (1 << 15));
+ delay(150);
+
+ // Write 0 to GPPUDCLK0 to make it take effect.
+ wr32(GPPUDCLK0, 0);
+
+ // Set integer & fractional part of baud rate.
+ // Divider = UART_CLOCK/(16 * Baud)
+ // Fraction part register = (Fractional part * 64) + 0.5
+ // UART_CLOCK = 3000000; Baud = 115200.
+
+ // Divider = 3000000 / (16 * 115200) = 1.627 = ~1.
+ wr32(PL011_UART_IBRD, 1);
+ // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
+ wr32(PL011_UART_FBRD, 40);
+
+ // Set 8 bit data transmission (1 stop bit, no parity)
+ // and disable FIFO to be able to receive interrupt every received
+ // char, not every 2 chars
+ wr32(PL011_UART_LCRH, (1 << 5) | (1 << 6));
+
+ // set interrupt to come when transmit FIFO becomes ≤ 1/8 full
+ // or receive FIFO becomes ≥ 1/8 full
+ // (not really matters, since we disabled FIFOs)
+ wr32(PL011_UART_IFLS, 0);
+
+ // Enable PL011_UART, receive & transfer part of UART.2
+ wr32(PL011_UART_CR, (1 << 0) | (1 << 8) | (1 << 9));
+
+ // At first, it's probably safer to disable interrupts :)
+ uart_irq_disable();
+
+ // The above disables the entire uart irq;
+ // Also disable single sources within it
+ wr32(PL011_UART_IMSC, 0);
+}
+
+inline static _Bool can_transmit(void)
+{
+ return !(rd32(PL011_UART_FR) & (1 << 5));
+}
+
+inline static _Bool can_receive(void)
+{
+ return !(rd32(PL011_UART_FR) & (1 << 4));
+}
+
+void putchar(char c)
+{
+ while (!can_transmit());
+
+ wr32(PL011_UART_DR, c);
+}
+
+char getchar(void)
+{
+ while (!can_receive());
+
+ return rd32(PL011_UART_DR);
+}
+
+_Bool putchar_non_blocking(char c)
+{
+ if (can_transmit())
+ {
+ wr32(PL011_UART_DR, c);
+ return 0;
+ }
+
+ return 1;
+}
+
+int getchar_non_blocking(void)
+{
+ if (can_receive())
+ return rd32(PL011_UART_DR);
+
+ return -1;
+}