aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPalmer Dabbelt <palmer@dabbelt.com>2017-12-13 12:32:38 -0800
committerGitHub <noreply@github.com>2017-12-13 12:32:38 -0800
commit8e4683478d16c42ac2a698112c80c09e8bb293d2 (patch)
treee8bb8c83b10f637b83566058fb65d4ee80dafa40
parent3d921d3c76db3af7b9ae0b5df0f0790f26222246 (diff)
parent132c6292bb3fc18e52a1e3a3a5cdd3b8ca838b28 (diff)
downloadriscv-pk-8e4683478d16c42ac2a698112c80c09e8bb293d2.zip
riscv-pk-8e4683478d16c42ac2a698112c80c09e8bb293d2.tar.gz
riscv-pk-8e4683478d16c42ac2a698112c80c09e8bb293d2.tar.bz2
Merge pull request #72 from riscv/16550
Add a 16550 UART driver to back the SBI console
-rw-r--r--machine/machine.mk.in2
-rw-r--r--machine/minit.c2
-rw-r--r--machine/mtrap.c5
-rw-r--r--machine/uart16550.c74
-rw-r--r--machine/uart16550.h12
5 files changed, 95 insertions, 0 deletions
diff --git a/machine/machine.mk.in b/machine/machine.mk.in
index dc8492f..3d19bf0 100644
--- a/machine/machine.mk.in
+++ b/machine/machine.mk.in
@@ -12,6 +12,7 @@ machine_hdrs = \
mcall.h \
mtrap.h \
uart.h \
+ uart16550.h \
finisher.h \
unprivileged_memory.h \
vm.h \
@@ -26,6 +27,7 @@ machine_c_srcs = \
fp_emulation.c \
fp_ldst.c \
uart.c \
+ uart16550.c \
finisher.c \
misaligned_ldst.c \
diff --git a/machine/minit.c b/machine/minit.c
index 3623f38..ee83f98 100644
--- a/machine/minit.c
+++ b/machine/minit.c
@@ -4,6 +4,7 @@
#include "fp_emulation.h"
#include "fdt.h"
#include "uart.h"
+#include "uart16550.h"
#include "finisher.h"
#include "disabled_hart_mask.h"
#include "htif.h"
@@ -141,6 +142,7 @@ void init_first_hart(uintptr_t hartid, uintptr_t dtb)
{
// Confirm console as early as possible
query_uart(dtb);
+ query_uart16550(dtb);
query_htif(dtb);
hart_init();
diff --git a/machine/mtrap.c b/machine/mtrap.c
index 0f77def..6c0e99c 100644
--- a/machine/mtrap.c
+++ b/machine/mtrap.c
@@ -5,6 +5,7 @@
#include "bits.h"
#include "vm.h"
#include "uart.h"
+#include "uart16550.h"
#include "finisher.h"
#include "fdt.h"
#include "unprivileged_memory.h"
@@ -22,6 +23,8 @@ static uintptr_t mcall_console_putchar(uint8_t ch)
{
if (uart) {
uart_putchar(ch);
+ } else if (uart16550) {
+ uart16550_putchar(ch);
} else if (htif) {
htif_console_putchar(ch);
}
@@ -73,6 +76,8 @@ static uintptr_t mcall_console_getchar()
{
if (uart) {
return uart_getchar();
+ } else if (uart16550) {
+ return uart16550_getchar();
} else if (htif) {
return htif_console_getchar();
} else {
diff --git a/machine/uart16550.c b/machine/uart16550.c
new file mode 100644
index 0000000..fe1ba99
--- /dev/null
+++ b/machine/uart16550.c
@@ -0,0 +1,74 @@
+#include <string.h>
+#include "uart16550.h"
+#include "fdt.h"
+
+volatile uint8_t* uart16550;
+
+#define UART_REG_QUEUE 0
+#define UART_REG_LINESTAT 5
+#define UART_REG_STATUS_RX 0x01
+#define UART_REG_STATUS_TX 0x20
+
+void uart16550_putchar(uint8_t ch)
+{
+ while ((uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_TX) == 0);
+ uart16550[UART_REG_QUEUE] = ch;
+}
+
+int uart16550_getchar()
+{
+ if (uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_RX)
+ return uart16550[UART_REG_QUEUE];
+ return -1;
+}
+
+struct uart16550_scan
+{
+ int compat;
+ uint64_t reg;
+};
+
+static void uart16550_open(const struct fdt_scan_node *node, void *extra)
+{
+ struct uart16550_scan *scan = (struct uart16550_scan *)extra;
+ memset(scan, 0, sizeof(*scan));
+}
+
+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")) {
+ scan->compat = 1;
+ } else if (!strcmp(prop->name, "reg")) {
+ fdt_get_address(prop->node->parent, prop->value, &scan->reg);
+ }
+}
+
+static void uart16550_done(const struct fdt_scan_node *node, void *extra)
+{
+ struct uart16550_scan *scan = (struct uart16550_scan *)extra;
+ if (!scan->compat || !scan->reg || uart16550) return;
+
+ uart16550 = (void*)(uintptr_t)scan->reg;
+ // 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
+}
+
+void query_uart16550(uintptr_t fdt)
+{
+ struct fdt_cb cb;
+ struct uart16550_scan scan;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.open = uart16550_open;
+ cb.prop = uart16550_prop;
+ cb.done = uart16550_done;
+ cb.extra = &scan;
+
+ fdt_scan(fdt, &cb);
+}
diff --git a/machine/uart16550.h b/machine/uart16550.h
new file mode 100644
index 0000000..d7a0805
--- /dev/null
+++ b/machine/uart16550.h
@@ -0,0 +1,12 @@
+#ifndef _RISCV_16550_H
+#define _RISCV_16550_H
+
+#include <stdint.h>
+
+extern volatile uint8_t* uart16550;
+
+void uart16550_putchar(uint8_t ch);
+int uart16550_getchar();
+void query_uart16550(uintptr_t dtb);
+
+#endif