1// See LICENSE for license details. 2 3#include <string.h> 4#include "uart16550.h" 5#include "fdt.h" 6 7volatile uint8_t* uart16550; 8 9#define UART_REG_QUEUE 0 10#define UART_REG_LINESTAT 5 11#define UART_REG_STATUS_RX 0x01 12#define UART_REG_STATUS_TX 0x20 13 14void uart16550_putchar(uint8_t ch) 15{ 16 while ((uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_TX) == 0); 17 uart16550[UART_REG_QUEUE] = ch; 18} 19 20int uart16550_getchar() 21{ 22 if (uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_RX) 23 return uart16550[UART_REG_QUEUE]; 24 return -1; 25} 26 27struct uart16550_scan 28{ 29 int compat; 30 uint64_t reg; 31}; 32 33static void uart16550_open(const struct fdt_scan_node *node, void *extra) 34{ 35 struct uart16550_scan *scan = (struct uart16550_scan *)extra; 36 memset(scan, 0, sizeof(*scan)); 37} 38 39static void uart16550_prop(const struct fdt_scan_prop *prop, void *extra) 40{ 41 struct uart16550_scan *scan = (struct uart16550_scan *)extra; 42 if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "ns16550a")) { 43 scan->compat = 1; 44 } else if (!strcmp(prop->name, "reg")) { 45 fdt_get_address(prop->node->parent, prop->value, &scan->reg); 46 } 47} 48 49static void uart16550_done(const struct fdt_scan_node *node, void *extra) 50{ 51 struct uart16550_scan *scan = (struct uart16550_scan *)extra; 52 if (!scan->compat || !scan->reg || uart16550) return; 53 54 uart16550 = (void*)(uintptr_t)scan->reg; 55 // http://wiki.osdev.org/Serial_Ports 56 uart16550[1] = 0x00; // Disable all interrupts 57 uart16550[3] = 0x80; // Enable DLAB (set baud rate divisor) 58 uart16550[0] = 0x03; // Set divisor to 3 (lo byte) 38400 baud 59 uart16550[1] = 0x00; // (hi byte) 60 uart16550[3] = 0x03; // 8 bits, no parity, one stop bit 61 uart16550[2] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold 62} 63 64void query_uart16550(uintptr_t fdt) 65{ 66 struct fdt_cb cb; 67 struct uart16550_scan scan; 68 69 memset(&cb, 0, sizeof(cb)); 70 cb.open = uart16550_open; 71 cb.prop = uart16550_prop; 72 cb.done = uart16550_done; 73 cb.extra = &scan; 74 75 fdt_scan(fdt, &cb); 76} 77