]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
kvm: Improve 8250 serial console support
authorPekka Enberg <penberg@kernel.org>
Sat, 8 Jan 2011 17:29:23 +0000 (19:29 +0200)
committerPekka Enberg <penberg@kernel.org>
Sat, 8 Jan 2011 17:57:47 +0000 (19:57 +0200)
Signed-off-by: Pekka Enberg <penberg@kernel.org>
tools/kvm/early_printk.c

index 6087976a4093fa8c6d205ba78e5605b2096e4246..ffc405fa86c6ecfd22dd4de13491503eae0430fc 100644 (file)
 #include "kvm/early_printk.h"
 
 #include "kvm/ioport.h"
+#include "kvm/util.h"
 
-#include <stdio.h>
+#include <stdbool.h>
 
-static int early_serial_base = 0x3f8;  /* ttyS0 */
+/* Transmitter holding register */
+#define THR             0
 
-#define XMTRDY          0x20
+/* Receive buffer register */
+#define RBR             0
 
-#define TXR             0       /*  Transmit register (WRITE) */
-#define LSR             5       /*  Line Status               */
-#define MSR            6       /*  Modem Status              */
+/* Divisor latch low byte */
+#define DLL            0
 
-static bool early_serial_txr_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
-{
-       char *p = data;
-       int i;
+/* Divisor latch high byte */
+#define DLM            1
 
-       while (count--) {
-               for (i = 0; i < size; i++)
-                       fprintf(stdout, "%c", *p++);
-       }
-       fflush(stdout);
+/* Interrupt enable register */
+#define IER            1
 
-       return true;
-}
+/* Interrupt identification register */
+#define IIR            2
 
-static bool early_serial_rxr_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
-{
-       return true;
-}
+/* 16550 FIFO Control Register */
+#define FCR            2
 
-static struct ioport_operations early_serial_txr_rxr_ops = {
-       .io_out         = early_serial_txr_out,
-       .io_in          = early_serial_rxr_in,
+/* Line control register */
+#define LCR            3
+enum {
+       DLAB            = 1 << 7,               /* Divisor latch access bit (DLAB) */
+       /* bit 7 - set break enable */
+       PM2             = 1 << 5,
+       PM1             = 1 << 4,
+       PM0             = 1 << 3,
+       STB             = 1 << 2,
+       WLS1            = 1 << 1,
+       WLS0            = 1 << 0,
 };
 
-static bool early_serial_lsr_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
-{
-       uint8_t *p = data;
+/* Modem control register */
+#define MCR            4
 
-       *p      = XMTRDY;
+/* Line status register */
+#define LSR            5
 
-       return true;
-}
+/* Modem status register */
+#define MSR            6
+
+/* Scratch register */
+#define SCR            7
 
-static struct ioport_operations early_serial_lsr_ops = {
-       .io_in          = early_serial_lsr_in,
+struct serial8250_device {
+       uint16_t                iobase;
+       uint8_t                 dll;
+       uint8_t                 dlm;
+       uint8_t                 ier;
+       uint8_t                 fcr;
+       uint8_t                 lcr;
+       uint8_t                 mcr;
+       uint8_t                 scr;
 };
 
-static bool early_serial_msr_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
+static struct serial8250_device device = {
+       .iobase                 = 0x3f8,        /* ttyS0 */
+};
+
+static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
+{
+       uint16_t offset = port - device.iobase;
+
+       if (device.lcr & DLAB) {
+               switch (offset) {
+               case DLL:
+                       device.dll              = ioport__read8(data);
+                       break;
+               case DLM:
+                       device.dlm              = ioport__read8(data);
+                       break;
+               case FCR:
+                       device.fcr              = ioport__read8(data);
+                       break;
+               case LCR:
+                       device.lcr              = ioport__read8(data);
+                       break;
+               default:
+                       return false;
+               }
+       } else {
+               switch (offset) {
+               case THR: {
+                       char *p = data;
+                       int i;
+
+                       while (count--) {
+                               for (i = 0; i < size; i++)
+                                       fprintf(stdout, "%c", *p++);
+                       }
+                       fflush(stdout);
+                       break;
+               }
+               case IER:
+                       device.ier              = ioport__read8(data);
+                       break;
+               case FCR:
+                       device.fcr              = ioport__read8(data);
+                       break;
+               case LCR:
+                       device.lcr              = ioport__read8(data);
+                       break;
+               case MCR:
+                       device.mcr              = ioport__read8(data);
+                       break;
+               case SCR:
+                       device.scr              = ioport__read8(data);
+                       break;
+               default:
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
 {
+       uint16_t offset = port - device.iobase;
+
+       if (device.lcr & DLAB)
+               return false;
+
+       switch (offset) {
+       case THR:
+               ioport__write8(data, 0x00);
+               break;
+       case IER:
+               ioport__write8(data, device.ier);
+               break;
+       case IIR:
+               ioport__write8(data, 0x01); /* no interrupt pending */
+               break;
+       case LCR:
+               ioport__write8(data, device.lcr);
+               break;
+       case MCR:
+               ioport__write8(data, device.mcr);
+               break;
+       case LSR:
+               ioport__write8(data, 0x20); /* XMTRDY */
+               break;
+       case MSR:
+               ioport__write8(data, 0x01); /* clear to send */
+               break;
+       case SCR:
+               ioport__write8(data, device.scr);
+               break;
+       default:
+               return false;
+       }
+
        return true;
 }
 
-static struct ioport_operations early_serial_msr_ops = {
-       .io_in          = early_serial_msr_in,
+static struct ioport_operations serial8250_ops = {
+       .io_in          = serial8250_in,
+       .io_out         = serial8250_out,
 };
 
 void early_printk__init(void)
 {
-       ioport__register(early_serial_base + TXR, &early_serial_txr_rxr_ops, 1);
-       ioport__register(early_serial_base + LSR, &early_serial_lsr_ops, 1);
-       ioport__register(early_serial_base + MSR, &early_serial_msr_ops, 1);
+       ioport__register(device.iobase, &serial8250_ops, 8);
 }