Add support for OXPCIe PCIe serial port cards

SeaBIOS right now only supports IO mapped standard PC serial ports
for debugging. This patch adds support for using the Startech Mini PCIe
cards on systems without a functional onboard UART.

BUG=none
TEST=boot coreboot on Alex with Startech PCIe serial adapter and see output

Change-Id: Ic36029704c6c721ff69fce72934c1df36686cb78
diff --git a/src/Kconfig b/src/Kconfig
index 123db01..69a95a4 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -378,6 +378,13 @@
         help
             Base port for serial - generally 0x3f8, 0x2f8, 0x3e8, or 0x2e8.
 
+    config DEBUG_SERIAL_OXFORD
+        depends on DEBUG_LEVEL != 0
+        bool "OXPCIe 952 serial port debugging"
+        default n
+        help
+            Send debugging information to serial port.
+
     config SCREEN_AND_DEBUG
         depends on DEBUG_LEVEL != 0
         bool "Show screen writes on debug ports"
diff --git a/src/boot.c b/src/boot.c
index 9a67916..23839dc 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -644,7 +644,7 @@
 void VISIBLE32FLAT
 handle_18(void)
 {
-    debug_serial_setup();
+    debug_setup();
     debug_enter(NULL, DEBUG_HDL_18);
     u16 ebda_seg = get_ebda_seg();
     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
@@ -656,7 +656,7 @@
 void VISIBLE32FLAT
 handle_19(void)
 {
-    debug_serial_setup();
+    debug_setup();
     debug_enter(NULL, DEBUG_HDL_19);
     SET_EBDA(boot_sequence, 0);
     do_boot(0);
diff --git a/src/optionroms.c b/src/optionroms.c
index 3ecd6d6..0b21300 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -99,7 +99,7 @@
     call16big(&br);
     finish_preempt();
 
-    debug_serial_setup();
+    debug_setup();
 }
 
 // Execute a given option rom at the standard entry vector.
diff --git a/src/output.c b/src/output.c
index 7c10d33..2399209 100644
--- a/src/output.c
+++ b/src/output.c
@@ -11,6 +11,7 @@
 #include "bregs.h" // struct bregs
 #include "config.h" // CONFIG_*
 #include "biosvar.h" // GET_GLOBAL
+#include "pci.h" // pci_find_device
 
 struct putcinfo {
     void (*func)(struct putcinfo *info, char c);
@@ -23,7 +24,10 @@
 
 #define DEBUG_TIMEOUT 100000
 
-void
+#define OXPCIE_COM1 (oxpcie_bar + 0x1000)
+u32 oxpcie_bar VAR16VISIBLE;
+
+static void
 debug_serial_setup(void)
 {
     if (!CONFIG_DEBUG_SERIAL)
@@ -69,29 +73,114 @@
             return;
 }
 
-// Write a character to debug port(s).
 static void
-putc_debug(struct putcinfo *action, char c)
+debug_serial_oxford_setup(void)
+{
+    if (!CONFIG_DEBUG_SERIAL_OXFORD)
+        return;
+
+    int oxpcie_bdf;
+    oxpcie_bar = 0;
+    oxpcie_bdf = pci_find_device(0x1415, 0xc158);
+    if (oxpcie_bdf != -1)
+        oxpcie_bar = pci_config_readl(oxpcie_bdf, 0x10);
+    if (!oxpcie_bar)
+        return;
+    // setup for serial logging: 8N1
+    u8 oldparam, newparam = 0x03;
+    oldparam = readb((void *)OXPCIE_COM1+SEROFF_LCR);
+    writeb((void *)OXPCIE_COM1+SEROFF_LCR, newparam);
+    // Disable irqs
+    u8 oldier, newier = 0;
+    oldier = readb((void *)OXPCIE_COM1+SEROFF_IER);
+    writeb((void *)OXPCIE_COM1+SEROFF_IER, newier);
+
+    if (oldparam != newparam || oldier != newier)
+        dprintf(1, "Changing serial settings was %x/%x now %x/%x\n"
+                , oldparam, oldier, newparam, newier);
+}
+
+// Write a character to the serial port.
+static void
+debug_serial_oxford(char c)
+{
+    if (!CONFIG_DEBUG_SERIAL_OXFORD)
+        return;
+    if (!oxpcie_bar)
+        return;
+
+    int timeout = DEBUG_TIMEOUT;
+    while ((readb((void *)OXPCIE_COM1+SEROFF_LSR) & 0x60) != 0x60)
+        if (!timeout--)
+            // Ran out of time.
+            return;
+    writeb((void *)OXPCIE_COM1+SEROFF_DATA, c);
+}
+
+// Make sure all serial port writes have been completely sent.
+static void
+debug_serial_oxford_flush(void)
+{
+    if (!CONFIG_DEBUG_SERIAL_OXFORD)
+        return;
+    if (!oxpcie_bar)
+        return;
+    int timeout = DEBUG_TIMEOUT;
+
+    while ((readb((void *)OXPCIE_COM1+SEROFF_LSR) & 0x40) != 0x40)
+        if (!timeout--)
+            // Ran out of time.
+            return;
+}
+
+void
+debug_setup(void)
 {
     if (! CONFIG_DEBUG_LEVEL)
         return;
+    if (CONFIG_DEBUG_SERIAL)
+        debug_serial_setup();
+    if (CONFIG_DEBUG_SERIAL_OXFORD)
+        debug_serial_oxford_setup();
+}
+
+// Write a character to debug port(s).
+static void
+debug_putc(struct putcinfo *action, char c)
+{
+    if (! CONFIG_DEBUG_LEVEL)
+        return;
+    if (c == '\n')
+        debug_putc(action, '\r');
     if (! CONFIG_COREBOOT)
         // Send character to debug port.
         outb(c, PORT_BIOS_DEBUG);
-    if (c == '\n')
-        debug_serial('\r');
-    debug_serial(c);
+    if (CONFIG_DEBUG_SERIAL)
+        debug_serial(c);
+    if (CONFIG_DEBUG_SERIAL_OXFORD)
+        debug_serial_oxford(c);
 }
 
-// In segmented mode just need a dummy variable (putc_debug is always
+static void
+debug_flush(void)
+{
+    if (! CONFIG_DEBUG_LEVEL)
+        return;
+    if (CONFIG_DEBUG_SERIAL)
+        debug_serial_flush();
+    if (CONFIG_DEBUG_SERIAL_OXFORD)
+        debug_serial_oxford_flush();
+}
+
+// In segmented mode just need a dummy variable (debug_putc is always
 // used anyway), and in 32bit flat mode need a pointer to the 32bit
-// instance of putc_debug().
+// instance of debug_putc().
 #if MODE16
 static struct putcinfo debuginfo VAR16;
 #elif MODESEGMENT
 static struct putcinfo debuginfo VAR32SEG;
 #else
-static struct putcinfo debuginfo = { putc_debug };
+static struct putcinfo debuginfo = { debug_putc };
 #endif
 
 
@@ -116,7 +205,7 @@
 putc_screen(struct putcinfo *action, char c)
 {
     if (CONFIG_SCREEN_AND_DEBUG)
-        putc_debug(&debuginfo, c);
+        debug_putc(&debuginfo, c);
     if (c == '\n')
         screenc('\r');
     screenc(c);
@@ -135,7 +224,7 @@
 {
     if (MODESEGMENT) {
         // Only debugging output supported in segmented mode.
-        putc_debug(action, c);
+        debug_putc(action, c);
         return;
     }
 
@@ -324,7 +413,7 @@
         va_start(args, fmt);
         bvprintf(&debuginfo, fmt, args);
         va_end(args);
-        debug_serial_flush();
+        debug_flush();
     }
 
     // XXX - use PANIC PORT.
@@ -341,10 +430,10 @@
         struct thread_info *cur = getCurThread();
         if (cur != &MainThread) {
             // Show "thread id" for this debug message.
-            putc_debug(&debuginfo, '|');
+            debug_putc(&debuginfo, '|');
             puthex(&debuginfo, (u32)cur, 8, 0);
-            putc_debug(&debuginfo, '|');
-            putc_debug(&debuginfo, ' ');
+            debug_putc(&debuginfo, '|');
+            debug_putc(&debuginfo, ' ');
         }
     }
 
@@ -352,7 +441,7 @@
     va_start(args, fmt);
     bvprintf(&debuginfo, fmt, args);
     va_end(args);
-    debug_serial_flush();
+    debug_flush();
 }
 
 void
@@ -364,7 +453,7 @@
     bvprintf(&screeninfo, fmt, args);
     va_end(args);
     if (CONFIG_SCREEN_AND_DEBUG)
-        debug_serial_flush();
+        debug_flush();
 }
 
 
@@ -455,7 +544,7 @@
         d+=4;
     }
     putc(&debuginfo, '\n');
-    debug_serial_flush();
+    debug_flush();
 }
 
 static void
@@ -479,7 +568,7 @@
 {
     puts_cs(&debuginfo, fname);
     putc(&debuginfo, '\n');
-    debug_serial_flush();
+    debug_flush();
 }
 
 // Function called on handler startup.
diff --git a/src/post.c b/src/post.c
index 7d2b5f2..f39d862 100644
--- a/src/post.c
+++ b/src/post.c
@@ -373,7 +373,7 @@
 {
     init_dma();
 
-    debug_serial_setup();
+    debug_setup();
     dprintf(1, "Start bios (version %s)\n", VERSION);
 
     if (HaveRunPost)
diff --git a/src/resume.c b/src/resume.c
index 20e2e3d..dacb889 100644
--- a/src/resume.c
+++ b/src/resume.c
@@ -30,7 +30,7 @@
 {
     init_dma();
 
-    debug_serial_setup();
+    debug_setup();
     dprintf(1, "In resume (status=%d)\n", status);
 
     switch (status) {
diff --git a/src/util.h b/src/util.h
index 2160b37..5a0eb96 100644
--- a/src/util.h
+++ b/src/util.h
@@ -231,7 +231,7 @@
 void check_preempt(void);
 
 // output.c
-void debug_serial_setup(void);
+void debug_setup(void);
 void panic(const char *fmt, ...)
     __attribute__ ((format (printf, 1, 2))) __noreturn;
 void printf(const char *fmt, ...)