From 96e695704a06bd8f9eacdcb8db52348cfa34e51d Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 7 May 2023 12:22:47 +0100 Subject: [PATCH] x86_64: allow serial port baud rate to be configured --- arch/x86_64/include/arch/serial.h | 2 +- arch/x86_64/init.c | 2 +- arch/x86_64/serial.c | 56 ++++++++++++++++++++++++------- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/arch/x86_64/include/arch/serial.h b/arch/x86_64/include/arch/serial.h index cb7e828..8daaa8b 100644 --- a/arch/x86_64/include/arch/serial.h +++ b/arch/x86_64/include/arch/serial.h @@ -18,7 +18,7 @@ extern char serial_recv_byte(int device); extern int serial_rcvd(int device); -extern void serialcon_init(void); +extern void serialcon_init(int baud); #ifdef __cplusplus } diff --git a/arch/x86_64/init.c b/arch/x86_64/init.c index fa56e88..e9b445c 100644 --- a/arch/x86_64/init.c +++ b/arch/x86_64/init.c @@ -48,7 +48,7 @@ int ml_init(uintptr_t arg) bootstrap_cpu_init(); vgacon_init(); - serialcon_init(); + serialcon_init(115200); clock_calibrate(500); print_kernel_banner(); diff --git a/arch/x86_64/serial.c b/arch/x86_64/serial.c index 840de79..660897d 100644 --- a/arch/x86_64/serial.c +++ b/arch/x86_64/serial.c @@ -49,17 +49,49 @@ void serialcon_write(struct console *con, const char *s, unsigned int len) } } -static void init_serial_port(int port) +static int get_baud_divisor(int baud) { - outportb(port + 1, 0x00); // Disable all interrupts - outportb(port + 3, 0x80); // Enable DLAB (set baud rate divisor) - outportb(port + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud - outportb(port + 1, 0x00); // (hi byte) - outportb(port + 3, 0x03); // 8 bits, no parity, one stop bit - outportb(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold - outportb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set - outportb(port + 4, 0x1E); // Set in loopback mode, test the serial chip - outportb(port + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) + int freq = 115200; + int best_baud = -1; + int best_div = -1; + + for (int i = 1; i < 254; i++) { + int this_baud = freq / i; + + if (this_baud == baud) { + return i; + } + + if (best_baud == -1) { + best_baud = this_baud; + best_div = i; + continue; + } + + if (this_baud < baud && best_baud > baud) { + /* TODO pick divisor that gives the closest baud rate */ + return best_div; + } + + best_baud = this_baud; + best_div = i; + } + + return best_div; +} + +static void init_serial_port(int port, int baud) +{ + int baud_div = get_baud_divisor(baud); + outportb(port + 1, 0x00); // Disable all interrupts + outportb(port + 3, 0x80); // Enable DLAB (set baud rate divisor) + outportb(port + 0, baud_div); // Set divisor + outportb(port + 1, 0x00); + outportb(port + 3, 0x03); // 8 bits, no parity, one stop bit + outportb(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outportb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set + outportb(port + 4, 0x1E); // Set in loopback mode, test the serial chip + outportb(port + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) volatile unsigned int q = 0; while (!serial_received(port)) { @@ -85,8 +117,8 @@ static struct console serialcon = { .c_lock = SPIN_LOCK_INIT, }; -void serialcon_init(void) +void serialcon_init(int baud) { - init_serial_port(COM1); + init_serial_port(COM1, baud); console_register(&serialcon); }