|
|
|
@ -1,26 +1,42 @@ |
|
|
|
// See LICENSE for license details.
|
|
|
|
|
|
|
|
#include <string.h> |
|
|
|
#include <stdarg.h> |
|
|
|
#include <assert.h> |
|
|
|
#include "uart16550.h" |
|
|
|
#include "fdt.h" |
|
|
|
|
|
|
|
volatile uint8_t* uart16550; |
|
|
|
// some devices require a shifted register index
|
|
|
|
// (e.g. 32 bit registers instead of 8 bit registers)
|
|
|
|
static uint32_t uart16550_reg_shift; |
|
|
|
static uint32_t uart16550_clock = 1843200; // a "common" base clock
|
|
|
|
|
|
|
|
#define UART_REG_QUEUE 0 |
|
|
|
#define UART_REG_LINESTAT 5 |
|
|
|
#define UART_REG_QUEUE 0 // rx/tx fifo data
|
|
|
|
#define UART_REG_DLL 0 // divisor latch (LSB)
|
|
|
|
#define UART_REG_IER 1 // interrupt enable register
|
|
|
|
#define UART_REG_DLM 1 // divisor latch (MSB)
|
|
|
|
#define UART_REG_FCR 2 // fifo control register
|
|
|
|
#define UART_REG_LCR 3 // line control register
|
|
|
|
#define UART_REG_MCR 4 // modem control register
|
|
|
|
#define UART_REG_LSR 5 // line status register
|
|
|
|
#define UART_REG_MSR 6 // modem status register
|
|
|
|
#define UART_REG_SCR 7 // scratch register
|
|
|
|
#define UART_REG_STATUS_RX 0x01 |
|
|
|
#define UART_REG_STATUS_TX 0x20 |
|
|
|
|
|
|
|
#define UART_DEFAULT_BAUD 38400 |
|
|
|
|
|
|
|
void uart16550_putchar(uint8_t ch) |
|
|
|
{ |
|
|
|
while ((uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_TX) == 0); |
|
|
|
uart16550[UART_REG_QUEUE] = ch; |
|
|
|
while ((uart16550[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_TX) == 0); |
|
|
|
uart16550[UART_REG_QUEUE << uart16550_reg_shift] = ch; |
|
|
|
} |
|
|
|
|
|
|
|
int uart16550_getchar() |
|
|
|
{ |
|
|
|
if (uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_RX) |
|
|
|
return uart16550[UART_REG_QUEUE]; |
|
|
|
if (uart16550[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_RX) |
|
|
|
return uart16550[UART_REG_QUEUE << uart16550_reg_shift]; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
@ -28,6 +44,9 @@ struct uart16550_scan |
|
|
|
{ |
|
|
|
int compat; |
|
|
|
uint64_t reg; |
|
|
|
uint32_t reg_offset; |
|
|
|
uint32_t reg_shift; |
|
|
|
uint32_t clock_freq; |
|
|
|
}; |
|
|
|
|
|
|
|
static void uart16550_open(const struct fdt_scan_node *node, void *extra) |
|
|
|
@ -39,26 +58,41 @@ static void uart16550_open(const struct fdt_scan_node *node, void *extra) |
|
|
|
static void uart16550_prop(const struct fdt_scan_prop *prop, void *extra) |
|
|
|
{ |
|
|
|
struct uart16550_scan *scan = (struct uart16550_scan *)extra; |
|
|
|
if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "ns16550a")) { |
|
|
|
if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, "ns16550a") != -1) { |
|
|
|
scan->compat = 1; |
|
|
|
} else if (!strcmp(prop->name, "reg")) { |
|
|
|
fdt_get_address(prop->node->parent, prop->value, &scan->reg); |
|
|
|
} else if (!strcmp(prop->name, "reg-shift")) { |
|
|
|
scan->reg_shift = fdt_get_value(prop, 0); |
|
|
|
} else if (!strcmp(prop->name, "reg-offset")) { |
|
|
|
scan->reg_offset = fdt_get_value(prop, 0); |
|
|
|
} else if (!strcmp(prop->name, "clock-frequency")) { |
|
|
|
scan->clock_freq = fdt_get_value(prop, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void uart16550_done(const struct fdt_scan_node *node, void *extra) |
|
|
|
{ |
|
|
|
uint32_t clock_freq; |
|
|
|
struct uart16550_scan *scan = (struct uart16550_scan *)extra; |
|
|
|
if (!scan->compat || !scan->reg || uart16550) return; |
|
|
|
|
|
|
|
uart16550 = (void*)(uintptr_t)scan->reg; |
|
|
|
if (scan->clock_freq != 0) |
|
|
|
uart16550_clock = scan->clock_freq; |
|
|
|
// if device tree doesn't supply a clock, fallback to default clock of 1843200
|
|
|
|
|
|
|
|
uint32_t divisor = uart16550_clock / (16 * UART_DEFAULT_BAUD); |
|
|
|
assert (divisor < 0x10000u); |
|
|
|
|
|
|
|
uart16550 = (void*)((uintptr_t)scan->reg + scan->reg_offset); |
|
|
|
uart16550_reg_shift = scan->reg_shift; |
|
|
|
// http://wiki.osdev.org/Serial_Ports
|
|
|
|
uart16550[1] = 0x00; // Disable all interrupts
|
|
|
|
uart16550[3] = 0x80; // Enable DLAB (set baud rate divisor)
|
|
|
|
uart16550[0] = 0x03; // Set divisor to 3 (lo byte) 38400 baud
|
|
|
|
uart16550[1] = 0x00; // (hi byte)
|
|
|
|
uart16550[3] = 0x03; // 8 bits, no parity, one stop bit
|
|
|
|
uart16550[2] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold
|
|
|
|
uart16550[UART_REG_IER << uart16550_reg_shift] = 0x00; // Disable all interrupts
|
|
|
|
uart16550[UART_REG_LCR << uart16550_reg_shift] = 0x80; // Enable DLAB (set baud rate divisor)
|
|
|
|
uart16550[UART_REG_DLL << uart16550_reg_shift] = (uint8_t)divisor; // Set divisor (lo byte)
|
|
|
|
uart16550[UART_REG_DLM << uart16550_reg_shift] = (uint8_t)(divisor >> 8); // (hi byte)
|
|
|
|
uart16550[UART_REG_LCR << uart16550_reg_shift] = 0x03; // 8 bits, no parity, one stop bit
|
|
|
|
uart16550[UART_REG_FCR << uart16550_reg_shift] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold
|
|
|
|
} |
|
|
|
|
|
|
|
void query_uart16550(uintptr_t fdt) |
|
|
|
|