96 lines
2.4 KiB
C
96 lines
2.4 KiB
C
#include <assert.h>
|
|
#include <clock.h>
|
|
#include <mem.h>
|
|
#include <serial.h>
|
|
#include <spinlock.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <timer.h>
|
|
|
|
#define DEFAULT_BAUDRATE 115200
|
|
#define SERIAL_BUF_SIZE 0x1000
|
|
#define SERIAL_BUF_MASK (SERIAL_BUF_SIZE - 1)
|
|
|
|
/* NOTE(dinosaure): [serial.c] is usable only **after** the initialization of
|
|
* the .bss section:
|
|
* memset (&__bss_start, 0, (uintptr_t) _end - (uintptr_t) __bss_start);
|
|
*
|
|
* TODO(dinosaure): we should probably initialize .bss in [startup.S]
|
|
*/
|
|
static bool initialized = false;
|
|
static uint8_t tx_buf[SERIAL_BUF_SIZE] = {0};
|
|
static uint8_t tx_rd = 0;
|
|
static uint8_t tx_wr = 0;
|
|
// static uint32_t spinlock = 0;
|
|
|
|
static void flush_tx_buf(void) {
|
|
assert(initialized);
|
|
|
|
while (tx_rd != tx_wr) {
|
|
if (!(read32(ARM_UART0_FR) & 0x20)) {
|
|
write32(ARM_UART0_DR, tx_buf[tx_rd++]);
|
|
tx_rd &= SERIAL_BUF_MASK;
|
|
}
|
|
}
|
|
}
|
|
|
|
void gilbraltar_serial_init(void) {
|
|
if (initialized)
|
|
return;
|
|
|
|
uint32_t baudrate = DEFAULT_BAUDRATE;
|
|
uint32_t clock_rate = gilbraltar_get_rate_of_clock(2); // UART clock
|
|
uint32_t baud16 = baudrate * 16;
|
|
uint32_t int_div = clock_rate / baud16;
|
|
uint32_t fract_div_2 = (clock_rate % baud16) * 8 / baudrate;
|
|
uint32_t fract_div = fract_div_2 / 2 + fract_div_2 % 2;
|
|
|
|
write32(ARM_UART0_IMSC, 0);
|
|
write32(ARM_UART0_ICR, 0x7ff);
|
|
write32(ARM_UART0_IBRD, int_div);
|
|
write32(ARM_UART0_FBRD, fract_div);
|
|
write32(ARM_UART0_LCRH, (1 << 4) | (3 << 5));
|
|
write32(ARM_UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
|
|
|
|
initialized = true;
|
|
flush_tx_buf();
|
|
}
|
|
|
|
void gilbraltar_serial_send(uint8_t chr) {
|
|
if (initialized) {
|
|
while (1)
|
|
if (!(read32(ARM_UART0_FR) & 0x20))
|
|
break;
|
|
|
|
write32(ARM_UART0_DR, chr);
|
|
} else {
|
|
// gilbraltar_spinlock_acquire(IRQ, &spinlock);
|
|
|
|
if (((tx_wr + 1) & SERIAL_BUF_MASK) != tx_rd) {
|
|
tx_buf[tx_wr++] = chr;
|
|
tx_wr &= SERIAL_BUF_MASK;
|
|
}
|
|
|
|
// gilbraltar_spinlock_release(IRQ, &spinlock);
|
|
}
|
|
}
|
|
|
|
uint8_t gilbraltar_serial_recv(void) {
|
|
while (1)
|
|
if (!(read32(ARM_UART0_FR) & 0x10))
|
|
break;
|
|
|
|
return (read32(ARM_UART0_DR) & 0xff);
|
|
}
|
|
|
|
void gilbraltar_serial_puts(const char *str) {
|
|
while (*str)
|
|
gilbraltar_serial_send(*str++);
|
|
}
|
|
|
|
void gilbraltar_serial_putchar(int chr) { gilbraltar_serial_send(chr & 0xff); }
|
|
|
|
void gilbraltar_serial_write(const char *str, size_t len) {
|
|
while (len--)
|
|
gilbraltar_serial_send(*str++);
|
|
}
|