qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 07/16] Peripheral driver for S3C SOC Serial ports.


From: Vincent Sanders
Subject: [Qemu-devel] [PATCH 07/16] Peripheral driver for S3C SOC Serial ports.
Date: Sat, 23 May 2009 17:35:25 +0100

Signed-off-by: Vincent Sanders <address@hidden>
---
 Makefile.target     |    1 +
 hw/s3c2410x.c       |   11 ++
 hw/s3c2440.c        |   11 ++
 hw/s3c24xx.h        |    7 ++
 hw/s3c24xx_serial.c |  261 +++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 291 insertions(+), 0 deletions(-)
 create mode 100644 hw/s3c24xx_serial.c

diff --git a/Makefile.target b/Makefile.target
index 81fbc34..2888e86 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -649,6 +649,7 @@ OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o 
tusb6010.o usb-musb.o
 OBJS+= mst_fpga.o mainstone.o
 OBJS+= musicpal.o pflash_cfi02.o
 OBJS+= s3c24xx_memc.o s3c24xx_irq.o s3c24xx_clkcon.o s3c24xx_timers.o
+OBJS+= s3c24xx_serial.o
 OBJS+= s3c2410x.o s3c2440.o
 OBJS+= framebuffer.o
 OBJS+= syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
diff --git a/hw/s3c2410x.c b/hw/s3c2410x.c
index d61abe2..084f9ec 100644
--- a/hw/s3c2410x.c
+++ b/hw/s3c2410x.c
@@ -9,6 +9,7 @@
  */
 
 #include "hw.h"
+#include "sysemu.h"
 
 #include "s3c2410x.h"
 
@@ -27,6 +28,11 @@
 /* Clock control */
 #define CPU_S3C2410X_CLKCON_BASE (CPU_S3C2410X_PERIPHERAL + 0xC000000)
 
+/* serial port bases */
+#define CPU_S3C2410X_SERIAL0_BASE (CPU_S3C2410X_PERIPHERAL + 0x10000000)
+#define CPU_S3C2410X_SERIAL1_BASE (CPU_S3C2410X_PERIPHERAL + 0x10004000)
+#define CPU_S3C2410X_SERIAL2_BASE (CPU_S3C2410X_PERIPHERAL + 0x10008000)
+
 /* Timer controller */
 #define CPU_S3C2410X_TIMERS_BASE (CPU_S3C2410X_PERIPHERAL + 0x11000000)
 
@@ -63,5 +69,10 @@ s3c2410x_init(int sdram_size)
     /* Timer controller */
     s->timers = s3c24xx_timers_init(s, CPU_S3C2410X_TIMERS_BASE, 0, 12000000);
 
+    /* Serial port controllers */
+    s->uart[0] = s3c24xx_serial_init(s, serial_hds[0], 
CPU_S3C2410X_SERIAL0_BASE, 32);
+    s->uart[1] = s3c24xx_serial_init(s, serial_hds[1], 
CPU_S3C2410X_SERIAL1_BASE, 35);
+    s->uart[2] = s3c24xx_serial_init(s, serial_hds[2], 
CPU_S3C2410X_SERIAL2_BASE, 38);
+
     return s;
 }
diff --git a/hw/s3c2440.c b/hw/s3c2440.c
index 1e947df..3896965 100644
--- a/hw/s3c2440.c
+++ b/hw/s3c2440.c
@@ -9,6 +9,7 @@
  */
 
 #include "hw.h"
+#include "sysemu.h"
 
 #include "s3c2440.h"
 
@@ -27,6 +28,11 @@
 /* Clock control */
 #define CPU_S3C2440_CLKCON_BASE (CPU_S3C2440_PERIPHERAL + 0xC000000)
 
+/* serial port bases */
+#define CPU_S3C2440_SERIAL0_BASE (CPU_S3C2440_PERIPHERAL + 0x10000000)
+#define CPU_S3C2440_SERIAL1_BASE (CPU_S3C2440_PERIPHERAL + 0x10004000)
+#define CPU_S3C2440_SERIAL2_BASE (CPU_S3C2440_PERIPHERAL + 0x10008000)
+
 /* Timer controller */
 #define CPU_S3C2440_TIMERS_BASE (CPU_S3C2440_PERIPHERAL + 0x11000000)
 
@@ -61,5 +67,10 @@ s3c2440_init(int sdram_size)
     /* Timer controller */
     s->timers = s3c24xx_timers_init(s, CPU_S3C2440_TIMERS_BASE, 0, 12000000);
 
+    /* Serial port controllers */
+    s->uart[0] = s3c24xx_serial_init(s, serial_hds[0], 
CPU_S3C2440_SERIAL0_BASE, 32);
+    s->uart[1] = s3c24xx_serial_init(s, serial_hds[1], 
CPU_S3C2440_SERIAL1_BASE, 35);
+    s->uart[2] = s3c24xx_serial_init(s, serial_hds[2], 
CPU_S3C2440_SERIAL2_BASE, 38);
+
     return s;
 }
diff --git a/hw/s3c24xx.h b/hw/s3c24xx.h
index 9cc7f37..5a2e806 100644
--- a/hw/s3c24xx.h
+++ b/hw/s3c24xx.h
@@ -26,6 +26,10 @@ typedef struct S3CState_s {
 
     /* timer controller */
     struct s3c24xx_timers_state_s *timers;
+
+    /* Serial ports */
+    struct s3c24xx_serial_dev_s *uart[3];
+
 } S3CState;
 
 
@@ -44,4 +48,7 @@ struct s3c24xx_clkcon_state_s *s3c24xx_clkcon_init(S3CState 
*soc, target_phys_ad
 /* initialise timer controller */
 struct s3c24xx_timers_state_s *s3c24xx_timers_init(S3CState *soc, 
target_phys_addr_t base_addr, uint32_t tclk0, uint32_t tclk1);
 
+/* initialise a serial port controller */
+struct s3c24xx_serial_dev_s *s3c24xx_serial_init(S3CState *soc, 
CharDriverState *chr, target_phys_addr_t base_addr, int irqn);
+
 #endif /* S3C24XX_H */
diff --git a/hw/s3c24xx_serial.c b/hw/s3c24xx_serial.c
new file mode 100644
index 0000000..2b379de
--- /dev/null
+++ b/hw/s3c24xx_serial.c
@@ -0,0 +1,261 @@
+/* hw/s3c24xx_serial.c
+ *
+ * Samsung S3C24XX Serial block
+ *
+ * Copyright 2006, 2007 Daniel Silverstone and Vincent Sanders
+ *
+ * This file is under the terms of the GNU General Public
+ * License Version 2
+ */
+
+#include "hw.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+
+#include "s3c24xx.h"
+
+/* S3C24XX serial port registers */
+
+/* Line control         RW WORD */
+#define S3C_SERIAL_ULCON 0x00
+/* General control      RW WORD */
+#define S3C_SERIAL_UCON  0x04
+/* Fifo control         RW WORD */
+#define S3C_SERIAL_UFCON 0x08
+/* Modem control        RW WORD */
+#define S3C_SERIAL_UMCON 0x0C
+/* TX/RX Status         RO WORD */
+#define S3C_SERIAL_UTRSTAT 0x10
+/* Receive Error Status RO WORD */
+#define S3C_SERIAL_UERSTAT 0x14
+/* FiFo Status          RO WORD */
+#define S3C_SERIAL_UFSTAT 0x18
+/* Modem Status         RO WORD */
+#define S3C_SERIAL_UMSTAT 0x1C
+/* TX buffer            WR BYTE */
+#define S3C_SERIAL_UTXH 0x20
+/* RX buffer            RO BYTE */
+#define S3C_SERIAL_URXH 0x24
+/* BAUD Divisor         RW WORD */
+#define S3C_SERIAL_UBRDIV 0x28
+
+/* S3C24XX serial port state */
+typedef struct s3c24xx_serial_dev_s {
+    uint32_t ulcon, ucon, ufcon, umcon, ubrdiv;
+    unsigned char rx_byte;
+    /* Byte is available to be read */
+    unsigned int rx_available : 1;
+    CharDriverState *chr;
+    qemu_irq tx_irq;
+    qemu_irq rx_irq;
+    qemu_irq tx_level;
+    qemu_irq rx_level;
+} s3c24xx_serial_dev;
+
+static void
+s3c24xx_serial_write_f(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    s3c24xx_serial_dev *s = opaque;
+    int reg = addr & 0x3f;
+
+    switch(reg) {
+    case S3C_SERIAL_ULCON:
+        s->ulcon = value;
+        break;
+
+    case S3C_SERIAL_UCON:
+        s->ucon = value;
+        if( s->ucon & 1<<9 ) {
+            qemu_set_irq(s->tx_level, 1);
+        } else {
+            qemu_set_irq(s->tx_level, 0);
+        }
+        if( !(s->ucon & 1<<8) ) {
+            qemu_set_irq(s->rx_level, 0);
+        }
+        break;
+
+    case S3C_SERIAL_UFCON:
+        s->ufcon = (value & ~6);
+        break;
+
+    case S3C_SERIAL_UMCON:
+        s->umcon = value;
+        break;
+
+    case S3C_SERIAL_UTRSTAT:
+        break;
+
+    case S3C_SERIAL_UERSTAT:
+        break;
+
+    case S3C_SERIAL_UFSTAT:
+        break;
+
+    case S3C_SERIAL_UMSTAT:
+        break;
+
+    case S3C_SERIAL_UTXH: {
+        unsigned char ch = value & 0xff;
+        if (s->chr && ((s->ucon & 1<<5)==0)) {
+            qemu_chr_write(s->chr, &ch, 1);
+        } else {
+            s->rx_byte = ch;
+            s->rx_available = 1;
+            if( s->ucon & 1<<8 ) {
+                qemu_set_irq(s->rx_level, 1);
+            } else {
+                qemu_set_irq(s->rx_irq, 1);
+            }
+        }
+        if (s->ucon & 1<<9) {
+            qemu_set_irq(s->tx_level, 1);
+        } else {
+            qemu_set_irq(s->tx_irq, 1);
+        }
+        break;
+    }
+
+    case S3C_SERIAL_URXH:
+        break;
+
+    case S3C_SERIAL_UBRDIV:
+        s->ubrdiv = value;
+        break;
+
+    default:
+        break;
+    };
+}
+
+static uint32_t
+s3c24xx_serial_read_f(void *opaque, target_phys_addr_t addr)
+{
+    s3c24xx_serial_dev *s = opaque;
+    int reg = addr & 0x3f;
+
+    switch (reg) {
+    case S3C_SERIAL_ULCON:
+        return s->ulcon;
+
+    case S3C_SERIAL_UCON:
+        return s->ucon;
+
+    case S3C_SERIAL_UFCON:
+        return s->ufcon & ~0x8; /* bit 3 is reserved, must be zero */
+
+    case S3C_SERIAL_UMCON:
+        return s->umcon & 0x11; /* Rest are reserved, must be zero */
+
+    case S3C_SERIAL_UTRSTAT:
+        return 6 | s->rx_available; /* TX always clear, RX when available */
+
+    case S3C_SERIAL_UERSTAT:
+        return 0; /* Later, break detect comes in here */
+
+    case S3C_SERIAL_UFSTAT:
+        return s->rx_available; /* TXFIFO, always empty, RXFIFO 0 or 1 bytes */
+
+    case S3C_SERIAL_UMSTAT:
+        return 0;
+
+    case S3C_SERIAL_UTXH:
+        return 0;
+
+    case S3C_SERIAL_URXH:
+        s->rx_available = 0;
+        if (s->ucon & 1<<8) {
+            qemu_set_irq(s->rx_level, 0);
+        }
+        return s->rx_byte;
+
+    case S3C_SERIAL_UBRDIV:
+        return s->ubrdiv;
+
+    default:
+        return 0;
+    };
+}
+
+static CPUReadMemoryFunc *s3c24xx_serial_read[] = {
+    &s3c24xx_serial_read_f,
+    &s3c24xx_serial_read_f,
+    &s3c24xx_serial_read_f,
+};
+
+static CPUWriteMemoryFunc *s3c24xx_serial_write[] = {
+    &s3c24xx_serial_write_f,
+    &s3c24xx_serial_write_f,
+    &s3c24xx_serial_write_f,
+};
+
+
+static void s3c24xx_serial_event(void *opaque, int event)
+{
+}
+
+static int
+s3c24xx_serial_can_receive(void *opaque)
+{
+    s3c24xx_serial_dev *s = opaque;
+
+    /* If there's no byte to be read, we can receive a new one */
+    return !s->rx_available;
+}
+
+static void
+s3c24xx_serial_receive(void *opaque, const uint8_t *buf, int size)
+{
+    s3c24xx_serial_dev *s = opaque;
+    s->rx_byte = buf[0];
+    s->rx_available = 1;
+    if ( s->ucon & 1 << 8 ) {
+        qemu_set_irq(s->rx_level, 1);
+    } else {
+        /* Is there something we can do here to ensure it's just a pulse ? */
+        qemu_set_irq(s->rx_irq, 1);
+    }
+}
+
+/* Create a S3C serial port, the port implementation is common to all
+ * current s3c devices only differing in the I/O base address and number of
+ * ports.
+ */
+struct s3c24xx_serial_dev_s *
+s3c24xx_serial_init(S3CState *soc,
+                    CharDriverState *chr,
+                    target_phys_addr_t base_addr,
+                    int irqn)
+{
+    /* Initialise a serial port at the given port address */
+    s3c24xx_serial_dev *s;
+    int serial_io;
+
+    s = qemu_mallocz(sizeof(s3c24xx_serial_dev));
+    if (!s)
+        return NULL;
+
+    /* initialise serial port context */
+    s->rx_irq = s3c24xx_get_irq(soc->irq, irqn);
+    s->rx_level = s3c24xx_get_irq(soc->irq, irqn + 64);
+
+    s->tx_irq = s3c24xx_get_irq(soc->irq, irqn + 1);
+    s->tx_level = s3c24xx_get_irq(soc->irq, irqn + 1 + 64);
+
+    /* Prepare our MMIO tag */
+    serial_io = cpu_register_io_memory(0, s3c24xx_serial_read, 
s3c24xx_serial_write, s);
+    /* Register the region with the tag */
+    cpu_register_physical_memory(base_addr, 44, serial_io);
+
+    if (chr) {
+        /* If the port is present add to the character device's IO handlers. */
+        s->chr = chr;
+
+        qemu_chr_add_handlers(s->chr,
+                              s3c24xx_serial_can_receive,
+                              s3c24xx_serial_receive,
+                              s3c24xx_serial_event,
+                              s);
+    }
+    return s;
+}
-- 
1.6.0.4





reply via email to

[Prev in Thread] Current Thread [Next in Thread]