The irq bits need to be cleared after reading the fifo register, so the fifo
register needs to be read from interrupt context. Therefore, I introduced
the variables _input_ready and _uart_fifo to store the RX character during the
interrupt handler to be retrieved by the service thread later.

The driver will be initialized from device tree in a follow-up patch.

I modeled the structure of the driver roughly after the OSv pl011 driver.

Signed-off-by: Stewart Hildebrand <[email protected]>
---
 Makefile                |   1 +
 drivers/cadence-uart.cc | 145 ++++++++++++++++++++++++++++++++++++++++
 drivers/cadence-uart.hh |  45 +++++++++++++
 3 files changed, 191 insertions(+)
 create mode 100644 drivers/cadence-uart.cc
 create mode 100644 drivers/cadence-uart.hh

diff --git a/Makefile b/Makefile
index bbc57252..b2332611 100644
--- a/Makefile
+++ b/Makefile
@@ -833,6 +833,7 @@ endif # x64
 ifeq ($(arch),aarch64)
 drivers += drivers/mmio-isa-serial.o
 drivers += drivers/pl011.o
+drivers += drivers/cadence-uart.o
 drivers += drivers/xenconsole.o
 drivers += drivers/virtio.o
 drivers += drivers/virtio-pci-device.o
diff --git a/drivers/cadence-uart.cc b/drivers/cadence-uart.cc
new file mode 100644
index 00000000..aa407008
--- /dev/null
+++ b/drivers/cadence-uart.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 DornerWorks, Ltd
+ * Author: Stewart Hildebrand
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include "cadence-uart.hh"
+#include <stdint.h>
+
+namespace console {
+
+// Register reference:
+// https://www.xilinx.com/html_docs/registers/ug1087/mod___uart.html
+
+#define BIT(x) (1U << (x))
+
+#define UART_CR_RXRES       BIT(0)  // RX reset
+#define UART_CR_TXRES       BIT(1)  // TX reset
+#define UART_CR_RXEN        BIT(2)  // RX enable
+#define UART_CR_TXEN        BIT(4)  // TX enable
+
+#define UART_MR_PARITY_NONE BIT(5)  // No parity
+
+#define UART_SR_RTRIG       BIT(0)  // RX trigger
+#define UART_SR_REMPTY      BIT(1)  // RX empty
+#define UART_SR_TEMPTY      BIT(3)  // TX empty
+#define UART_SR_TFUL        BIT(4)  // TX full
+#define UART_SR_TACTIVE     BIT(11) // TX active
+
+typedef struct __attribute__ ((aligned (4))) {
+    volatile uint32_t cr;    // 0x00 Control register
+    volatile uint32_t mr;    // 0x04 Mode register
+    volatile uint32_t ier;   // 0x08 Interrupt enable register
+    volatile uint32_t idr;   // 0x0C Interrupt disable register
+    volatile uint32_t imr;   // 0x10 Interrupt mask register
+    volatile uint32_t cisr;  // 0x14 Channel interrupt status register
+    uint32_t pad1[2];        // 0x18 - 0x1C
+    volatile uint32_t rtrig; // 0x20 RX trigger threshold
+    uint32_t pad2[2];        // 0x24 - 0x28
+    volatile uint32_t sr;    // 0x2C Status register
+    volatile uint32_t fifo;  // 0x30 RX/TX FIFO register
+} cadence_t;
+
+static_assert(sizeof(cadence_t) == 0x34, "Wrong size for cadence_t");
+
+// Default base addr
+cadence_t *uart = (cadence_t *)0xff000000;
+
+bool Cadence_Console::active = false;
+
+void Cadence_Console::set_base_addr(u64 addr)
+{
+    // Intentional bitwise AND inside condition
+    if (addr & 0x3U) {
+        abort("UART base address is not 32-bit aligned");
+    }
+    uart = (cadence_t *)addr;
+}
+
+void Cadence_Console::set_irqid(int irqid) {
+    this->irqid = irqid;
+}
+
+u64 Cadence_Console::get_base_addr() {
+    return (u64)uart;
+}
+
+void Cadence_Console::flush()
+{
+    uint32_t sr;
+    do {
+        sr = uart->sr;
+        asm volatile("nop");
+        // Intentional bitwise AND inside condition
+    } while (!(sr & UART_SR_TEMPTY) || (sr & UART_SR_TACTIVE));
+}
+
+bool Cadence_Console::input_ready() {
+    return _input_ready;
+}
+
+char Cadence_Console::readch()
+{
+    _input_ready = false;
+    return _uart_fifo;
+}
+
+bool Cadence_Console::ack_irq()
+{
+    // Intentional bitwise AND inside condition
+    if (uart->cisr & uart->imr) {
+        return true;
+    }
+    return false;
+}
+
+void Cadence_Console::irq_handler()
+{
+    uint32_t cisr = uart->cisr & uart->imr;
+
+    // Intentional bitwise AND inside condition
+    if ((cisr & UART_SR_RTRIG) && !(uart->sr & UART_SR_REMPTY)) {
+        _uart_fifo = uart->fifo;
+        _input_ready = true;
+    }
+
+    // IRQ must be cleared after character is read from FIFO
+    uart->cisr = cisr;
+
+    _thread->wake();
+}
+
+void Cadence_Console::dev_start() {
+    flush();
+    // Reset and enable the RX and TX paths
+    uart->cr = UART_CR_RXRES | UART_CR_TXRES | UART_CR_RXEN | UART_CR_TXEN;
+
+    uart->mr = UART_MR_PARITY_NONE;
+
+    _irq.reset(new spi_interrupt(gic::irq_type::IRQ_TYPE_LEVEL, this->irqid,
+                                 [this] { return this->ack_irq(); },
+                                 [this] { this->irq_handler(); }));
+
+    uart->rtrig = 1U;
+    uart->cisr = ~0U;
+    uart->idr = ~0U;
+
+    uart->ier = UART_SR_RTRIG;
+}
+
+void Cadence_Console::write(const char *str, size_t len) {
+    while (len > 0) {
+        // Intentional bitwise AND inside condition
+        while (uart->sr & UART_SR_TFUL) {
+            // spin
+            asm volatile("nop");
+        }
+        uart->fifo = *str++;
+        len--;
+    }
+}
+
+}
diff --git a/drivers/cadence-uart.hh b/drivers/cadence-uart.hh
new file mode 100644
index 00000000..19f27104
--- /dev/null
+++ b/drivers/cadence-uart.hh
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 DornerWorks, Ltd
+ * Author: Stewart Hildebrand
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef CADENCE_UART_HH
+#define CADENCE_UART_HH
+
+#include "console-driver.hh"
+#include "exceptions.hh"
+#include <osv/interrupt.hh>
+#include <osv/types.h>
+
+namespace console {
+
+class Cadence_Console : public console_driver {
+public:
+    virtual void write(const char *str, size_t len);
+    virtual void flush();
+    virtual bool input_ready();
+    virtual char readch();
+
+    void set_base_addr(u64 addr);
+    u64 get_base_addr();
+    void set_irqid(int irqid);
+
+    static bool active;
+private:
+    virtual void dev_start();
+    virtual const char *thread_name() { return "cadence-input"; }
+    bool ack_irq();
+    void irq_handler();
+    // Default UART irq = SPI 21 = 32 + 21
+    unsigned int irqid = 53;
+    std::unique_ptr<spi_interrupt> _irq;
+    char _uart_fifo = 0;
+    bool _input_ready = false;
+};
+
+}
+
+#endif /* CADENCE_UART_HH */
-- 
2.30.2

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20210311171431.31417-2-stewart.hildebrand%40dornerworks.com.

Reply via email to