]> Git Repo - qemu.git/blobdiff - hw/etraxfs_ser.c
RTC: Do not fire timer periodically to catch next alarm
[qemu.git] / hw / etraxfs_ser.c
index 778b429c0a8b47d2f41bfd7037f0834df49afe38..5f16b178355848acaa34c0b81da6c0c318e51706 100644 (file)
  * THE SOFTWARE.
  */
 
-#include <stdio.h>
-#include <ctype.h>
-#include "hw.h"
+#include "sysbus.h"
+#include "qemu-char.h"
+#include "qemu-log.h"
 
 #define D(x)
 
-#define RW_TR_DMA_EN 0x04
-#define RW_DOUT 0x1c
-#define RW_STAT_DIN 0x20
-#define R_STAT_DIN 0x24
+#define RW_TR_CTRL     (0x00 / 4)
+#define RW_TR_DMA_EN   (0x04 / 4)
+#define RW_REC_CTRL    (0x08 / 4)
+#define RW_DOUT        (0x1c / 4)
+#define RS_STAT_DIN    (0x20 / 4)
+#define R_STAT_DIN     (0x24 / 4)
+#define RW_INTR_MASK   (0x2c / 4)
+#define RW_ACK_INTR    (0x30 / 4)
+#define R_INTR         (0x34 / 4)
+#define R_MASKED_INTR  (0x38 / 4)
+#define R_MAX          (0x3c / 4)
 
-static uint32_t ser_readb (void *opaque, target_phys_addr_t addr)
+#define STAT_DAV     16
+#define STAT_TR_IDLE 22
+#define STAT_TR_RDY  24
+
+struct etrax_serial
 {
-       D(CPUState *env = opaque);
-       D(printf ("%s %x pc=%x\n", __func__, addr, env->pc));
-       return 0;
-}
-static uint32_t ser_readw (void *opaque, target_phys_addr_t addr)
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+    CharDriverState *chr;
+    qemu_irq irq;
+
+    int pending_tx;
+
+    uint8_t rx_fifo[16];
+    unsigned int rx_fifo_pos;
+    unsigned int rx_fifo_len;
+
+    /* Control registers.  */
+    uint32_t regs[R_MAX];
+};
+
+static void ser_update_irq(struct etrax_serial *s)
 {
-       D(CPUState *env = opaque);
-       D(printf ("%s %x pc=%x\n", __func__, addr, env->pc));
-       return 0;
+
+    if (s->rx_fifo_len) {
+        s->regs[R_INTR] |= 8;
+    } else {
+        s->regs[R_INTR] &= ~8;
+    }
+
+    s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK];
+    qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]);
 }
 
-static uint32_t ser_readl (void *opaque, target_phys_addr_t addr)
+static uint64_t
+ser_read(void *opaque, target_phys_addr_t addr, unsigned int size)
 {
-       D(CPUState *env = opaque);
-       uint32_t r = 0;
-
-       switch (addr & 0xfff)
-       {
-               case RW_TR_DMA_EN:
-                       break;
-               case R_STAT_DIN:
-                       r |= 1 << 24; /* set tr_rdy.  */
-                       r |= 1 << 22; /* set tr_idle.  */
-                       break;
-
-               default:
-                       D(printf ("%s %x p=%x\n", __func__, addr, env->pc));
-                       break;
-       }
-       return r;
+    struct etrax_serial *s = opaque;
+    D(CPUCRISState *env = s->env);
+    uint32_t r = 0;
+
+    addr >>= 2;
+    switch (addr)
+    {
+        case R_STAT_DIN:
+            r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15];
+            if (s->rx_fifo_len) {
+                r |= 1 << STAT_DAV;
+            }
+            r |= 1 << STAT_TR_RDY;
+            r |= 1 << STAT_TR_IDLE;
+            break;
+        case RS_STAT_DIN:
+            r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15];
+            if (s->rx_fifo_len) {
+                r |= 1 << STAT_DAV;
+                s->rx_fifo_len--;
+            }
+            r |= 1 << STAT_TR_RDY;
+            r |= 1 << STAT_TR_IDLE;
+            break;
+        default:
+            r = s->regs[addr];
+            D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r));
+            break;
+    }
+    return r;
 }
 
 static void
-ser_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+ser_write(void *opaque, target_phys_addr_t addr,
+          uint64_t val64, unsigned int size)
 {
-       D(CPUState *env = opaque);
-       D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc));
+    struct etrax_serial *s = opaque;
+    uint32_t value = val64;
+    unsigned char ch = val64;
+    D(CPUCRISState *env = s->env);
+
+    D(qemu_log("%s " TARGET_FMT_plx "=%x\n",  __func__, addr, value));
+    addr >>= 2;
+    switch (addr)
+    {
+        case RW_DOUT:
+            qemu_chr_fe_write(s->chr, &ch, 1);
+            s->regs[R_INTR] |= 3;
+            s->pending_tx = 1;
+            s->regs[addr] = value;
+            break;
+        case RW_ACK_INTR:
+            if (s->pending_tx) {
+                value &= ~1;
+                s->pending_tx = 0;
+                D(qemu_log("fixedup value=%x r_intr=%x\n",
+                           value, s->regs[R_INTR]));
+            }
+            s->regs[addr] = value;
+            s->regs[R_INTR] &= ~value;
+            D(printf("r_intr=%x\n", s->regs[R_INTR]));
+            break;
+        default:
+            s->regs[addr] = value;
+            break;
+    }
+    ser_update_irq(s);
 }
-static void
-ser_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+
+static const MemoryRegionOps ser_ops = {
+    .read = ser_read,
+    .write = ser_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void serial_receive(void *opaque, const uint8_t *buf, int size)
 {
-       D(CPUState *env = opaque);
-       D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc));
+    struct etrax_serial *s = opaque;
+    int i;
+
+    /* Got a byte.  */
+    if (s->rx_fifo_len >= 16) {
+        qemu_log("WARNING: UART dropped char.\n");
+        return;
+    }
+
+    for (i = 0; i < size; i++) { 
+        s->rx_fifo[s->rx_fifo_pos] = buf[i];
+        s->rx_fifo_pos++;
+        s->rx_fifo_pos &= 15;
+        s->rx_fifo_len++;
+    }
+
+    ser_update_irq(s);
 }
-static void
-ser_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+
+static int serial_can_receive(void *opaque)
+{
+    struct etrax_serial *s = opaque;
+    int r;
+
+    /* Is the receiver enabled?  */
+    if (!(s->regs[RW_REC_CTRL] & (1 << 3))) {
+        return 0;
+    }
+
+    r = sizeof(s->rx_fifo) - s->rx_fifo_len;
+    return r;
+}
+
+static void serial_event(void *opaque, int event)
 {
-       D(CPUState *env = opaque);
-
-       switch (addr & 0xfff)
-       {
-               case RW_TR_DMA_EN:
-                       break;
-               case RW_DOUT:
-                       if (isprint(value) || isspace(value))
-                               putchar(value);
-                       else
-                               putchar('.');
-                       fflush(stdout);
-                       break;
-               default:
-                       D(printf ("%s %x %x pc=%x\n",
-                                 __func__, addr, value, env->pc));
-                       break;
-       }
+
 }
 
-static CPUReadMemoryFunc *ser_read[] = {
-       &ser_readb,
-       &ser_readw,
-       &ser_readl,
-};
+static void etraxfs_ser_reset(DeviceState *d)
+{
+    struct etrax_serial *s = container_of(d, typeof(*s), busdev.qdev);
+
+    /* transmitter begins ready and idle.  */
+    s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY);
+    s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE);
+
+    s->regs[RW_REC_CTRL] = 0x10000;
+
+}
 
-static CPUWriteMemoryFunc *ser_write[] = {
-       &ser_writeb,
-       &ser_writew,
-       &ser_writel,
+static int etraxfs_ser_init(SysBusDevice *dev)
+{
+    struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev);
+
+    sysbus_init_irq(dev, &s->irq);
+    memory_region_init_io(&s->mmio, &ser_ops, s, "etraxfs-serial", R_MAX * 4);
+    sysbus_init_mmio(dev, &s->mmio);
+
+    s->chr = qemu_char_get_next_serial();
+    if (s->chr)
+        qemu_chr_add_handlers(s->chr,
+                      serial_can_receive, serial_receive,
+                      serial_event, s);
+    return 0;
+}
+
+static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = etraxfs_ser_init;
+    dc->reset = etraxfs_ser_reset;
+}
+
+static TypeInfo etraxfs_ser_info = {
+    .name          = "etraxfs,serial",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct etrax_serial),
+    .class_init    = etraxfs_ser_class_init,
 };
 
-void etraxfs_ser_init(CPUState *env, qemu_irq *irqs, target_phys_addr_t base)
+static void etraxfs_serial_register_types(void)
 {
-       int ser_regs;
-       ser_regs = cpu_register_io_memory(0, ser_read, ser_write, env);
-       cpu_register_physical_memory (base, 0x3c, ser_regs);
+    type_register_static(&etraxfs_ser_info);
 }
+
+type_init(etraxfs_serial_register_types)
This page took 0.033659 seconds and 4 git commands to generate.