smol-gilbraltar/kernel/serial.c
2024-12-23 23:44:47 +01:00

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++);
}