aboutsummaryrefslogtreecommitdiff
path: root/src/arm/PL1/PL1_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm/PL1/PL1_common')
-rw-r--r--src/arm/PL1/PL1_common/global.h38
-rw-r--r--src/arm/PL1/PL1_common/uart.c103
-rw-r--r--src/arm/PL1/PL1_common/uart.h106
3 files changed, 247 insertions, 0 deletions
diff --git a/src/arm/PL1/PL1_common/global.h b/src/arm/PL1/PL1_common/global.h
new file mode 100644
index 0000000..4e17b44
--- /dev/null
+++ b/src/arm/PL1/PL1_common/global.h
@@ -0,0 +1,38 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include <stdint.h>
+
+// board type, raspi2
+#define RASPI 2
+
+// conditionally #define PERIF_BASE
+#if RASPI == 4
+
+#define PERIF_BASE 0xFE000000
+
+#elif RASPI == 3 || RASPI == 2
+
+#define PERIF_BASE 0x3F000000
+
+#else // if RASPI == 1
+
+#define PERIF_BASE 0x20000000
+
+#endif
+
+// GPIO_BASE is #define'd in terms of PERIF_BASE
+// (as in sane kernels - like linux, not like in wiki.osdev codes...)
+#define GPIO_BASE (PERIF_BASE + 0x200000)
+
+inline static uint32_t rd32(uint32_t addr)
+{
+ return *(uint32_t volatile*) addr;
+}
+
+inline static void wr32(uint32_t addr, uint32_t value)
+{
+ *(uint32_t volatile*) addr = value;
+}
+
+#endif // GLOBAL_H
diff --git a/src/arm/PL1/PL1_common/uart.c b/src/arm/PL1/PL1_common/uart.c
new file mode 100644
index 0000000..4dd1c2b
--- /dev/null
+++ b/src/arm/PL1/PL1_common/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;
+}
diff --git a/src/arm/PL1/PL1_common/uart.h b/src/arm/PL1/PL1_common/uart.h
new file mode 100644
index 0000000..96f3634
--- /dev/null
+++ b/src/arm/PL1/PL1_common/uart.h
@@ -0,0 +1,106 @@
+#ifndef UART_H
+#define UART_H
+
+#include <stdint.h>
+#include "global.h"
+#include "interrupts.h"
+
+// The offsets for reach register.
+
+// Controls actuation of pull up/down to ALL GPIO pins.
+#define GPPUD (GPIO_BASE + 0x94)
+
+// Controls actuation of pull up/down for specific GPIO pin.
+#define GPPUDCLK0 (GPIO_BASE + 0x98)
+
+// The base address for UART.
+#define PL011_UART_BASE (GPIO_BASE + 0x1000)
+
+// The offsets for reach register for the UART.
+#define PL011_UART_DR (PL011_UART_BASE + 0x00)
+#define PL011_UART_RSRECR (PL011_UART_BASE + 0x04)
+#define PL011_UART_FR (PL011_UART_BASE + 0x18)
+#define PL011_UART_ILPR (PL011_UART_BASE + 0x20)
+#define PL011_UART_IBRD (PL011_UART_BASE + 0x24)
+#define PL011_UART_FBRD (PL011_UART_BASE + 0x28)
+#define PL011_UART_LCRH (PL011_UART_BASE + 0x2C)
+#define PL011_UART_CR (PL011_UART_BASE + 0x30)
+#define PL011_UART_IFLS (PL011_UART_BASE + 0x34)
+#define PL011_UART_IMSC (PL011_UART_BASE + 0x38)
+#define PL011_UART_RIS (PL011_UART_BASE + 0x3C)
+#define PL011_UART_MIS (PL011_UART_BASE + 0x40)
+#define PL011_UART_ICR (PL011_UART_BASE + 0x44)
+#define PL011_UART_DMACR (PL011_UART_BASE + 0x48)
+#define PL011_UART_ITCR (PL011_UART_BASE + 0x80)
+#define PL011_UART_ITIP (PL011_UART_BASE + 0x84)
+#define PL011_UART_ITOP (PL011_UART_BASE + 0x88)
+#define PL011_UART_TDR (PL011_UART_BASE + 0x8C)
+
+void uart_init();
+void putchar(char c);
+char getchar(void);
+_Bool putchar_non_blocking(char c);
+int getchar_non_blocking(void);
+
+// TODO experiment to see if this gives us raw uart irq or the uart
+// irq bit or'd with it's enable bit (not crucial for now, sice in our
+// code this function only gets called when this irq is enabled)
+static inline _Bool uart_irq_pending(void)
+{
+ return
+ ((uint32_t) 1 << 25) & rd32(ARM_IRQ_PENDING_2);
+}
+
+static inline void uart_irq_disable(void)
+{
+ // Mask uart in arm peripheral interrupts
+ wr32(ARM_DISABLE_IRQS_2, ((uint32_t) 1) << 25);
+}
+
+static inline void uart_irq_enable(void)
+{
+ // Unmask uart in arm peripheral interrupts
+ wr32(ARM_ENABLE_IRQS_2, ((uint32_t) 1) << 25);
+}
+
+static inline _Bool uart_recv_irq_pending(void)
+{
+ return (1 << 4) & rd32(PL011_UART_MIS);
+}
+
+static inline void uart_recv_irq_disable(void)
+{
+ wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) & ~(1 << 4));
+}
+
+static inline void uart_recv_irq_enable(void)
+{
+ wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) | (1 << 4));
+}
+
+static inline void uart_clear_recv_irq(void)
+{
+ wr32(PL011_UART_ICR, (1 << 4));
+}
+
+static inline _Bool uart_send_irq_pending(void)
+{
+ return (1 << 5) & rd32(PL011_UART_MIS);
+}
+
+static inline void uart_send_irq_disable(void)
+{
+ wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) & ~(1 << 5));
+}
+
+static inline void uart_send_irq_enable(void)
+{
+ wr32(PL011_UART_IMSC, rd32(PL011_UART_IMSC) | (1 << 5));
+}
+
+static inline void uart_clear_send_irq(void)
+{
+ wr32(PL011_UART_ICR, (1 << 5));
+}
+
+#endif // UART_H