toaruos/kernel/arch/aarch64/pl011.c

91 lines
2.3 KiB
C

/**
* @file kernel/arch/aarch64/pl011.c
* @brief Rudimentary serial driver for the pl011 uart
*
* @copyright
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2022 K. Lange
*/
#include <stdint.h>
#include <kernel/process.h>
#include <kernel/vfs.h>
#include <kernel/printf.h>
#include <kernel/pty.h>
#include <kernel/mmu.h>
#include <kernel/arch/aarch64/dtb.h>
/* TODO interrupt handler installers */
struct irq_callback {
void (*callback)(process_t * this, int irq, void *data);
process_t * owner;
void * data;
};
extern struct irq_callback irq_callbacks[];
static void pl011_irq(process_t * this, int irq, void * data) {
volatile uint32_t * uart_mapped = (volatile uint32_t *)data;
uint32_t mis = uart_mapped[16];
if (mis & (1 << 4)) {
make_process_ready(this);
}
uart_mapped[17] = mis;
}
static void pl011_fill_name(pty_t * pty, char * name) {
snprintf(name, 100, "/dev/ttyS0");
}
static void pl011_write_out(pty_t * pty, uint8_t c) {
volatile uint32_t * uart_mapped = (volatile uint32_t *)pty->_private;
uart_mapped[0] = c;
}
static void pl011_thread(void * arg) {
volatile uint32_t * uart_mapped = (volatile uint32_t *)arg;
pty_t * pty = pty_new(NULL, 0);
pty->write_out = pl011_write_out;
pty->fill_name = pl011_fill_name;
pty->slave->gid = 2; /* dialout group */
pty->slave->mask = 0660;
pty->_private = arg;
vfs_mount("/dev/ttyS0", pty->slave);
/* Set up interrupt callback */
irq_callbacks[1].callback = pl011_irq;
irq_callbacks[1].owner = (process_t*)this_core->current_process;
irq_callbacks[1].data = (void *)uart_mapped;
/* Enable interrupts */
uart_mapped[12] = 0;
uart_mapped[11] = 0x70;
uart_mapped[12] = 0x301;
uart_mapped[14] |= (1 << 4);
asm volatile ("isb" ::: "memory");
/* Handle incoming data */
while (1) {
while ((uart_mapped[6] & (1 << 4))) {
switch_task(0);
}
uint8_t rx = uart_mapped[0];
tty_input_process(pty, rx);
}
}
void pl011_start(void) {
uint32_t * uart = dtb_find_node_prefix("pl011");
if (!uart) return;
/* I know this is going to be 0x09000000, but let's find it anyway */
uint32_t * reg = dtb_node_find_property(uart, "reg");
uintptr_t uart_base = swizzle(reg[3]);
void * uart_mapped = mmu_map_mmio_region(uart_base, 0x1000);
spawn_worker_thread(pl011_thread, "[pl011]", uart_mapped);
}