PolarFire SoC: Initial platform UART support

Signed-off-by: Jesse Millwood <jesse.millwood@dornerworks.com>
diff --git a/libplatsupport/plat_include/polarfire/platsupport/plat/icicle_mss.h b/libplatsupport/plat_include/polarfire/platsupport/plat/icicle_mss.h
new file mode 100644
index 0000000..7ef92f1
--- /dev/null
+++ b/libplatsupport/plat_include/polarfire/platsupport/plat/icicle_mss.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2020, DornerWorks
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DORNERWORKS_BSD)
+ */
+
+/* These values are grabbed from the ICICLE_MSS_0.xml file that is generated from
+ * the Libero SoC IDE. This file can be found in various ICICLE Kit example projects
+ * including the Yocto recipes */
+
+/* MSS_APB_AHB_CLK in ICICLE_MSS_0.xml */
+#define POLARFIRE_PCLK 150000000
+#define POLARFIRE_BAUD 115200
diff --git a/libplatsupport/plat_include/polarfire/platsupport/plat/serial.h b/libplatsupport/plat_include/polarfire/platsupport/plat/serial.h
new file mode 100644
index 0000000..24232e5
--- /dev/null
+++ b/libplatsupport/plat_include/polarfire/platsupport/plat/serial.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020, DornerWorks
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DORNERWORKS_BSD)
+ */
+
+#pragma once
+#include <autoconf.h>
+
+enum chardev_id {
+    UART0,
+    UART1,
+    UART2,
+    UART3,
+    PS_SERIAL_DEFAULT = UART3
+};
+
+/*
+ * Depending on whether or not the Divisor Latch Access Bit (DLAB) is set
+ * in the line_control (LCR) register (bit 7) the first two registers will
+ * have different functions.
+ * If DLAB is not set in the LCR:
+ *  reg[0]: receive or transmit buffer
+ *    If written to: tranmit buffer
+ *    If read      : receive buffer
+ *  reg[1]: interrupt enable register
+ * If the DLAB is set in the LCR:
+ *  reg[0]: LSB of the divisor
+ *  reg[1]: MSB of the divisor
+ * The interrupt id and fifo control registers are related
+ *  If the 3rd register is written to, it affects the fifo control
+ *  If it is read from, the interrupt id register contents are returned
+ */
+struct uart {
+    union {
+        uint32_t rx_buffer;
+        uint32_t tx_buffer;
+        uint32_t divisor_latch_lsb;
+    };
+    union {
+        uint32_t interrupt_enable;
+        uint32_t divisor_latch_msb;
+    };
+    union {
+        uint32_t interrupt_id;
+        uint32_t fifo_control;
+    };
+    uint32_t line_control ;
+    uint32_t modem_control ;
+    uint32_t line_status;
+    uint32_t modem_status;
+    uint32_t scratch;
+    uint32_t RESERVED ;
+    uint32_t multi_mode_interrupt_enable;
+    uint32_t multi_mode_interrupt_id;
+    uint32_t RESERVED_1 ;
+    uint32_t multi_mode_control_0;
+    uint32_t multi_mode_control_1;
+    uint32_t multi_mode_control_2;
+    uint32_t fractional_divisor;
+    uint32_t glitch_filter;
+    uint32_t transmitter_time_guard;
+    uint32_t receiver_time_out;
+    uint32_t address;
+};
+typedef volatile struct uart uart_regs_t;
+
+
+#define UART0_PADDR 0x20000000
+#define UART1_PADDR 0x20100000
+#define UART2_PADDR 0x20102000
+#define UART3_PADDR 0x20104000
+
+#define UART0_IRQ 90
+#define UART1_IRQ 91
+#define UART2_IRQ 92
+#define UART3_IRQ 93
+
+#define DEFAULT_SERIAL_PADDR UART3_PADDR
+#define DEFAULT_SERIAL_INTERRUPT UART3_IRQ
+
+/* Relevant Register Masks *
+/
+/* Line Control Register */
+#define LCR_WORD_LEN_5                  0b00
+#define LCR_WORD_LEN_6                  0b01
+#define LCR_WORD_LEN_7                  0b10
+#define LCR_WORD_LEN_8                  0b11
+#define LCR_DIV_LATCH_MASK              (1 << 7)
+
+/* Modem Control Register */
+#define MCR_DTR_MASK                    (1 << 0)
+#define MCR_RTS_MASK                    (1 << 1)
+
+/* Multi Mode Control Register 0 */
+#define MM0_ENABLE_FRAC_MASK            (1 << 7)
+
+/* Line Status Register */
+#define LSR_DATA_READY_MASK             (1 << 0)
+#define LSR_TX_HOLD_REG_EMPTY_MASK      (1 << 5)
diff --git a/libplatsupport/src/plat/polarfire/chardev.c b/libplatsupport/src/plat/polarfire/chardev.c
new file mode 100644
index 0000000..835ff0a
--- /dev/null
+++ b/libplatsupport/src/plat/polarfire/chardev.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020, DornerWorks
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DORNERWORKS_BSD)
+ */
+
+/**
+ * Contains the definition for all character devices on this platform.
+ * Currently this is just a simple patch.
+ */
+
+#include "../../chardev.h"
+#include "../../common.h"
+#include <utils/util.h>
+
+
+static const int uart0_irqs[] = {UART0_IRQ, -1};
+static const int uart1_irqs[] = {UART1_IRQ, -1};
+static const int uart2_irqs[] = {UART2_IRQ, -1};
+static const int uart3_irqs[] = {UART3_IRQ, -1};
+
+#define UART_DEFN(devid) {          \
+    .id      = UART##devid,    \
+    .paddr   = UART##devid##_PADDR, \
+    .size    = BIT(12),             \
+    .irqs    = uart##devid##_irqs,  \
+    .init_fn = &uart_init           \
+}
+
+const struct dev_defn dev_defn[] = {
+    UART_DEFN(0),
+    UART_DEFN(1),
+    UART_DEFN(2),
+    UART_DEFN(3)
+};
+
+
+struct ps_chardevice *
+ps_cdev_init(enum chardev_id id, const ps_io_ops_t *o, struct ps_chardevice *d)
+{
+    unsigned int i;
+    for (i = 0; i < ARRAY_SIZE(dev_defn); i++) {
+        if (dev_defn[i].id == id) {
+            return (dev_defn[i].init_fn(dev_defn + i, o, d)) ? NULL : d;
+        }
+    }
+    return NULL;
+}
diff --git a/libplatsupport/src/plat/polarfire/uart.c b/libplatsupport/src/plat/polarfire/uart.c
new file mode 100644
index 0000000..de7182b
--- /dev/null
+++ b/libplatsupport/src/plat/polarfire/uart.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2020, DornerWorks
+ *
+ * This software may be distributed and modified according to the terms of
+ * the BSD 2-Clause license. Note that NO WARRANTY is provided.
+ * See "LICENSE_BSD2.txt" for details.
+ *
+ * @TAG(DORNERWORKS_BSD)
+ */
+
+#include <autoconf.h>
+
+#include <stdlib.h>
+#include <platsupport/serial.h>
+#include <platsupport/plat/serial.h>
+#include <platsupport/plat/icicle_mss.h>
+#include <string.h>
+
+#include "../../chardev.h"
+
+static inline uart_regs_t *uart_get_priv(ps_chardevice_t *d)
+{
+    return (uart_regs_t *)d->vaddr;
+}
+
+int uart_getchar(ps_chardevice_t *d)
+{
+    uart_regs_t *regs = uart_get_priv(d);
+    if (regs->line_status & LSR_DATA_READY_MASK) {
+        return regs->rx_buffer;
+    }
+    return -1;
+}
+
+static void busy_wait_fifo_empty_and_tx_char(uart_regs_t *regs, int c)
+{
+    // wait until FIFO empty
+    while ((regs->line_status & LSR_TX_HOLD_REG_EMPTY_MASK) == 0) {
+        // busy loop
+    }
+
+    regs->tx_buffer = c;
+}
+
+int uart_putchar(ps_chardevice_t *d, int c)
+{
+    uart_regs_t *regs = uart_get_priv(d);
+
+    // turn LF into CR+LF if SERIAL_AUTO_CR is active
+    if ((c == '\n') && (d->flags & SERIAL_AUTO_CR)) {
+        busy_wait_fifo_empty_and_tx_char(regs, '\r');
+    }
+
+    busy_wait_fifo_empty_and_tx_char(regs, c);
+    return c;
+}
+
+static void uart_handle_irq(ps_chardevice_t *d UNUSED)
+{
+    // IRQs are cleared when the TX/RX watermark conditions are no longer met
+    // so there is nothing to do here.
+}
+
+int uart_init(const struct dev_defn *defn,
+              const ps_io_ops_t *ops,
+              ps_chardevice_t *dev)
+{
+    uart_regs_t *regs;
+    uint32_t divisor;
+    uint32_t divisor_by_128;
+    uint32_t divisor_by_64;
+    uint32_t fractional_divisor;
+    /* Attempt to map the virtual address, assure this works */
+    void *vaddr = chardev_map(defn, ops);
+    if (vaddr == NULL) {
+        return -1;
+    }
+
+    memset(dev, 0, sizeof(*dev));
+
+    /* Set up all the  device properties. */
+    dev->id         = defn->id;
+    dev->vaddr      = (void *)vaddr;
+    dev->read       = &uart_read;
+    dev->write      = &uart_write;
+    dev->handle_irq = &uart_handle_irq;
+    dev->irqs       = defn->irqs;
+    dev->ioops      = *ops;
+    dev->flags      = SERIAL_AUTO_CR;
+
+    regs = uart_get_priv(dev);
+
+    regs->line_control = LCR_WORD_LEN_8;
+    regs->modem_control = 0;
+    regs->multi_mode_control_0 = 0;
+    regs->multi_mode_control_1 = 0;
+    regs->multi_mode_control_2 = 0;
+    regs->glitch_filter = 0;
+    regs->transmitter_time_guard = 0;
+    regs->receiver_time_out = 0;
+
+    /*
+     * Configure baud rate divisors. This uses the fractional baud rate divisor
+     * where possible to provide the most accurate baud rate possible.
+     * This algorithm is taken from the Microchip MSS UART Driver (LIC:MIT)
+     */
+    divisor_by_128 = (uint32_t)((8UL * POLARFIRE_PCLK) / POLARFIRE_BAUD);
+    divisor_by_64 = divisor_by_128 / 2u;
+    divisor = divisor_by_64 / 64u;
+    fractional_divisor = divisor_by_64 - (divisor * 64u);
+    fractional_divisor += (divisor_by_128 - (divisor * 128u))
+                          - (fractional_divisor * 2u);
+
+    // div: 81, frac: 24 => 115207
+
+    regs->line_control |= LCR_DIV_LATCH_MASK;
+    regs->divisor_latch_msb = (uint8_t)(divisor >> 8);
+    regs->divisor_latch_lsb = (uint8_t)divisor;
+    regs->line_control &= ~LCR_DIV_LATCH_MASK;
+
+    regs->multi_mode_control_0 |= MM0_ENABLE_FRAC_MASK;
+    regs->fractional_divisor = (uint8_t)fractional_divisor;
+    regs->line_control = LCR_WORD_LEN_8;
+
+    return 0;
+}