diff options
-rw-r--r-- | src/arm/PL1/PL1_common/uart.c | 57 | ||||
-rw-r--r-- | src/arm/PL1/PL1_common/uart.h | 18 | ||||
-rw-r--r-- | src/arm/PL1/kernel/setup.c | 2 |
3 files changed, 43 insertions, 34 deletions
diff --git a/src/arm/PL1/PL1_common/uart.c b/src/arm/PL1/PL1_common/uart.c index 4dd1c2b..94aae46 100644 --- a/src/arm/PL1/PL1_common/uart.c +++ b/src/arm/PL1/PL1_common/uart.c @@ -3,53 +3,48 @@ #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) +void uart_init(uint32_t baud, uint32_t config) { - asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" - : "=r"(count): [count]"0"(count) : "cc"); -} - -void uart_init() -{ - // Disable PL011_UART. + // PL011 UART must be disabled before configuring wr32(PL011_UART_CR, 0); - - // Setup the GPIO pin 14 && 15. - // Disable pull up/down for all GPIO pins & delay for 150 cycles. + // GPIO pins used for UART should have pull up/down disabled + // Procedure as described in BCM2835 ARM Peripherals wr32(GPPUD, 0); - delay(150); - // Disable pull up/down for pin 14,15 & delay for 150 cycles. + for (int i = 0; i < 150; i++) // delay for at least 150 cycles + asm volatile("nop"); + wr32(GPPUDCLK0, (1 << 14) | (1 << 15)); - delay(150); - // Write 0 to GPPUDCLK0 to make it take effect. - wr32(GPPUDCLK0, 0); + for (int i = 0; i < 150; i++) + asm volatile("nop"); - // 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. + wr32(GPPUDCLK0, 0); + + wr32(GPPUD, 0); - // 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); + // Setting clock rate + // As described in UART (PL011) Technical Reference Manual + uint32_t int_part = DEFAULT_UART_CLOCK_RATE / (16 * baud); + uint32_t rest = DEFAULT_UART_CLOCK_RATE % (16 * baud); + uint32_t fract_part = (rest * 64 * 2 + 1) / (2 * 16 * baud); + + wr32(PL011_UART_IBRD, int_part); + wr32(PL011_UART_FBRD, fract_part); - // Set 8 bit data transmission (1 stop bit, no parity) - // and disable FIFO to be able to receive interrupt every received + // Set data transmission specified by caller + // Don't enable FIFO to be able to receive interrupt every received // char, not every 2 chars - wr32(PL011_UART_LCRH, (1 << 5) | (1 << 6)); + wr32(PL011_UART_LCRH, config); - // set interrupt to come when transmit FIFO becomes ≤ 1/8 full + // 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)); + // Enable UART receiving and transmitting, as well as UART itself + wr32(PL011_UART_CR, (1 << 9) | (1 << 8) | 1); // At first, it's probably safer to disable interrupts :) uart_irq_disable(); diff --git a/src/arm/PL1/PL1_common/uart.h b/src/arm/PL1/PL1_common/uart.h index 96f3634..e02b3c8 100644 --- a/src/arm/PL1/PL1_common/uart.h +++ b/src/arm/PL1/PL1_common/uart.h @@ -5,12 +5,26 @@ #include "global.h" #include "interrupts.h" +#define DEFAULT_UART_CLOCK_RATE 3000000 + +#define UART_WLEN_8_BITS (0b11 << 5) +#define UART_WLEN_7_BITS (0b10 << 5) +#define UART_WLEN_6_BITS (0b01 << 5) +#define UART_WLEN_5_BITS 0b00 + +#define UART_2_STOP (1 << 3) +#define UART_1_STOP 0 + +#define UART_ODD_PAR (1 << 1) +#define UART_EVEN_PAR ((1 << 1) | (1 << 2)) +#define UART_NO_PAR 0 + // The offsets for reach register. -// Controls actuation of pull up/down to ALL GPIO pins. +// GPIO Pin Pull-up/down Enable #define GPPUD (GPIO_BASE + 0x94) -// Controls actuation of pull up/down for specific GPIO pin. +// GPIO Pin Pull-up/down Enable Clock 0 #define GPPUDCLK0 (GPIO_BASE + 0x98) // The base address for UART. diff --git a/src/arm/PL1/kernel/setup.c b/src/arm/PL1/kernel/setup.c index bf7c9a1..865a719 100644 --- a/src/arm/PL1/kernel/setup.c +++ b/src/arm/PL1/kernel/setup.c @@ -11,7 +11,7 @@ void setup(uint32_t r0, uint32_t machine_type, struct atag_header *atags) { - uart_init(); + uart_init(115200, UART_1_STOP | UART_NO_PAR | UART_WLEN_8_BITS); // When we attach screen session after loading kernel with socat // we miss kernel's greeting... So we'll make the kernel wait for |