summaryrefslogtreecommitdiff
path: root/riscv/riscv-probe/libfemto/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'riscv/riscv-probe/libfemto/drivers')
-rw-r--r--riscv/riscv-probe/libfemto/drivers/htif.c91
-rw-r--r--riscv/riscv-probe/libfemto/drivers/ns16550a.c65
-rw-r--r--riscv/riscv-probe/libfemto/drivers/semihost.c60
-rw-r--r--riscv/riscv-probe/libfemto/drivers/sifive_test.c28
-rw-r--r--riscv/riscv-probe/libfemto/drivers/sifive_uart.c58
5 files changed, 302 insertions, 0 deletions
diff --git a/riscv/riscv-probe/libfemto/drivers/htif.c b/riscv/riscv-probe/libfemto/drivers/htif.c
new file mode 100644
index 0000000..ef6995e
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/htif.c
@@ -0,0 +1,91 @@
+// See LICENSE for license details.
+
+#ifdef __riscv
+
+#include "femto.h"
+#include "spinlock.h"
+
+struct { uint32_t arr[2]; } volatile tohost __attribute__((section(".htif")));
+struct { uint32_t arr[2]; } volatile fromhost __attribute__((section(".htif")));
+
+static spinlock_t htif_lock = SPINLOCK_INIT;
+
+static inline void htif_send(uint8_t dev, uint8_t cmd, int64_t data)
+{
+ /* endian neutral encoding with ordered 32-bit writes */
+ union { uint32_t arr[2]; uint64_t val; } encode = {
+ .val = (uint64_t)dev << 56 | (uint64_t)cmd << 48 | data
+ };
+ tohost.arr[0] = encode.arr[0];
+ tohost.arr[1] = encode.arr[1];
+}
+
+static inline void htif_recv(uint8_t *dev, uint8_t *cmd, int64_t *data)
+{
+ /* endian neutral decoding with ordered 32-bit reads */
+ union { uint32_t arr[2]; uint64_t val; } decode;
+ decode.arr[0] = fromhost.arr[0];
+ decode.arr[1] = fromhost.arr[1];
+ *dev = decode.val >> 56;
+ *cmd = (decode.val >> 48) & 0xff;
+ *data = decode.val << 16 >> 16;
+}
+
+static int64_t htif_get_fromhost(uint8_t dev, uint8_t cmd)
+{
+ /* receive data for specified device and command */
+ uint8_t rdev, rcmd;
+ int64_t data;
+ htif_recv(&rdev, &rcmd, &data);
+ return rdev == dev && rcmd == cmd ? data : -1;
+}
+
+static void htif_set_tohost(uint8_t dev, uint8_t cmd, int64_t data)
+{
+ /* send data with specified device and command */
+ while (tohost.arr[0]) {
+ asm volatile ("" : : "r" (fromhost.arr[0]));
+ asm volatile ("" : : "r" (fromhost.arr[1]));
+ }
+ htif_send(dev, cmd, data);
+}
+
+static int htif_getchar()
+{
+ int ch;
+ spinlock_lock(&htif_lock);
+ if ((ch = htif_get_fromhost(1, 0) & 0xff)) {
+ htif_set_tohost(1, 0, 0);
+ }
+ spinlock_unlock(&htif_lock);
+ return ch;
+}
+
+static int htif_putchar(int ch)
+{
+ spinlock_lock(&htif_lock);
+ htif_set_tohost(1, 1, ch & 0xff);
+ spinlock_unlock(&htif_lock);
+ return ch & 0xff;
+}
+
+static void htif_poweroff(int status)
+{
+ for (;;) {
+ htif_set_tohost(0, 0, 1);
+ }
+}
+
+console_device_t console_htif = {
+ NULL,
+ htif_getchar,
+ htif_putchar
+};
+
+
+poweroff_device_t poweroff_htif = {
+ NULL,
+ htif_poweroff
+};
+
+#endif
diff --git a/riscv/riscv-probe/libfemto/drivers/ns16550a.c b/riscv/riscv-probe/libfemto/drivers/ns16550a.c
new file mode 100644
index 0000000..cff5605
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/ns16550a.c
@@ -0,0 +1,65 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+enum {
+ UART_RBR = 0x00, /* Receive Buffer Register */
+ UART_THR = 0x00, /* Transmit Hold Register */
+ UART_IER = 0x01, /* Interrupt Enable Register */
+ UART_DLL = 0x00, /* Divisor LSB (LCR_DLAB) */
+ UART_DLM = 0x01, /* Divisor MSB (LCR_DLAB) */
+ UART_FCR = 0x02, /* FIFO Control Register */
+ UART_LCR = 0x03, /* Line Control Register */
+ UART_MCR = 0x04, /* Modem Control Register */
+ UART_LSR = 0x05, /* Line Status Register */
+ UART_MSR = 0x06, /* Modem Status Register */
+ UART_SCR = 0x07, /* Scratch Register */
+
+ UART_LCR_DLAB = 0x80, /* Divisor Latch Bit */
+ UART_LCR_8BIT = 0x03, /* 8-bit */
+ UART_LCR_PODD = 0x08, /* Parity Odd */
+
+ UART_LSR_DA = 0x01, /* Data Available */
+ UART_LSR_OE = 0x02, /* Overrun Error */
+ UART_LSR_PE = 0x04, /* Parity Error */
+ UART_LSR_FE = 0x08, /* Framing Error */
+ UART_LSR_BI = 0x10, /* Break indicator */
+ UART_LSR_RE = 0x20, /* THR is empty */
+ UART_LSR_RI = 0x40, /* THR is empty and line is idle */
+ UART_LSR_EF = 0x80, /* Erroneous data in FIFO */
+};
+
+static volatile uint8_t *uart;
+
+static void ns16550a_init()
+{
+ uart = (uint8_t *)(void *)getauxval(NS16550A_UART0_CTRL_ADDR);
+ uint32_t uart_freq = getauxval(UART0_CLOCK_FREQ);
+ uint32_t baud_rate = getauxval(UART0_BAUD_RATE);
+ uint32_t divisor = uart_freq / (16 * baud_rate);
+ uart[UART_LCR] = UART_LCR_DLAB;
+ uart[UART_DLL] = divisor & 0xff;
+ uart[UART_DLM] = (divisor >> 8) & 0xff;
+ uart[UART_LCR] = UART_LCR_PODD | UART_LCR_8BIT;
+}
+
+static int ns16550a_getchar()
+{
+ if (uart[UART_LSR] & UART_LSR_DA) {
+ return uart[UART_RBR];
+ } else {
+ return -1;
+ }
+}
+
+static int ns16550a_putchar(int ch)
+{
+ while ((uart[UART_LSR] & UART_LSR_RI) == 0);
+ return uart[UART_THR] = ch & 0xff;
+}
+
+console_device_t console_ns16550a = {
+ ns16550a_init,
+ ns16550a_getchar,
+ ns16550a_putchar
+};
diff --git a/riscv/riscv-probe/libfemto/drivers/semihost.c b/riscv/riscv-probe/libfemto/drivers/semihost.c
new file mode 100644
index 0000000..9f47834
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/semihost.c
@@ -0,0 +1,60 @@
+// See LICENSE for license details.
+
+#ifdef __riscv
+
+#include "femto.h"
+
+enum {
+ SBI_CONSOLE_PUTCHAR = 1,
+ SBI_CONSOLE_GETCHAR = 2,
+ SBI_SHUTDOWN = 8
+};
+
+#define __syscall(...) \
+ asm volatile ("ecall\n\t" : "+r"(a0) : __VA_ARGS__ : "memory")
+
+static inline long semihost_call0(long n)
+{
+ register long a7 __asm__("a7") = n;
+ register long a0 __asm__("a0");
+ __syscall("r"(a7));
+ return a0;
+}
+
+static inline long semihost_call1(long n, long a)
+{
+ register long a7 __asm__("a7") = n;
+ register long a0 __asm__("a0") = a;
+ __syscall("r"(a7), "0"(a0));
+ return a0;
+}
+
+static int semihost_getchar()
+{
+ semihost_call0(SBI_CONSOLE_GETCHAR);
+}
+
+static int semihost_putchar(int ch)
+{
+ semihost_call1(SBI_CONSOLE_PUTCHAR, ch);
+}
+
+static void semihost_poweroff(int status)
+{
+ semihost_call0(SBI_SHUTDOWN);
+ __builtin_unreachable();
+}
+
+console_device_t console_semihost = {
+ NULL,
+ semihost_getchar,
+ semihost_putchar
+};
+
+
+poweroff_device_t poweroff_semihost = {
+ NULL,
+ semihost_poweroff
+};
+
+#endif \ No newline at end of file
diff --git a/riscv/riscv-probe/libfemto/drivers/sifive_test.c b/riscv/riscv-probe/libfemto/drivers/sifive_test.c
new file mode 100644
index 0000000..1bda9e9
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/sifive_test.c
@@ -0,0 +1,28 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+enum {
+ SIFIVE_TEST_FAIL = 0x3333,
+ SIFIVE_TEST_PASS = 0x5555,
+};
+
+static volatile uint32_t *test;
+
+static void sifive_test_init()
+{
+ test = (uint32_t *)(void *)getauxval(SIFIVE_TEST_CTRL_ADDR);
+}
+
+static void sifive_test_poweroff(int status)
+{
+ *test = SIFIVE_TEST_PASS;
+ while (1) {
+ asm volatile("");
+ }
+}
+
+poweroff_device_t poweroff_sifive_test = {
+ sifive_test_init,
+ sifive_test_poweroff
+};
diff --git a/riscv/riscv-probe/libfemto/drivers/sifive_uart.c b/riscv/riscv-probe/libfemto/drivers/sifive_uart.c
new file mode 100644
index 0000000..9be877e
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/sifive_uart.c
@@ -0,0 +1,58 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+enum {
+ /* UART Registers */
+ UART_REG_TXFIFO = 0,
+ UART_REG_RXFIFO = 1,
+ UART_REG_TXCTRL = 2,
+ UART_REG_RXCTRL = 3,
+ UART_REG_IE = 4,
+ UART_REG_IP = 5,
+ UART_REG_DIV = 6,
+
+ /* TXCTRL register */
+ UART_TXEN = 1,
+ UART_TXSTOP = 2,
+
+ /* RXCTRL register */
+ UART_RXEN = 1,
+
+ /* IP register */
+ UART_IP_TXWM = 1,
+ UART_IP_RXWM = 2
+};
+
+static volatile int *uart;
+
+static void sifive_uart_init()
+{
+ uart = (int *)(void *)getauxval(SIFIVE_UART0_CTRL_ADDR);
+ uint32_t uart_freq = getauxval(UART0_CLOCK_FREQ);
+ uint32_t baud_rate = getauxval(UART0_BAUD_RATE);
+ uint32_t divisor = uart_freq / baud_rate - 1;
+ uart[UART_REG_DIV] = divisor;
+ uart[UART_REG_TXCTRL] = UART_TXEN;
+ uart[UART_REG_RXCTRL] = UART_RXEN;
+ uart[UART_REG_IE] = 0;
+}
+
+static int sifive_uart_getchar()
+{
+ int ch = uart[UART_REG_RXFIFO];
+ if (ch < 0) return -1;
+ return ch;
+}
+
+static int sifive_uart_putchar(int ch)
+{
+ while (uart[UART_REG_TXFIFO] < 0);
+ return uart[UART_REG_TXFIFO] = ch & 0xff;
+}
+
+console_device_t console_sifive_uart = {
+ sifive_uart_init,
+ sifive_uart_getchar,
+ sifive_uart_putchar
+};