All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
@ 2012-08-23 13:59 Alberto Garcia
  2012-08-23 13:59 ` [Qemu-devel] [PATCH 1/2] Add TEWS TPCI200 " Alberto Garcia
                   ` (8 more replies)
  0 siblings, 9 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-08-23 13:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Anthony Liguori, Alberto Garcia, Paul Brook

Hello,

I have been working on the emulation of the IP-Octal 232 IndustryPack
module, a device that implements eight RS-232 serial ports.

IndustryPack modules are small boards that are attached to a carrier
board, so in order to have a complete and working system I also wrote
an emulation of the TEWS TPCI200 carrier, which is a PCI board to
which up to 4 IndustryPack modules can be attached.

I have been using this virtual device to test and debug the ipoctal
driver in the Linux kernel. I'm publishing the code now, the emulation
is not 100% complete but it's fairly functional, and it can be used to
easily implement more IndustryPack modules.

So the work consists on three parts:
  - TPCI200, the bridge between PCI and IndustryPack.
  - The IndustryPack bus.
  - IP-Octal, the IndustryPack module.

Basic usage:

$ qemu -device tpci200 -device ipoctal

Each one of the serial ports in the ipoctal device can be redirected
to a character device in the host using the functionality provided by
QEMU. The 'serial0' to 'serial7' parameters can be used to specify
each one of the redirections.

Suggestions, comments, etc, will be appreciated.

Alberto Garcia (2):
  Add TEWS TPCI200 IndustryPack emulation
  Add IP-Octal 232 IndustryPack emulation

 default-configs/pci.mak |    1 +
 hw/Makefile.objs        |    3 +
 hw/ipack.c              |  106 ++++++++
 hw/ipack.h              |   75 ++++++
 hw/ipoctal.c            |  662 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/pci_ids.h            |    3 +
 hw/tpci200.c            |  630 ++++++++++++++++++++++++++++++++++++++++++++
 7 ficheiros modificados, 1480 adições(+)
 create mode 100644 hw/ipack.c
 create mode 100644 hw/ipack.h
 create mode 100644 hw/ipoctal.c
 create mode 100644 hw/tpci200.c

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH 1/2] Add TEWS TPCI200 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
@ 2012-08-23 13:59 ` Alberto Garcia
  2012-08-23 13:59 ` [Qemu-devel] [PATCH 2/2] Add IP-Octal 232 " Alberto Garcia
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-08-23 13:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Anthony Liguori, Alberto Garcia, Paul Brook

The TPCI200 is a PCI board that supports up to 4 IndustryPack modules.

A new bus type called 'IndustryPack' has been created so any
compatible module can be attached to this board.

Signed-off-by: Alberto Garcia <agarcia@igalia.com>
---
 default-configs/pci.mak |    1 +
 hw/Makefile.objs        |    3 +
 hw/ipack.c              |  106 ++++++++
 hw/ipack.h              |   75 ++++++
 hw/pci_ids.h            |    3 +
 hw/tpci200.c            |  630 +++++++++++++++++++++++++++++++++++++++++++++++
 6 ficheiros modificados, 818 adições(+)
 create mode 100644 hw/ipack.c
 create mode 100644 hw/ipack.h
 create mode 100644 hw/tpci200.c

diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 69e18f1..7753a4f 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -19,3 +19,4 @@ CONFIG_IDE_PCI=y
 CONFIG_AHCI=y
 CONFIG_ESP=y
 CONFIG_ESP_PCI=y
+CONFIG_IPACK=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 7f57ed5..f4a3a9b 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -99,6 +99,9 @@ hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 
 hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
 
+# IndustryPack
+hw-obj-$(CONFIG_IPACK) += tpci200.o ipack.o
+
 # PCI network cards
 hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
 hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
diff --git a/hw/ipack.c b/hw/ipack.c
new file mode 100644
index 0000000..59e272b
--- /dev/null
+++ b/hw/ipack.c
@@ -0,0 +1,106 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "ipack.h"
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
+{
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        IPackDevice *ip = DO_UPCAST(IPackDevice, qdev, qdev);
+        if (ip->slot == slot) {
+            return ip;
+        }
+    }
+    return NULL;
+}
+
+static int ipack_device_dev_init(DeviceState *qdev)
+{
+    IPackBus *bus = DO_UPCAST(IPackBus, qbus, qdev->parent_bus);
+    IPackDevice *dev = DO_UPCAST(IPackDevice, qdev, qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (dev->slot < 0) {
+        dev->slot = bus->free_slot;
+    }
+    if (dev->slot >= bus->n_slots) {
+        return -1;
+    }
+    bus->free_slot = dev->slot + 1;
+
+    dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2);
+
+    return k->init(dev);
+}
+
+static int ipack_device_dev_exit(DeviceState *qdev)
+{
+    IPackDevice *dev = DO_UPCAST(IPackDevice, qdev, qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (k->exit) {
+        k->exit(dev);
+    }
+
+    qemu_free_irqs(dev->irq);
+
+    return 0;
+}
+
+static Property ipack_device_props[] = {
+    DEFINE_PROP_INT32("slot", IPackDevice, slot, -1),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void ipack_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_type = TYPE_IPACK_BUS;
+    k->init = ipack_device_dev_init;
+    k->exit = ipack_device_dev_exit;
+    k->props = ipack_device_props;
+}
+
+const VMStateDescription vmstate_ipack_device = {
+    .name = "ipack_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(slot, IPackDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const TypeInfo ipack_device_info = {
+    .name          = TYPE_IPACK_DEVICE,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(IPackDevice),
+    .class_size    = sizeof(IPackDeviceClass),
+    .class_init    = ipack_device_class_init,
+    .abstract      = true,
+};
+
+static const TypeInfo ipack_bus_info = {
+    .name = TYPE_IPACK_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(IPackBus),
+};
+
+static void ipack_register_types(void)
+{
+    type_register_static(&ipack_device_info);
+    type_register_static(&ipack_bus_info);
+}
+
+type_init(ipack_register_types)
diff --git a/hw/ipack.h b/hw/ipack.h
new file mode 100644
index 0000000..61a9019
--- /dev/null
+++ b/hw/ipack.h
@@ -0,0 +1,75 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#ifndef QEMU_IPACK_H
+#define QEMU_IPACK_H
+
+#include "qdev.h"
+
+typedef struct IPackBus IPackBus;
+
+#define TYPE_IPACK_BUS "IndustryPack"
+#define IPACK_BUS(obj) OBJECT_CHECK(IPackBus, (obj), TYPE_IPACK_BUS)
+
+struct IPackBus {
+    BusState qbus;
+    uint8_t n_slots;
+    uint8_t free_slot;
+    qemu_irq_handler set_irq;
+};
+
+typedef struct IPackDevice IPackDevice;
+typedef struct IPackDeviceClass IPackDeviceClass;
+
+#define TYPE_IPACK_DEVICE "ipack-device"
+#define IPACK_DEVICE(obj) \
+     OBJECT_CHECK(IPackDevice, (obj), TYPE_IPACK_DEVICE)
+#define IPACK_DEVICE_CLASS(klass)                                        \
+     OBJECT_CLASS_CHECK(IPackDeviceClass, (klass), TYPE_IPACK_DEVICE)
+#define IPACK_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(IPackDeviceClass, (obj), TYPE_IPACK_DEVICE)
+
+struct IPackDeviceClass {
+    DeviceClass parent_class;
+
+    int (*init)(IPackDevice *dev);
+    int (*exit)(IPackDevice *dev);
+
+    uint16_t (*io_read)(IPackDevice *dev, uint8_t addr);
+    void (*io_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*id_read)(IPackDevice *dev, uint8_t addr);
+    void (*id_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*int_read)(IPackDevice *dev, uint8_t addr);
+    void (*int_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*mem_read16)(IPackDevice *dev, uint32_t addr);
+    void (*mem_write16)(IPackDevice *dev, uint32_t addr, uint16_t val);
+
+    uint8_t (*mem_read8)(IPackDevice *dev, uint32_t addr);
+    void (*mem_write8)(IPackDevice *dev, uint32_t addr, uint8_t val);
+};
+
+struct IPackDevice {
+    DeviceState qdev;
+    int32_t slot;
+    /* IRQ objects for the IndustryPack INT0# and INT1# */
+    qemu_irq *irq;
+};
+
+extern const VMStateDescription vmstate_ipack_device;
+
+#define VMSTATE_IPACK_DEVICE(_field, _state)                            \
+    VMSTATE_STRUCT(_field, _state, 1, vmstate_ipack_device, IPackDevice)
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot);
+
+#endif
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 301bf1c..ef511f6 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -128,3 +128,6 @@
 
 #define PCI_VENDOR_ID_NEC                0x1033
 #define PCI_DEVICE_ID_NEC_UPD720200      0x0194
+
+#define PCI_VENDOR_ID_TEWS               0x1498
+#define PCI_DEVICE_ID_TEWS_TPCI200       0x30C8
diff --git a/hw/tpci200.c b/hw/tpci200.c
new file mode 100644
index 0000000..8fa6b76
--- /dev/null
+++ b/hw/tpci200.c
@@ -0,0 +1,630 @@
+/*
+ * QEMU TEWS TPCI200 IndustryPack carrier emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "pci.h"
+#include "ipack.h"
+#include <stdio.h>
+
+/* #define DEBUG_TPCI */
+
+#ifdef DEBUG_TPCI
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define N_MODULES 4
+
+#define IP_ID_SPACE  2
+#define IP_INT_SPACE 3
+#define IP_IO_SPACE_ADDR_MASK  0x7F
+#define IP_ID_SPACE_ADDR_MASK  0x3F
+#define IP_INT_SPACE_ADDR_MASK 0x3F
+
+#define STATUS_INT(IP, INTNO) (1 << ((IP) * 2 + (INTNO)))
+#define STATUS_TIMEOUT(IP)    (1 << ((IP) + 12))
+#define STATUS_ERR_ANY        0xF00
+
+#define CTRL_CLKRATE          (1 << 0)
+#define CTRL_RECOVER          (1 << 1)
+#define CTRL_TIME_INT         (1 << 2)
+#define CTRL_ERR_INT          (1 << 3)
+#define CTRL_INT_EDGE(INTNO)  (1 << (4 + (INTNO)))
+#define CTRL_INT(INTNO)       (1 << (6 + (INTNO)))
+
+typedef struct {
+    PCIDevice dev;
+    IPackBus bus;
+    MemoryRegion mmio;
+    MemoryRegion io;
+    MemoryRegion las0;
+    MemoryRegion las1;
+    MemoryRegion las2;
+    MemoryRegion las3;
+    bool big_endian[3];
+    uint8_t ctrl[N_MODULES];
+    uint16_t status;
+} TPCI200State;
+
+static const uint8_t local_config_regs[] = {
+    0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00,
+    0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4,
+    0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01,
+    0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02,
+    0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41,
+    0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02
+};
+
+static void adjust_addr(bool big_endian, target_phys_addr_t *addr,
+                        unsigned size)
+{
+    /* During 8 bit access in big endian mode,
+       odd and even addresses are swapped */
+    if (big_endian && size == 1) {
+        *addr ^= 1;
+    }
+}
+
+static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size)
+{
+    /* Local spaces only support 8/16 bit access,
+     * so there's no need to care for sizes > 2 */
+    if (big_endian && size == 2) {
+        *val = bswap16(*val);
+    }
+    return *val;
+}
+
+static void tpci200_set_irq(void *opaque, int intno, int level)
+{
+    IPackDevice *ip = opaque;
+    IPackBus *bus = DO_UPCAST(IPackBus, qbus, ip->qdev.parent_bus);
+    PCIDevice *pcidev = DO_UPCAST(PCIDevice, qdev, bus->qbus.parent);
+    TPCI200State *dev = DO_UPCAST(TPCI200State, dev, pcidev);
+    unsigned ip_n = ip->slot;
+
+    assert(ip->slot >= 0 && ip->slot < N_MODULES);
+
+    /* The requested interrupt must be enabled in the IP CONTROL
+     * register */
+    if (dev->ctrl[ip_n] & CTRL_INT(intno)) {
+        DPRINTF("INT%u#: %u\n", intno, level);
+
+        /* Update the interrupt status in the IP STATUS register */
+        if (level) {
+            dev->status |= STATUS_INT(ip_n, intno);
+        } else {
+            dev->status &= ~STATUS_INT(ip_n, intno);
+        }
+
+        if ((dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) && level) {
+            qemu_irq_pulse(dev->dev.irq[0]);
+        } else {
+            qemu_set_irq(dev->dev.irq[0], level);
+        }
+    }
+}
+
+static uint64_t tpci200_read_cfg(void *opaque, target_phys_addr_t addr,
+                                 unsigned size)
+{
+    uint8_t ret = 0;
+    if (addr < ARRAY_SIZE(local_config_regs)) {
+        ret = local_config_regs[addr];
+    }
+    DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret);
+    return ret;
+}
+
+static void tpci200_write_cfg(void *opaque, target_phys_addr_t addr,
+                              uint64_t val, unsigned size)
+{
+    TPCI200State *s = opaque;
+    if (addr == 0x2b && val == 0xd5) {
+        DPRINTF("LAS0: big endian mode on\n");
+        s->big_endian[0] = true;
+    } else if (addr == 0x2f && val == 0x15) {
+        DPRINTF("LAS1: big endian mode on\n");
+        s->big_endian[1] = true;
+    } else if (addr == 0x33 && val == 0x15) {
+        DPRINTF("LAS2: big endian mode on\n");
+        s->big_endian[2] = true;
+    } else {
+        DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val);
+    }
+}
+
+static uint64_t tpci200_read_las0(void *opaque, target_phys_addr_t addr,
+                                  unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint64_t ret = 0;
+
+    switch (addr) {
+    /* Revision ID - current value is 0x00 */
+    case 0x00:
+        DPRINTF("Read REVISION ID\n");
+        break;
+
+    /* IP Control */
+    case 0x02:
+    case 0x04:
+    case 0x06:
+    case 0x08: {
+        unsigned ip_n = addr / 2 - 1;
+        ret = s->ctrl[ip_n];
+        DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret);
+    }
+        break;
+
+    /* Reset - not implemented */
+    case 0x0A:
+        DPRINTF("Read RESET\n");
+        break;
+
+    /* Status */
+    case 0x0C: {
+        ret = s->status;
+        DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret);
+    }
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr);
+        break;
+    }
+
+    return adjust_value(s->big_endian[0], &ret, size);
+}
+
+static void tpci200_write_las0(void *opaque, target_phys_addr_t addr,
+                               uint64_t val, unsigned size)
+{
+    TPCI200State *s = opaque;
+
+    adjust_value(s->big_endian[0], &val, size);
+
+    switch (addr) {
+    /* Revision ID - No effect */
+    case 0x00:
+        DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val);
+        break;
+
+    /* IP Control */
+    case 0x02:
+    case 0x04:
+    case 0x06:
+    case 0x08: {
+        unsigned ip_n = addr / 2 - 1;
+        s->ctrl[ip_n] = val;
+        DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val);
+    }
+        break;
+
+    /* Reset - not implemented */
+    case 0x0A:
+        DPRINTF("Write RESET: 0x%x\n", (unsigned) val);
+        break;
+
+    /* Status */
+    case 0x0C: {
+        unsigned i;
+
+        for (i = 0; i < N_MODULES; i++) {
+            IPackDevice *ip = ipack_device_find(&s->bus, i);
+
+            if (ip != NULL) {
+                if (val & STATUS_INT(i, 0)) {
+                    DPRINTF("Clear IP %c INT0# status\n", 'A' + i);
+                    qemu_irq_lower(ip->irq[0]);
+                }
+                if (val & STATUS_INT(i, 1)) {
+                    DPRINTF("Clear IP %c INT1# status\n", 'A' + i);
+                    qemu_irq_lower(ip->irq[1]);
+                }
+            }
+
+            if (val & STATUS_TIMEOUT(i)) {
+                DPRINTF("Clear IP %c timeout\n", 'A' + i);
+                s->status &= ~STATUS_TIMEOUT(i);
+            }
+        }
+
+        if (val & STATUS_ERR_ANY) {
+            DPRINTF("Unexpected write to STATUS register: 0x%x\n",
+                    (unsigned) val);
+        }
+    }
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
+                (unsigned) addr, (unsigned) val);
+        break;
+    }
+}
+
+static uint64_t tpci200_read_las1(void *opaque, target_phys_addr_t addr,
+                                  unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+
+    /*
+     * The address is divided into the IP module number (0-4), the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_read) {
+                ret = k->id_read(ip, offset);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+
+            /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
+            if (offset == 0 || offset == 2) {
+                unsigned intno = offset / 2;
+                bool int_set = s->status & STATUS_INT(ip_n, intno);
+                bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
+                if (int_set && !int_edge_sensitive) {
+                    qemu_irq_lower(ip->irq[intno]);
+                }
+            }
+
+            if (k->int_read) {
+                ret = k->int_read(ip, offset);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_read) {
+                ret = k->io_read(ip, offset);
+            }
+            break;
+        }
+    }
+
+    return adjust_value(s->big_endian[1], &ret, size);
+}
+
+static void tpci200_write_las1(void *opaque, target_phys_addr_t addr,
+                               uint64_t val, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+    adjust_value(s->big_endian[1], &val, size);
+
+    /*
+     * The address is divided into the IP module number, the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_write) {
+                k->id_write(ip, offset, val);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+            if (k->int_write) {
+                k->int_write(ip, offset, val);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_write) {
+                k->io_write(ip, offset, val);
+            }
+            break;
+        }
+    }
+}
+
+static uint64_t tpci200_read_las2(void *opaque, target_phys_addr_t addr,
+                                  unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read16) {
+            ret = k->mem_read16(ip, offset);
+        }
+    }
+
+    return adjust_value(s->big_endian[2], &ret, size);
+}
+
+static void tpci200_write_las2(void *opaque, target_phys_addr_t addr,
+                               uint64_t val, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+    adjust_value(s->big_endian[2], &val, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write16) {
+            k->mem_write16(ip, offset, val);
+        }
+    }
+}
+
+static uint64_t tpci200_read_las3(void *opaque, target_phys_addr_t addr,
+                                  unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read8) {
+            ret = k->mem_read8(ip, offset);
+        }
+    }
+
+    return ret;
+}
+
+static void tpci200_write_las3(void *opaque, target_phys_addr_t addr,
+                               uint64_t val, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write8) {
+            k->mem_write8(ip, offset, val);
+        }
+    }
+}
+
+static const MemoryRegionOps tpci200_cfg_ops = {
+    .read = tpci200_read_cfg,
+    .write = tpci200_write_cfg,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 4
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static const MemoryRegionOps tpci200_las0_ops = {
+    .read = tpci200_read_las0,
+    .write = tpci200_write_las0,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 2,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las1_ops = {
+    .read = tpci200_read_las1,
+    .write = tpci200_write_las1,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las2_ops = {
+    .read = tpci200_read_las2,
+    .write = tpci200_write_las2,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las3_ops = {
+    .read = tpci200_read_las3,
+    .write = tpci200_write_las3,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static int tpci200_initfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = DO_UPCAST(TPCI200State, dev, pci_dev);
+    uint8_t *c = s->dev.config;
+
+    pci_set_word(c + PCI_COMMAND, 0x0003);
+    pci_set_word(c + PCI_STATUS,  0x0280);
+
+    pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
+
+    pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
+    pci_set_long(c + 0x40, 0x48014801);
+    pci_set_long(c + 0x48, 0x00024C06);
+    pci_set_long(c + 0x4C, 0x00000003);
+
+    memory_region_init_io(&s->mmio, &tpci200_cfg_ops,
+                          s, "tpci200_mmio", 128);
+    memory_region_init_io(&s->io,   &tpci200_cfg_ops,
+                          s, "tpci200_io",   128);
+    memory_region_init_io(&s->las0, &tpci200_las0_ops,
+                          s, "tpci200_las0", 256);
+    memory_region_init_io(&s->las1, &tpci200_las1_ops,
+                          s, "tpci200_las1", 1024);
+    memory_region_init_io(&s->las2, &tpci200_las2_ops,
+                          s, "tpci200_las2", 1024*1024*32);
+    memory_region_init_io(&s->las3, &tpci200_las3_ops,
+                          s, "tpci200_las3", 1024*1024*16);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
+    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);
+    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
+    pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
+    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
+    pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
+
+    qbus_create_inplace(&s->bus.qbus, TYPE_IPACK_BUS, &s->dev.qdev, NULL);
+    s->bus.n_slots = N_MODULES;
+    s->bus.set_irq = tpci200_set_irq;
+
+    return 0;
+}
+
+static void tpci200_exitfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = DO_UPCAST(TPCI200State, dev, pci_dev);
+
+    memory_region_destroy(&s->mmio);
+    memory_region_destroy(&s->io);
+    memory_region_destroy(&s->las0);
+    memory_region_destroy(&s->las1);
+    memory_region_destroy(&s->las2);
+    memory_region_destroy(&s->las3);
+}
+
+static const VMStateDescription vmstate_tpci200 = {
+    .name = "tpci200",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, TPCI200State),
+        VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
+        VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES),
+        VMSTATE_UINT16(status, TPCI200State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void tpci200_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = tpci200_initfn;
+    k->exit = tpci200_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_TEWS;
+    k->device_id = PCI_DEVICE_ID_TEWS_TPCI200;
+    k->class_id = PCI_CLASS_BRIDGE_OTHER;
+    k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS;
+    k->subsystem_id = 0x300A;
+    dc->desc = "TEWS TPCI200 IndustryPack carrier";
+    dc->vmsd = &vmstate_tpci200;
+}
+
+static const TypeInfo tpci200_info = {
+    .name          = "tpci200",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(TPCI200State),
+    .class_init    = tpci200_class_init,
+};
+
+static void tpci200_register_types(void)
+{
+    type_register_static(&tpci200_info);
+}
+
+type_init(tpci200_register_types)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH 2/2] Add IP-Octal 232 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
  2012-08-23 13:59 ` [Qemu-devel] [PATCH 1/2] Add TEWS TPCI200 " Alberto Garcia
@ 2012-08-23 13:59 ` Alberto Garcia
  2012-08-31 14:12 ` [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and " Alberto Garcia
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-08-23 13:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Anthony Liguori, Alberto Garcia, Paul Brook

The IP-Octal 232 is an IndustryPack module that implements eight
RS-232 serial ports, each one of which can be redirected to a
character device in the host.

Signed-off-by: Alberto Garcia <agarcia@igalia.com>
---
 hw/Makefile.objs |    2 +-
 hw/ipoctal.c     |  662 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 ficheiros modificados, 663 adições(+), 1 eliminado(-)
 create mode 100644 hw/ipoctal.c

diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index f4a3a9b..dfb3042 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -100,7 +100,7 @@ hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
 
 # IndustryPack
-hw-obj-$(CONFIG_IPACK) += tpci200.o ipack.o
+hw-obj-$(CONFIG_IPACK) += tpci200.o ipoctal.o ipack.o
 
 # PCI network cards
 hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
diff --git a/hw/ipoctal.c b/hw/ipoctal.c
new file mode 100644
index 0000000..e655bf2
--- /dev/null
+++ b/hw/ipoctal.c
@@ -0,0 +1,662 @@
+/*
+ * QEMU IP-Octal 232 IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "ipack.h"
+#include "qemu-timer.h"
+#include <glib.h>
+
+/* #define DEBUG_IPOCTAL */
+
+#ifdef DEBUG_IPOCTAL
+#define DPRINTF2(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF2(fmt, ...) do { } while (0)
+#endif
+
+#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
+
+#define TIMER_INTERVAL (qemu_get_clock_ms(vm_clock) + 1)
+
+/* The IP-Octal has 8 channels (a-h)
+   divided into 4 blocks (A-D) */
+#define N_CHANNELS 8
+#define N_BLOCKS   4
+
+#define REG_MRa  0x01
+#define REG_MRb  0x11
+#define REG_SRa  0x03
+#define REG_SRb  0x13
+#define REG_CSRa 0x03
+#define REG_CSRb 0x13
+#define REG_CRa  0x05
+#define REG_CRb  0x15
+#define REG_RHRa 0x07
+#define REG_RHRb 0x17
+#define REG_THRa 0x07
+#define REG_THRb 0x17
+#define REG_ACR  0x09
+#define REG_ISR  0x0B
+#define REG_IMR  0x0B
+#define REG_OPCR 0x1B
+
+#define CR_ENABLE_RX    (1 << 0)
+#define CR_DISABLE_RX   (1 << 1)
+#define CR_ENABLE_TX    (1 << 2)
+#define CR_DISABLE_TX   (1 << 3)
+#define CR_CMD(cr)      ((cr) >> 4)
+#define CR_NO_OP        0
+#define CR_RESET_MR     1
+#define CR_RESET_RX     2
+#define CR_RESET_TX     3
+#define CR_RESET_ERR    4
+#define CR_RESET_BRKINT 5
+#define CR_START_BRK    6
+#define CR_STOP_BRK     7
+#define CR_ASSERT_RTSN  8
+#define CR_NEGATE_RTSN  9
+#define CR_TIMEOUT_ON   10
+#define CR_TIMEOUT_OFF  12
+
+#define SR_RXRDY   (1 << 0)
+#define SR_FFULL   (1 << 1)
+#define SR_TXRDY   (1 << 2)
+#define SR_TXEMT   (1 << 3)
+#define SR_OVERRUN (1 << 4)
+#define SR_PARITY  (1 << 5)
+#define SR_FRAMING (1 << 6)
+#define SR_BREAK   (1 << 7)
+
+#define ISR_TXRDYA (1 << 0)
+#define ISR_RXRDYA (1 << 1)
+#define ISR_BREAKA (1 << 2)
+#define ISR_CNTRDY (1 << 3)
+#define ISR_TXRDYB (1 << 4)
+#define ISR_RXRDYB (1 << 5)
+#define ISR_BREAKB (1 << 6)
+#define ISR_MPICHG (1 << 7)
+#define ISR_TXRDY(CH) (1 << (((CH) & 1) ? 4 : 0))
+#define ISR_RXRDY(CH) (1 << (((CH) & 1) ? 5 : 1))
+#define ISR_BREAK(CH) (1 << (((CH) & 1) ? 6 : 2))
+
+typedef struct IPOctalState IPOctalState;
+typedef struct SCC2698Channel SCC2698Channel;
+typedef struct SCC2698Block SCC2698Block;
+
+struct SCC2698Channel {
+    IPOctalState *ipoctal;
+    CharDriverState *dev;
+    char *devpath;
+    bool tx_enabled;
+    bool rx_enabled;
+    bool tx_data;
+    bool rx_data;
+    uint8_t mr[2];
+    uint8_t mr_idx;
+    uint8_t sr;
+    uint8_t thr;
+    uint8_t rhr;
+};
+
+struct SCC2698Block {
+    uint8_t imr;
+    uint8_t isr;
+};
+
+struct IPOctalState {
+    IPackDevice dev;
+    SCC2698Channel ch[N_CHANNELS];
+    SCC2698Block blk[N_BLOCKS];
+    uint8_t irq_vector;
+    QEMUTimer *timer;
+};
+
+static const VMStateDescription vmstate_scc2698_channel = {
+    .name = "scc2698_channel",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(tx_enabled, SCC2698Channel),
+        VMSTATE_BOOL(rx_enabled, SCC2698Channel),
+        VMSTATE_BOOL(tx_data, SCC2698Channel),
+        VMSTATE_BOOL(rx_data, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
+        VMSTATE_UINT8(mr_idx, SCC2698Channel),
+        VMSTATE_UINT8(sr, SCC2698Channel),
+        VMSTATE_UINT8(thr, SCC2698Channel),
+        VMSTATE_UINT8(rhr, SCC2698Channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_scc2698_block = {
+    .name = "scc2698_block",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(imr, SCC2698Block),
+        VMSTATE_UINT8(isr, SCC2698Block),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_ipoctal = {
+    .name = "ipoctal",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_IPACK_DEVICE(dev, IPOctalState),
+        VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
+                             vmstate_scc2698_channel, SCC2698Channel),
+        VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
+                             vmstate_scc2698_block, SCC2698Block),
+        VMSTATE_UINT8(irq_vector, IPOctalState),
+        VMSTATE_TIMER(timer, IPOctalState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* data[10] is 0x0C, not 0x0B as the doc says */
+static const uint8_t id_prom_data[] = {
+    0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
+    0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
+};
+
+static void ipoctal_set_irq(IPOctalState *dev)
+{
+    qemu_mod_timer(dev->timer, TIMER_INTERVAL);
+}
+
+static bool write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
+{
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[channel / 2];
+    bool set_irq = false;
+
+    DPRINTF("Write CR%c %u: ", channel + 'a', val);
+
+    /* The lower 4 bits are used to enable and disable Tx and Rx */
+    if (val & CR_ENABLE_RX) {
+        DPRINTF2("Rx on, ");
+        ch->rx_enabled = true;
+    }
+    if (val & CR_DISABLE_RX) {
+        DPRINTF2("Rx off, ");
+        ch->rx_enabled = false;
+        ch->rx_data = false;
+    }
+    if (val & CR_ENABLE_TX) {
+        DPRINTF2("Tx on, ");
+        ch->tx_enabled = true;
+        /* Set the TxRDY bit unless there's data being transmitted */
+        if (!ch->tx_data) {
+            ch->sr |= SR_TXRDY | SR_TXEMT;
+            set_irq = true;
+        }
+    }
+    if (val & CR_DISABLE_TX) {
+        DPRINTF2("Tx off, ");
+        ch->tx_enabled = false;
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+    }
+
+    DPRINTF2("cmd: ");
+
+    /* The rest of the bits implement different commands */
+    switch (CR_CMD(val)) {
+    case CR_NO_OP:
+        DPRINTF2("none");
+        break;
+    case CR_RESET_MR:
+        DPRINTF2("reset MR");
+        ch->mr_idx = 0;
+        break;
+    case CR_RESET_RX:
+        DPRINTF2("reset Rx");
+        ch->rx_enabled = false;
+        ch->rx_data = false;
+        break;
+    case CR_RESET_TX:
+        DPRINTF2("reset Tx");
+        ch->tx_enabled = false;
+        ch->tx_data = false;
+        break;
+    case CR_RESET_ERR:
+        DPRINTF2("reset err");
+        ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
+        break;
+    case CR_RESET_BRKINT:
+        DPRINTF2("reset brk ch int");
+        blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
+        break;
+    default:
+        DPRINTF2("unsupported 0x%x", CR_CMD(val));
+    }
+
+    DPRINTF2("\n");
+
+    return set_irq;
+}
+
+static uint16_t io_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    uint16_t ret = 0;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    bool set_irq = false;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ret = ch->mr[ch->mr_idx];
+        DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
+        ch->mr_idx = 1;
+        break;
+
+    case REG_SRa:
+    case REG_SRb:
+        ret = ch->sr;
+        DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
+        break;
+
+    case REG_RHRa:
+    case REG_RHRb:
+        ret = ch->rhr;
+        ch->rx_data = false;
+        ch->sr &= ~SR_RXRDY;
+        if (ch->sr & SR_BREAK) {
+            ch->sr &= ~SR_BREAK;
+            blk->isr |= ISR_BREAK(channel);
+            set_irq = true;
+        }
+        DPRINTF("Read RHR%c '%c'\n", channel + 'a', ret);
+        break;
+
+    case REG_ISR: {
+        /* Duplicate TxRDY/RxRDY from SR in ISR */
+        SCC2698Channel *cha = &dev->ch[block * 2];
+        SCC2698Channel *chb = &dev->ch[block * 2 + 1];
+
+        if (cha->sr & SR_TXRDY) {
+            blk->isr |= ISR_TXRDYA;
+        } else {
+            blk->isr &= ~ISR_TXRDYA;
+        }
+
+        if (cha->sr & SR_RXRDY) {
+            blk->isr |= ISR_RXRDYA;
+        } else {
+            blk->isr &= ~ISR_RXRDYA;
+        }
+
+        if (chb->sr & SR_TXRDY) {
+            blk->isr |= ISR_TXRDYB;
+        } else {
+            blk->isr &= ~ISR_TXRDYB;
+        }
+
+        if (chb->sr & SR_RXRDY) {
+            blk->isr |= ISR_RXRDYB;
+        } else {
+            blk->isr &= ~ISR_RXRDYB;
+        }
+
+        ret = blk->isr;
+        DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
+    }
+        break;
+
+    default:
+        DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
+    }
+
+    if (set_irq) {
+        ipoctal_set_irq(dev);
+    }
+
+    return ret;
+}
+
+static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    unsigned reg = val & 0xFF;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    bool set_irq = false;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ch->mr[ch->mr_idx] = reg;
+        DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
+        ch->mr_idx = 1;
+        break;
+
+    /* Not implemented */
+    case REG_CSRa:
+    case REG_CSRb:
+        DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
+        break;
+
+    case REG_CRa:
+    case REG_CRb:
+        set_irq = write_cr(dev, channel, reg);
+        break;
+
+    case REG_THRa:
+    case REG_THRb:
+        if (ch->tx_enabled) {
+            DPRINTF("Write THR%c '%c'\n", channel + 'a', reg);
+            ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+            ch->thr = reg;
+            ch->tx_data = true;
+            set_irq = true;
+        } else {
+            DPRINTF("Write THR%c '%c', but Tx disabled\n", channel + 'a', reg);
+        }
+        break;
+
+    /* Not implemented */
+    case REG_ACR:
+        DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
+        break;
+
+    case REG_IMR:
+        DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
+        blk->imr = reg;
+        break;
+
+    /* Not implemented */
+    case REG_OPCR:
+        DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
+        break;
+
+    default:
+        DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
+    }
+
+    if (set_irq) {
+        ipoctal_set_irq(dev);
+    }
+}
+
+static uint16_t id_read(IPackDevice *ip, uint8_t addr)
+{
+    uint16_t ret = 0;
+    unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
+
+    if (pos < ARRAY_SIZE(id_prom_data)) {
+        ret = id_prom_data[pos];
+    } else {
+        DPRINTF("Attempt to read unavailable PROM data at 0x%x\n",  addr);
+    }
+
+    return ret;
+}
+
+static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val; /* Undocumented, but the hw works like that */
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static uint16_t int_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr != 0 && addr != 2) {
+        DPRINTF("Attempt to read from 0x%x\n", addr);
+        return 0;
+    } else {
+        return dev->irq_vector;
+    }
+}
+
+static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val;
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static void ipoctal_timer(void *opaque)
+{
+    IPOctalState *dev = opaque;
+    bool intno_set[2] = { false, false };
+    unsigned i;
+
+    /* Check the status of each channel to see what interrupts needs
+     * to be raised. */
+    for (i = 0; i < N_CHANNELS; i++) {
+        unsigned block = i / 2;
+        unsigned intno = block / 2;
+        SCC2698Channel *ch = &dev->ch[i];
+        SCC2698Block *blk = &dev->blk[block];
+        if (ch->tx_data) {
+
+            /* Transmit data */
+            if (ch->dev) {
+                qemu_chr_fe_write(ch->dev, &ch->thr, 1);
+            }
+            DPRINTF("Channel %u: transmitted '%c'\n", i, ch->thr);
+
+            /* After transmitting, set TxRDY again if Tx is enabled. */
+            ch->tx_data = false;
+            if (ch->tx_enabled) {
+                ch->sr |= SR_TXRDY | SR_TXEMT;
+            }
+        }
+        if (((ch->sr & SR_TXRDY) && (blk->imr & ISR_TXRDY(i))) ||
+            ((ch->sr & SR_RXRDY) && (blk->imr & ISR_RXRDY(i))) ||
+            ((blk->isr & ISR_BREAK(i)) && (blk->imr & ISR_BREAK(i)))) {
+            intno_set[intno] = true;
+        }
+    }
+
+    if (intno_set[0]) {
+        qemu_irq_raise(dev->dev.irq[0]);
+    }
+    if (intno_set[1]) {
+        qemu_irq_raise(dev->dev.irq[1]);
+    }
+}
+
+static int hostdev_can_receive(void *opaque)
+{
+    SCC2698Channel *ch = opaque;
+    return ch->rx_enabled && !ch->rx_data;
+}
+
+static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
+{
+    SCC2698Channel *ch = opaque;
+    ch->rhr = buf[0];
+    ch->rx_data = true;
+    ch->sr |= SR_RXRDY;
+    ipoctal_set_irq(ch->ipoctal);
+}
+
+static void hostdev_event(void *opaque, int event)
+{
+    SCC2698Channel *ch = opaque;
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        DPRINTF("Device %s opened\n", ch->dev->label);
+        break;
+    case CHR_EVENT_BREAK: {
+        uint8_t zero = 0;
+        DPRINTF("Device %s received break\n", ch->dev->label);
+
+        if (!(ch->sr & SR_BREAK)) {
+            IPOctalState *dev = ch->ipoctal;
+            unsigned block, channel = 0;
+
+            while (&dev->ch[channel] != ch) {
+                channel++;
+            }
+            block = channel / 2;
+
+            ch->sr |= SR_BREAK;
+            dev->blk[block].isr |= ISR_BREAK(channel);
+        }
+
+        /* Put a zero character in the buffer */
+        hostdev_receive(ch, &zero, 1);
+    }
+        break;
+    default:
+        DPRINTF("Device %s received event %d\n", ch->dev->label, event);
+    }
+}
+
+static int ipoctal_init(IPackDevice *ip)
+{
+    IPOctalState *s = DO_UPCAST(IPOctalState, dev, ip);
+    unsigned i;
+
+    s->timer = qemu_new_timer_ms(vm_clock, ipoctal_timer, s);
+
+    for (i = 0; i < N_CHANNELS; i++) {
+        SCC2698Channel *ch = &s->ch[i];
+        ch->ipoctal = s;
+
+        /* Redirect IP-Octal channels to host character devices */
+        if (ch->devpath) {
+            const char chr_name[] = "ipoctal";
+            char label[ARRAY_SIZE(chr_name) + 2];
+            static int index;
+
+            snprintf(label, sizeof(label), "%s%d", chr_name, index);
+
+            ch->dev = qemu_chr_new(label, ch->devpath, NULL);
+
+            if (ch->dev) {
+                index++;
+                qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
+                                      hostdev_receive, hostdev_event, ch);
+                DPRINTF("Redirecting channel %u to %s (%s)\n",
+                        i, ch->devpath, label);
+            } else {
+                DPRINTF("Could not redirect channel %u to %s\n",
+                        i, ch->devpath);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int ipoctal_exit(IPackDevice *ip)
+{
+    IPOctalState *s = DO_UPCAST(IPOctalState, dev, ip);
+    qemu_del_timer(s->timer);
+    qemu_free_timer(s->timer);
+    return 0;
+}
+
+static Property ipoctal_properties[] = {
+    DEFINE_PROP_STRING("serial0", IPOctalState, ch[0].devpath),
+    DEFINE_PROP_STRING("serial1", IPOctalState, ch[1].devpath),
+    DEFINE_PROP_STRING("serial2", IPOctalState, ch[2].devpath),
+    DEFINE_PROP_STRING("serial3", IPOctalState, ch[3].devpath),
+    DEFINE_PROP_STRING("serial4", IPOctalState, ch[4].devpath),
+    DEFINE_PROP_STRING("serial5", IPOctalState, ch[5].devpath),
+    DEFINE_PROP_STRING("serial6", IPOctalState, ch[6].devpath),
+    DEFINE_PROP_STRING("serial7", IPOctalState, ch[7].devpath),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipoctal_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
+
+    ic->init        = ipoctal_init;
+    ic->exit        = ipoctal_exit;
+    ic->io_read     = io_read;
+    ic->io_write    = io_write;
+    ic->id_read     = id_read;
+    ic->id_write    = id_write;
+    ic->int_read    = int_read;
+    ic->int_write   = int_write;
+    ic->mem_read16  = mem_read16;
+    ic->mem_write16 = mem_write16;
+    ic->mem_read8   = mem_read8;
+    ic->mem_write8  = mem_write8;
+
+    dc->desc    = "IP-Octal 232 8-channel RS-232 IndustryPack";
+    dc->props   = ipoctal_properties;
+    dc->vmsd    = &vmstate_ipoctal;
+}
+
+static const TypeInfo ipoctal_info = {
+    .name          = "ipoctal",
+    .parent        = TYPE_IPACK_DEVICE,
+    .instance_size = sizeof(IPOctalState),
+    .class_init    = ipoctal_class_init,
+};
+
+static void ipoctal_register_types(void)
+{
+    type_register_static(&ipoctal_info);
+}
+
+type_init(ipoctal_register_types)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
  2012-08-23 13:59 ` [Qemu-devel] [PATCH 1/2] Add TEWS TPCI200 " Alberto Garcia
  2012-08-23 13:59 ` [Qemu-devel] [PATCH 2/2] Add IP-Octal 232 " Alberto Garcia
@ 2012-08-31 14:12 ` Alberto Garcia
  2012-08-31 16:09   ` Andreas Färber
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 " Alberto Garcia
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 27+ messages in thread
From: Alberto Garcia @ 2012-08-31 14:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: Anthony Liguori, Alberto Garcia, Paul Brook

Ping

Patches here:

http://patchwork.ozlabs.org/patch/179657/
http://patchwork.ozlabs.org/patch/179658/

Description:

https://lists.gnu.org/archive/html/qemu-devel/2012-08/msg04173.html

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-08-31 14:12 ` [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and " Alberto Garcia
@ 2012-08-31 16:09   ` Andreas Färber
  2012-09-10 13:10     ` Alberto Garcia
  2012-10-05 13:20     ` Alberto Garcia
  0 siblings, 2 replies; 27+ messages in thread
From: Andreas Färber @ 2012-08-31 16:09 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: Anthony Liguori, qemu-devel, Paul Brook

Hi,

Am 31.08.2012 16:12, schrieb Alberto Garcia:
> Ping

We are currently in Hard Freeze, new devices will not get accepted
before the release, and our review may focus on bug fixes.

http://wiki.qemu.org/Planning/1.2

I remember having had a brief look through your patches and they looked
pretty good modeling- and style-wise for an initial submission. Don't
know the hardware though and haven't tested the patches so far.

Regards,
Andreas

-- 
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-08-31 16:09   ` Andreas Färber
@ 2012-09-10 13:10     ` Alberto Garcia
  2012-10-05 13:20     ` Alberto Garcia
  1 sibling, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-09-10 13:10 UTC (permalink / raw)
  To: Andreas Färber; +Cc: Anthony Liguori, qemu-devel, Paul Brook

On Fri, Aug 31, 2012 at 06:09:11PM +0200, Andreas Färber wrote:

> We are currently in Hard Freeze, new devices will not get accepted
> before the release, and our review may focus on bug fixes.

QEMU 1.2 has been released, so here's the new ping.

Patches here:

http://patchwork.ozlabs.org/patch/179657/
http://patchwork.ozlabs.org/patch/179658/

Description:

https://lists.gnu.org/archive/html/qemu-devel/2012-08/msg04173.html

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-08-31 16:09   ` Andreas Färber
  2012-09-10 13:10     ` Alberto Garcia
@ 2012-10-05 13:20     ` Alberto Garcia
  2012-10-05 14:28       ` Paolo Bonzini
  2012-10-05 14:54       ` Andreas Färber
  1 sibling, 2 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-10-05 13:20 UTC (permalink / raw)
  To: Andreas Färber; +Cc: Anthony Liguori, Paul Brook, qemu-devel

On Fri, Aug 31, 2012 at 06:09:11PM +0200, Andreas Färber wrote:

> > Ping
> 
> We are currently in Hard Freeze, new devices will not get accepted
> before the release, and our review may focus on bug fixes.
> 
> http://wiki.qemu.org/Planning/1.2
> 
> I remember having had a brief look through your patches and
> they looked pretty good modeling- and style-wise for an initial
> submission. Don't know the hardware though and haven't tested the
> patches so far.

I wrote a small blog post about the work, including a bit more
information on how to test it:

http://blogs.igalia.com/berto/2012/10/03/industrypack-qemu-and-linuxcon/

The patches are still available here:

http://patchwork.ozlabs.org/patch/179657/
http://patchwork.ozlabs.org/patch/179658/

Is there anything else I should do in order to have this reviewed, or
should I contact someone else?

Thanks!

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-05 13:20     ` Alberto Garcia
@ 2012-10-05 14:28       ` Paolo Bonzini
  2012-10-05 14:52         ` Anthony Liguori
  2012-10-05 16:24         ` Blue Swirl
  2012-10-05 14:54       ` Andreas Färber
  1 sibling, 2 replies; 27+ messages in thread
From: Paolo Bonzini @ 2012-10-05 14:28 UTC (permalink / raw)
  To: Alberto Garcia, Blue Swirl
  Cc: qemu-devel, Anthony Liguori, Andreas Färber, Paul Brook

Il 05/10/2012 15:20, Alberto Garcia ha scritto:
> On Fri, Aug 31, 2012 at 06:09:11PM +0200, Andreas Färber wrote:
> 
>>> Ping
>>
>> We are currently in Hard Freeze, new devices will not get accepted
>> before the release, and our review may focus on bug fixes.
>>
>> http://wiki.qemu.org/Planning/1.2
>>
>> I remember having had a brief look through your patches and
>> they looked pretty good modeling- and style-wise for an initial
>> submission. Don't know the hardware though and haven't tested the
>> patches so far.
> 
> I wrote a small blog post about the work, including a bit more
> information on how to test it:
> 
> http://blogs.igalia.com/berto/2012/10/03/industrypack-qemu-and-linuxcon/
> 
> The patches are still available here:
> 
> http://patchwork.ozlabs.org/patch/179657/
> http://patchwork.ozlabs.org/patch/179658/
> 
> Is there anything else I should do in order to have this reviewed, or
> should I contact someone else?

I think the patches are pretty good, thanks for the contribution!  I'm
adding another committer to the recipients.  Blue, can you take a look?

Paolo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-05 14:28       ` Paolo Bonzini
@ 2012-10-05 14:52         ` Anthony Liguori
  2012-10-05 16:24         ` Blue Swirl
  1 sibling, 0 replies; 27+ messages in thread
From: Anthony Liguori @ 2012-10-05 14:52 UTC (permalink / raw)
  To: Paolo Bonzini, Alberto Garcia, Blue Swirl
  Cc: qemu-devel, Andreas Färber, Paul Brook

Paolo Bonzini <pbonzini@redhat.com> writes:

> Il 05/10/2012 15:20, Alberto Garcia ha scritto:
>> On Fri, Aug 31, 2012 at 06:09:11PM +0200, Andreas Färber wrote:
>> 
>>>> Ping
>>>
>>> We are currently in Hard Freeze, new devices will not get accepted
>>> before the release, and our review may focus on bug fixes.
>>>
>>> http://wiki.qemu.org/Planning/1.2
>>>
>>> I remember having had a brief look through your patches and
>>> they looked pretty good modeling- and style-wise for an initial
>>> submission. Don't know the hardware though and haven't tested the
>>> patches so far.
>> 
>> I wrote a small blog post about the work, including a bit more
>> information on how to test it:
>> 
>> http://blogs.igalia.com/berto/2012/10/03/industrypack-qemu-and-linuxcon/
>> 
>> The patches are still available here:
>> 
>> http://patchwork.ozlabs.org/patch/179657/
>> http://patchwork.ozlabs.org/patch/179658/
>> 
>> Is there anything else I should do in order to have this reviewed, or
>> should I contact someone else?
>
> I think the patches are pretty good, thanks for the contribution!  I'm
> adding another committer to the recipients.  Blue, can you take a
> look?

I've got them in my testing queue right now.

Regards,

Anthony Liguori

>
> Paolo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-05 13:20     ` Alberto Garcia
  2012-10-05 14:28       ` Paolo Bonzini
@ 2012-10-05 14:54       ` Andreas Färber
  1 sibling, 0 replies; 27+ messages in thread
From: Andreas Färber @ 2012-10-05 14:54 UTC (permalink / raw)
  To: Alberto Garcia
  Cc: Peter Maydell, Anthony Liguori, qemu-devel, Blue Swirl,
	Paul Brook, Paolo Bonzini

Am 05.10.2012 15:20, schrieb Alberto Garcia:
> On Fri, Aug 31, 2012 at 06:09:11PM +0200, Andreas Färber wrote:
> 
>>> Ping
>>
>> We are currently in Hard Freeze, new devices will not get accepted
>> before the release, and our review may focus on bug fixes.
>>
>> http://wiki.qemu.org/Planning/1.2
>>
>> I remember having had a brief look through your patches and
>> they looked pretty good modeling- and style-wise for an initial
>> submission. Don't know the hardware though and haven't tested the
>> patches so far.
> 
> I wrote a small blog post about the work, including a bit more
> information on how to test it:
> 
> http://blogs.igalia.com/berto/2012/10/03/industrypack-qemu-and-linuxcon/

Thanks for the illustrative blog post.

One thing caught my eye: Wouldn't it make more sense to call the device
"ipoctal232"? Octal doesn't indicate of what there are eight. ;)

> Is there anything else I should do in order to have this reviewed, or
> should I contact someone else?

Paul and Peter are the ones with experience reviewing UARTs, I think.

Andreas

-- 
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-05 14:28       ` Paolo Bonzini
  2012-10-05 14:52         ` Anthony Liguori
@ 2012-10-05 16:24         ` Blue Swirl
  2012-10-06 11:29           ` Alberto Garcia
  2012-10-07 10:13           ` Avi Kivity
  1 sibling, 2 replies; 27+ messages in thread
From: Blue Swirl @ 2012-10-05 16:24 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: Anthony Liguori, Alberto Garcia, qemu-devel, Avi Kivity,
	Andreas Färber, Paul Brook

On Fri, Oct 5, 2012 at 2:28 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> Il 05/10/2012 15:20, Alberto Garcia ha scritto:
>> On Fri, Aug 31, 2012 at 06:09:11PM +0200, Andreas Färber wrote:
>>
>>>> Ping
>>>
>>> We are currently in Hard Freeze, new devices will not get accepted
>>> before the release, and our review may focus on bug fixes.
>>>
>>> http://wiki.qemu.org/Planning/1.2
>>>
>>> I remember having had a brief look through your patches and
>>> they looked pretty good modeling- and style-wise for an initial
>>> submission. Don't know the hardware though and haven't tested the
>>> patches so far.
>>
>> I wrote a small blog post about the work, including a bit more
>> information on how to test it:
>>
>> http://blogs.igalia.com/berto/2012/10/03/industrypack-qemu-and-linuxcon/
>>
>> The patches are still available here:
>>
>> http://patchwork.ozlabs.org/patch/179657/
>> http://patchwork.ozlabs.org/patch/179658/
>>
>> Is there anything else I should do in order to have this reviewed, or
>> should I contact someone else?
>
> I think the patches are pretty good, thanks for the contribution!  I'm
> adding another committer to the recipients.  Blue, can you take a look?

Endianness looks buggy, there's no way to turn off big endian mode and
DEVICE_NATIVE_ENDIAN should probably be DEVICE_LITTLE_ENDIAN.

Indentation of braces in switch cases looks strange.

Pointers to HW documentation and description how to test (or qtests)
would be nice.

I'd suppose addressing devices in the bus could be implemented more
efficiently with better use of memory API, now some of it is
reimplemented. Maybe Avi can propose something?

>
> Paolo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-05 16:24         ` Blue Swirl
@ 2012-10-06 11:29           ` Alberto Garcia
  2012-10-07 10:13           ` Avi Kivity
  1 sibling, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-10-06 11:29 UTC (permalink / raw)
  To: Blue Swirl
  Cc: Anthony Liguori, qemu-devel, Avi Kivity, Paolo Bonzini,
	Andreas Färber, Paul Brook

On Fri, Oct 05, 2012 at 04:24:08PM +0000, Blue Swirl wrote:

> Endianness looks buggy, there's no way to turn off big endian mode

I double checked the documentation and I think you're right, I can
update the patch. Thanks for pointing it out.

> DEVICE_NATIVE_ENDIAN should probably be DEVICE_LITTLE_ENDIAN.

Where? In BAR0?

> Indentation of braces in switch cases looks strange.

I tried to follow the CODING_STYLE file, but I might have got it
wrong, I can change that.

> Pointers to HW documentation and description how to test (or qtests)
> would be nice.

The documentation is available online:

http://www.tews.com/Products/ArticleGroup/TPCI/TPCI200.html
http://www.mvme.com/manuals/IP-OCTAL-232_UM.pdf
http://www.alldatasheet.com/datasheet-pdf/pdf/19123/PHILIPS/SCC2698B.html

I'm not familiar with qtests, should I take a look at it?

In my blog post (see previous e-mail) there's a brief explanation on
how to test it. I can provide with a compiled Linux kernel with the
necessary drivers for the guest system if it helps.

A quick way to test it is to redirect one of the IP-Octal serial ports
to a pty in the host and open minicom in both sides.

> I'd suppose addressing devices in the bus could be implemented
> more efficiently with better use of memory API, now some of it is
> reimplemented. Maybe Avi can propose something?

I'm open to suggestions.

And answering Andreas, yes, the device could be renamed as
'ipoctal232' :)

Thanks,

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-05 16:24         ` Blue Swirl
  2012-10-06 11:29           ` Alberto Garcia
@ 2012-10-07 10:13           ` Avi Kivity
  2012-10-07 10:19             ` Avi Kivity
  2012-10-10 10:24             ` Alberto Garcia
  1 sibling, 2 replies; 27+ messages in thread
From: Avi Kivity @ 2012-10-07 10:13 UTC (permalink / raw)
  To: Blue Swirl
  Cc: Anthony Liguori, Alberto Garcia, qemu-devel, Paul Brook,
	Paolo Bonzini, Andreas Färber

On 10/05/2012 06:24 PM, Blue Swirl wrote:
> 
> I'd suppose addressing devices in the bus could be implemented more
> efficiently with better use of memory API, now some of it is
> reimplemented. Maybe Avi can propose something?

Luckily the low-order bits are used for offsets, and the high-order bits
are used for selecting the sub-device.

So you could easily have

 struct IPackDevice {
     DeviceState qdev;
     int32_t slot;
     /* IRQ objects for the IndustryPack INT0# and INT1# */
     qemu_irq *irq;
     MemoryRegion io_space;
     MemoryRegion id_space;
     MemoryRegion int_space;
     MemoryRegion mem8_space;  /* for las3 */
     MemoryRegion mem16_space; /* for las2 */
 };

The PCI device would then just map each space (with
memory_region_add_subregion()) into las1/las2/las3 such that the high
bits select the device/space.  The low bits would automatically become
the offset into the space.



-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-07 10:13           ` Avi Kivity
@ 2012-10-07 10:19             ` Avi Kivity
  2012-10-08  8:02               ` Alberto Garcia
  2012-10-10 10:24             ` Alberto Garcia
  1 sibling, 1 reply; 27+ messages in thread
From: Avi Kivity @ 2012-10-07 10:19 UTC (permalink / raw)
  To: Blue Swirl
  Cc: Anthony Liguori, Alberto Garcia, qemu-devel, Paul Brook,
	Paolo Bonzini, Andreas Färber

On 10/07/2012 12:13 PM, Avi Kivity wrote:
> On 10/05/2012 06:24 PM, Blue Swirl wrote:
>> 
>> I'd suppose addressing devices in the bus could be implemented more
>> efficiently with better use of memory API, now some of it is
>> reimplemented. Maybe Avi can propose something?
> 
> Luckily the low-order bits are used for offsets, and the high-order bits
> are used for selecting the sub-device.
> 
> So you could easily have
> 
>  struct IPackDevice {
>      DeviceState qdev;
>      int32_t slot;
>      /* IRQ objects for the IndustryPack INT0# and INT1# */
>      qemu_irq *irq;
>      MemoryRegion io_space;
>      MemoryRegion id_space;
>      MemoryRegion int_space;
>      MemoryRegion mem8_space;  /* for las3 */
>      MemoryRegion mem16_space; /* for las2 */
>  };
> 
> The PCI device would then just map each space (with
> memory_region_add_subregion()) into las1/las2/las3 such that the high
> bits select the device/space.  The low bits would automatically become
> the offset into the space.

Note: you can easily verify that the mapping is correct with 'info
mtree'.  You should see something like:

pci:
...

   xxxxxxxx-xxxxxxxx las1
     xxxxxxxx-xxxxxxxxx ip0-io
     xxxxxxxx-xxxxxxxxx ip0-id
     xxxxxxxx-xxxxxxxxx ip0-int
     xxxxxxxx-xxxxxxxxx ip1-io
     xxxxxxxx-xxxxxxxxx ip1-id
     xxxxxxxx-xxxxxxxxx ip1-int
   xxxxxxxx-xxxxxxxx las2
     xxxxxxxx-xxxxxxxx ip0-mem16
     xxxxxxxx-xxxxxxxx ip1-mem16

with the addresses relative to the pci address space.

-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-07 10:19             ` Avi Kivity
@ 2012-10-08  8:02               ` Alberto Garcia
  0 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-10-08  8:02 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony Liguori, qemu-devel, Blue Swirl, Paul Brook,
	Paolo Bonzini, Andreas Färber

On Sun, Oct 07, 2012 at 12:19:07PM +0200, Avi Kivity wrote:

> > So you could easily have
> > 
> >  struct IPackDevice {
> >      DeviceState qdev;
> >      int32_t slot;
> >      /* IRQ objects for the IndustryPack INT0# and INT1# */
> >      qemu_irq *irq;
> >      MemoryRegion io_space;
> >      MemoryRegion id_space;
> >      MemoryRegion int_space;
> >      MemoryRegion mem8_space;  /* for las3 */
> >      MemoryRegion mem16_space; /* for las2 */
> >  };
> > 
> Note: you can easily verify that the mapping is correct with 'info
> mtree'.  You should see something like:

I got the idea, it sounds like a better solution, I'll try to rework
the patch with all the proposed changes and post it again, thanks!

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-07 10:13           ` Avi Kivity
  2012-10-07 10:19             ` Avi Kivity
@ 2012-10-10 10:24             ` Alberto Garcia
  2012-10-10 11:35               ` Avi Kivity
  1 sibling, 1 reply; 27+ messages in thread
From: Alberto Garcia @ 2012-10-10 10:24 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony Liguori, qemu-devel, Blue Swirl, Paul Brook,
	Paolo Bonzini, Andreas Färber

On Sun, Oct 07, 2012 at 12:13:53PM +0200, Avi Kivity wrote:

> Luckily the low-order bits are used for offsets, and the high-order
> bits are used for selecting the sub-device.
> 
> So you could easily have
> 
>  struct IPackDevice {
>      DeviceState qdev;
>      int32_t slot;
>      /* IRQ objects for the IndustryPack INT0# and INT1# */
>      qemu_irq *irq;
>      MemoryRegion io_space;
>      MemoryRegion id_space;
>      MemoryRegion int_space;
>      MemoryRegion mem8_space;  /* for las3 */
>      MemoryRegion mem16_space; /* for las2 */
>  };
> 
> The PCI device would then just map each space (with
> memory_region_add_subregion()) into las1/las2/las3 such that the
> high bits select the device/space.  The low bits would automatically
> become the offset into the space.

Hey, I finally found some time to look into this, the problem that I
see is that the PCI carrier doesn't just map each space into its local
address spaces, in addition to that:

  1) it changes the data and addresses according to the endianness
     configuration in the local configuration registers (PCI BAR0).
     See adjust_addr()/adjust_value() and page 20 of the user manual.

     http://www.tews.com/Products/ArticleGroup/TPCI/TPCI200.html

  2) read accesses to the local address space 1 are also used to
     acknowledge interrupts (manual page 33 and tpci200_read_las1()).



On a related note, I was wondering whether it would be simpler to get
rid of tpci200_cfg_ops and replace the current implementation of the
local configuration registers with something like this:

memory_region_init_ram_ptr(&s->mmio, "tpci200_mmio", 128, s->local_cfg);
memory_region_init_alias(&s->io, "tpci200_io", &s->mmio, 0, 128);
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);

(s->local_cfg would contain the config registers data)

But it doesn't seem to work.

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-10 10:24             ` Alberto Garcia
@ 2012-10-10 11:35               ` Avi Kivity
  2012-10-10 17:59                 ` Alberto Garcia
  0 siblings, 1 reply; 27+ messages in thread
From: Avi Kivity @ 2012-10-10 11:35 UTC (permalink / raw)
  To: Alberto Garcia
  Cc: Anthony Liguori, qemu-devel, Blue Swirl, Paul Brook,
	Paolo Bonzini, Andreas Färber

On 10/10/2012 12:24 PM, Alberto Garcia wrote:
> On Sun, Oct 07, 2012 at 12:13:53PM +0200, Avi Kivity wrote:
> 
>> Luckily the low-order bits are used for offsets, and the high-order
>> bits are used for selecting the sub-device.
>> 
>> So you could easily have
>> 
>>  struct IPackDevice {
>>      DeviceState qdev;
>>      int32_t slot;
>>      /* IRQ objects for the IndustryPack INT0# and INT1# */
>>      qemu_irq *irq;
>>      MemoryRegion io_space;
>>      MemoryRegion id_space;
>>      MemoryRegion int_space;
>>      MemoryRegion mem8_space;  /* for las3 */
>>      MemoryRegion mem16_space; /* for las2 */
>>  };
>> 
>> The PCI device would then just map each space (with
>> memory_region_add_subregion()) into las1/las2/las3 such that the
>> high bits select the device/space.  The low bits would automatically
>> become the offset into the space.
> 
> Hey, I finally found some time to look into this, the problem that I
> see is that the PCI carrier doesn't just map each space into its local
> address spaces, in addition to that:
> 
>   1) it changes the data and addresses according to the endianness
>      configuration in the local configuration registers (PCI BAR0).
>      See adjust_addr()/adjust_value() and page 20 of the user manual.
> 
>      http://www.tews.com/Products/ArticleGroup/TPCI/TPCI200.html

This is supported (albeit not cleanly) by the memory API.  There is the
MemoryRegionOps::endianess attribute, you can have a mem16_be_space and
mem16_le_space; use
memory_region_add_subregion()/memory_region_del_subregion() to map
le_space or be_space as needed to the BAR.

> 
>   2) read accesses to the local address space 1 are also used to
>      acknowledge interrupts (manual page 33 and tpci200_read_las1()).
> 

You can map a small subregion over those addresses.  What is the value
read?  does it need to go to the int space and read it, or is it some
fixed value?

> 
> 
> On a related note, I was wondering whether it would be simpler to get
> rid of tpci200_cfg_ops and replace the current implementation of the
> local configuration registers with something like this:
> 
> memory_region_init_ram_ptr(&s->mmio, "tpci200_mmio", 128, s->local_cfg);
> memory_region_init_alias(&s->io, "tpci200_io", &s->mmio, 0, 128);
> pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
> pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);
> 
> (s->local_cfg would contain the config registers data)
> 
> But it doesn't seem to work.

This is supposed to work.  What does 'info mtree' show?  What happens if
you grow the size up to a page size?  Do both BARs fail, or just the IO BAR?


-- 
error compiling committee.c: too many arguments to function

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-10-10 11:35               ` Avi Kivity
@ 2012-10-10 17:59                 ` Alberto Garcia
  0 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-10-10 17:59 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Anthony Liguori, qemu-devel, Blue Swirl, Paul Brook,
	Paolo Bonzini, Andreas Färber

On Wed, Oct 10, 2012 at 01:35:06PM +0200, Avi Kivity wrote:

> > Hey, I finally found some time to look into this, the problem that
> > I see is that the PCI carrier doesn't just map each space into its
> > local address spaces, in addition to that:
> > 
> >   1) it changes the data and addresses according to the endianness
> >      configuration in the local configuration registers (PCI
> >      BAR0).  See adjust_addr()/adjust_value() and page 20 of the
> >      user manual.
> > 
> >      http://www.tews.com/Products/ArticleGroup/TPCI/TPCI200.html
> 
> This is supported (albeit not cleanly) by the memory
> API.  There is the MemoryRegionOps::endianess attribute,
> you can have a mem16_be_space and mem16_le_space; use
> memory_region_add_subregion()/memory_region_del_subregion() to map
> le_space or be_space as needed to the BAR.

I'm not sure if that will work in this case. From the TPCI200 manual:

  "Changing Local Space 0, 1 or 2 to Big Endian mode results in swapped
   data lines of the local bus:

   A 32 bit access is separated by the PCI9030 into two local 16 bit
   accesses. Byte lane 0 and 1 are swapped, and byte lane 2 and 3 are
   swapped.

   During 16 bit access, the upper and lower bytes are displayed in
   reverse order.

   During 8 bit access odd and even addresses are swapped. To access
   Address 0x00, the Address 0x01 must be used. An access to Address
   0x01 is done by Address 0x00."

32 bit access does not worry me in this case, since it's not used in
any local space, but how about 8 bit access? Is the "FIXME: big-endian
support" in access_with_adjusted_size() related to this?

> >   2) read accesses to the local address space 1 are also used to
> >      acknowledge interrupts (manual page 33 and
> >      tpci200_read_las1()).
> 
> You can map a small subregion over those addresses.  What is the
> value read? does it need to go to the int space and read it, or is
> it some fixed value?

It needs to go to the int space.

> > memory_region_init_ram_ptr(&s->mmio, "tpci200_mmio", 128, s->local_cfg);
> > memory_region_init_alias(&s->io, "tpci200_io", &s->mmio, 0, 128);
> > pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
> > pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);

> This is supposed to work.  What does 'info mtree' show?

pci
0000000000000000-7ffffffffffffffe (prio 0, RW): pci
[...]
  00000000fb022000-00000000fb02207f (prio 1, RW): tpci200_mmio
  00000000fb023000-00000000fb0230ff (prio 1, RW): tpci200_las0
  00000000fb024000-00000000fb0243ff (prio 1, RW): tpci200_las1
[...]

tpci200_mmio
00000000fb022000-00000000fb02207f (prio 1, RW): tpci200_mmio



I also tried to debug a bit read accesses to that memory region to see
what was going on. For example, when the kernel driver tries to read
the config register at PCI BAR0 + 0x2C the call ends up here:

subpage_ram_read (opaque=0x0, addr=0x2C, size=4)

I don't know the internals of the memory handling in QEMU but that
call doesn't look right, and the returned valued is indeed wrong.

> What happens if you grow the size up to a page size?

So I changed the size to 4K and got this:

kvm_set_phys_mem: error registering slot: Invalid argument
Aborted

If I run qemu without -enable-kvm then it appears to work fine (not
with the 128 bytes memory region, though).

> Do both BARs fail, or just the IO BAR?

I think the kernel driver doesn't use the IO BAR at all, so all my
tests are with BAR0.

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH v2 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
                   ` (2 preceding siblings ...)
  2012-08-31 14:12 ` [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and " Alberto Garcia
@ 2012-12-05 13:16 ` Alberto Garcia
  2012-12-05 14:03   ` Andreas Färber
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 1/2] Add TEWS TPCI200 " Alberto Garcia
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 27+ messages in thread
From: Alberto Garcia @ 2012-12-05 13:16 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anthony Liguori, Alberto Garcia, Blue Swirl, Avi Kivity,
	Paolo Bonzini, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 2325 bytes --]


Hello again,

I'm sending the new version of my TPCI200 and IP-Octal 232 patches,
here's the original submission for reference:

https://lists.gnu.org/archive/html/qemu-devel/2012-08/msg04173.html

It's been a while since I posted the previous patches. I was on
holidays for quite some time, plus the trip to the KVM Forum where I
could have the chance to meet some of you in person :)

Then came the v1.3 code freeze, so I was making a few changes to both
devices since then. These are the changes from the previous version:

   * Rebased against the current master.  ipoctal renamed to
   * ipoctal232.  Big-endian mode can now be disabled (tpci200).
   * Implemented the RxFIFO (ipoctal232).  Simplified the Tx code
   * (ipoctal232).  Removed the timer (ipoctal232).  Rewrote the IRQ
   * code (both devices).  A few minor bugfixes.  Coding style fixes.

The Linux driver is available in the latest mainline kernel (tested
with v3.7-rc6).

I also devoted quite some time to testing the code (and fixing a few
bugs in the kernel driver along the way). The device has 8 serial
ports, I tried them with minicom, pppd, etc and everything seems to
work fine.

I also tried attaching 4 instances of ipoctal232 to the tpci200 bridge
-32 serial ports in total- and using all of them at the same time and
there was nothing unexpected.

As a last thing, I also tried to reorganize the memory addressing
using Avi's suggestions but I couldn't come up with a solution to the
problems that I described back then. Here's the original thread for
reference:

https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg01117.html

I think that's all, if there's any questions please let me know.

Thanks,

Alberto Garcia (2):
  Add TEWS TPCI200 IndustryPack emulation
  Add GE IP-Octal 232 IndustryPack emulation

 default-configs/pci.mak |    1 +
 hw/Makefile.objs        |    3 +
 hw/ipack.c              |  106 ++++++++
 hw/ipack.h              |   75 ++++++
 hw/ipoctal.c            |  613 +++++++++++++++++++++++++++++++++++++++++++
 hw/pci_ids.h            |    3 +
 hw/tpci200.c            |  667 +++++++++++++++++++++++++++++++++++++++++++++++
 7 ficheiros modificados, 1468 adições(+)
 create mode 100644 hw/ipack.c
 create mode 100644 hw/ipack.h
 create mode 100644 hw/ipoctal.c
 create mode 100644 hw/tpci200.c

-- 
1.7.10.4


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH v2 1/2] Add TEWS TPCI200 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
                   ` (3 preceding siblings ...)
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 " Alberto Garcia
@ 2012-12-05 13:16 ` Alberto Garcia
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 2/2] Add GE IP-Octal 232 " Alberto Garcia
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-12-05 13:16 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anthony Liguori, Alberto Garcia, Blue Swirl, Avi Kivity,
	Paolo Bonzini, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 640 bytes --]


The TPCI200 is a PCI board that supports up to 4 IndustryPack modules.

A new bus type called 'IndustryPack' has been created so any
compatible module can be attached to this board.

Signed-off-by: Alberto Garcia <agarcia@igalia.com>
---
 default-configs/pci.mak |    1 +
 hw/Makefile.objs        |    3 +
 hw/ipack.c              |  106 ++++++++
 hw/ipack.h              |   75 ++++++
 hw/pci_ids.h            |    3 +
 hw/tpci200.c            |  667 +++++++++++++++++++++++++++++++++++++++++++++++
 6 ficheiros modificados, 855 adições(+)
 create mode 100644 hw/ipack.c
 create mode 100644 hw/ipack.h
 create mode 100644 hw/tpci200.c


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-TEWS-TPCI200-IndustryPack-emulation.patch --]
[-- Type: text/x-patch; name="0001-Add-TEWS-TPCI200-IndustryPack-emulation.patch", Size: 26205 bytes --]

diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index ae9d1eb..ee2d18d 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -21,3 +21,4 @@ CONFIG_ESP=y
 CONFIG_ESP_PCI=y
 CONFIG_SERIAL=y
 CONFIG_SERIAL_PCI=y
+CONFIG_IPACK=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index d581d8d..2ce5ec0 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -104,6 +104,9 @@ common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 
 common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
 
+# IndustryPack
+common-obj-$(CONFIG_IPACK) += tpci200.o ipack.o
+
 # PCI network cards
 common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
 common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
diff --git a/hw/ipack.c b/hw/ipack.c
new file mode 100644
index 0000000..59e272b
--- /dev/null
+++ b/hw/ipack.c
@@ -0,0 +1,106 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "ipack.h"
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
+{
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        IPackDevice *ip = DO_UPCAST(IPackDevice, qdev, qdev);
+        if (ip->slot == slot) {
+            return ip;
+        }
+    }
+    return NULL;
+}
+
+static int ipack_device_dev_init(DeviceState *qdev)
+{
+    IPackBus *bus = DO_UPCAST(IPackBus, qbus, qdev->parent_bus);
+    IPackDevice *dev = DO_UPCAST(IPackDevice, qdev, qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (dev->slot < 0) {
+        dev->slot = bus->free_slot;
+    }
+    if (dev->slot >= bus->n_slots) {
+        return -1;
+    }
+    bus->free_slot = dev->slot + 1;
+
+    dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2);
+
+    return k->init(dev);
+}
+
+static int ipack_device_dev_exit(DeviceState *qdev)
+{
+    IPackDevice *dev = DO_UPCAST(IPackDevice, qdev, qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (k->exit) {
+        k->exit(dev);
+    }
+
+    qemu_free_irqs(dev->irq);
+
+    return 0;
+}
+
+static Property ipack_device_props[] = {
+    DEFINE_PROP_INT32("slot", IPackDevice, slot, -1),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void ipack_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_type = TYPE_IPACK_BUS;
+    k->init = ipack_device_dev_init;
+    k->exit = ipack_device_dev_exit;
+    k->props = ipack_device_props;
+}
+
+const VMStateDescription vmstate_ipack_device = {
+    .name = "ipack_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(slot, IPackDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const TypeInfo ipack_device_info = {
+    .name          = TYPE_IPACK_DEVICE,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(IPackDevice),
+    .class_size    = sizeof(IPackDeviceClass),
+    .class_init    = ipack_device_class_init,
+    .abstract      = true,
+};
+
+static const TypeInfo ipack_bus_info = {
+    .name = TYPE_IPACK_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(IPackBus),
+};
+
+static void ipack_register_types(void)
+{
+    type_register_static(&ipack_device_info);
+    type_register_static(&ipack_bus_info);
+}
+
+type_init(ipack_register_types)
diff --git a/hw/ipack.h b/hw/ipack.h
new file mode 100644
index 0000000..61a9019
--- /dev/null
+++ b/hw/ipack.h
@@ -0,0 +1,75 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#ifndef QEMU_IPACK_H
+#define QEMU_IPACK_H
+
+#include "qdev.h"
+
+typedef struct IPackBus IPackBus;
+
+#define TYPE_IPACK_BUS "IndustryPack"
+#define IPACK_BUS(obj) OBJECT_CHECK(IPackBus, (obj), TYPE_IPACK_BUS)
+
+struct IPackBus {
+    BusState qbus;
+    uint8_t n_slots;
+    uint8_t free_slot;
+    qemu_irq_handler set_irq;
+};
+
+typedef struct IPackDevice IPackDevice;
+typedef struct IPackDeviceClass IPackDeviceClass;
+
+#define TYPE_IPACK_DEVICE "ipack-device"
+#define IPACK_DEVICE(obj) \
+     OBJECT_CHECK(IPackDevice, (obj), TYPE_IPACK_DEVICE)
+#define IPACK_DEVICE_CLASS(klass)                                        \
+     OBJECT_CLASS_CHECK(IPackDeviceClass, (klass), TYPE_IPACK_DEVICE)
+#define IPACK_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(IPackDeviceClass, (obj), TYPE_IPACK_DEVICE)
+
+struct IPackDeviceClass {
+    DeviceClass parent_class;
+
+    int (*init)(IPackDevice *dev);
+    int (*exit)(IPackDevice *dev);
+
+    uint16_t (*io_read)(IPackDevice *dev, uint8_t addr);
+    void (*io_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*id_read)(IPackDevice *dev, uint8_t addr);
+    void (*id_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*int_read)(IPackDevice *dev, uint8_t addr);
+    void (*int_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*mem_read16)(IPackDevice *dev, uint32_t addr);
+    void (*mem_write16)(IPackDevice *dev, uint32_t addr, uint16_t val);
+
+    uint8_t (*mem_read8)(IPackDevice *dev, uint32_t addr);
+    void (*mem_write8)(IPackDevice *dev, uint32_t addr, uint8_t val);
+};
+
+struct IPackDevice {
+    DeviceState qdev;
+    int32_t slot;
+    /* IRQ objects for the IndustryPack INT0# and INT1# */
+    qemu_irq *irq;
+};
+
+extern const VMStateDescription vmstate_ipack_device;
+
+#define VMSTATE_IPACK_DEVICE(_field, _state)                            \
+    VMSTATE_STRUCT(_field, _state, 1, vmstate_ipack_device, IPackDevice)
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot);
+
+#endif
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 5df7245..38d6af5 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -145,3 +145,6 @@
 
 #define PCI_VENDOR_ID_NEC                0x1033
 #define PCI_DEVICE_ID_NEC_UPD720200      0x0194
+
+#define PCI_VENDOR_ID_TEWS               0x1498
+#define PCI_DEVICE_ID_TEWS_TPCI200       0x30C8
diff --git a/hw/tpci200.c b/hw/tpci200.c
new file mode 100644
index 0000000..f63f916
--- /dev/null
+++ b/hw/tpci200.c
@@ -0,0 +1,667 @@
+/*
+ * QEMU TEWS TPCI200 IndustryPack carrier emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "pci.h"
+#include "ipack.h"
+#include "bitops.h"
+#include <stdio.h>
+
+/* #define DEBUG_TPCI */
+
+#ifdef DEBUG_TPCI
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define N_MODULES 4
+
+#define IP_ID_SPACE  2
+#define IP_INT_SPACE 3
+#define IP_IO_SPACE_ADDR_MASK  0x7F
+#define IP_ID_SPACE_ADDR_MASK  0x3F
+#define IP_INT_SPACE_ADDR_MASK 0x3F
+
+#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO))
+#define STATUS_TIMEOUT(IP)    BIT((IP) + 12)
+#define STATUS_ERR_ANY        0xF00
+
+#define CTRL_CLKRATE          BIT(0)
+#define CTRL_RECOVER          BIT(1)
+#define CTRL_TIME_INT         BIT(2)
+#define CTRL_ERR_INT          BIT(3)
+#define CTRL_INT_EDGE(INTNO)  BIT(4 + (INTNO))
+#define CTRL_INT(INTNO)       BIT(6 + (INTNO))
+
+#define REG_REV_ID    0x00
+#define REG_IP_A_CTRL 0x02
+#define REG_IP_B_CTRL 0x04
+#define REG_IP_C_CTRL 0x06
+#define REG_IP_D_CTRL 0x08
+#define REG_RESET     0x0A
+#define REG_STATUS    0x0C
+#define IP_N_FROM_REG(REG) ((REG) / 2 - 1)
+
+typedef struct {
+    PCIDevice dev;
+    IPackBus bus;
+    MemoryRegion mmio;
+    MemoryRegion io;
+    MemoryRegion las0;
+    MemoryRegion las1;
+    MemoryRegion las2;
+    MemoryRegion las3;
+    bool big_endian[3];
+    uint8_t ctrl[N_MODULES];
+    uint16_t status;
+    uint8_t int_set;
+} TPCI200State;
+
+static const uint8_t local_config_regs[] = {
+    0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00,
+    0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4,
+    0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01,
+    0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02,
+    0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41,
+    0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02
+};
+
+static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size)
+{
+    /* During 8 bit access in big endian mode,
+       odd and even addresses are swapped */
+    if (big_endian && size == 1) {
+        *addr ^= 1;
+    }
+}
+
+static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size)
+{
+    /* Local spaces only support 8/16 bit access,
+     * so there's no need to care for sizes > 2 */
+    if (big_endian && size == 2) {
+        *val = bswap16(*val);
+    }
+    return *val;
+}
+
+static void tpci200_set_irq(void *opaque, int intno, int level)
+{
+    IPackDevice *ip = opaque;
+    IPackBus *bus = DO_UPCAST(IPackBus, qbus, ip->qdev.parent_bus);
+    PCIDevice *pcidev = DO_UPCAST(PCIDevice, qdev, bus->qbus.parent);
+    TPCI200State *dev = DO_UPCAST(TPCI200State, dev, pcidev);
+    unsigned ip_n = ip->slot;
+    uint16_t prev_status = dev->status;
+
+    assert(ip->slot >= 0 && ip->slot < N_MODULES);
+
+    /* The requested interrupt must be enabled in the IP CONTROL
+     * register */
+    if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) {
+        return;
+    }
+
+    /* Update the interrupt status in the IP STATUS register */
+    if (level) {
+        dev->status |=  STATUS_INT(ip_n, intno);
+    } else {
+        dev->status &= ~STATUS_INT(ip_n, intno);
+    }
+
+    /* Return if there are no changes */
+    if (dev->status == prev_status) {
+        return;
+    }
+
+    DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level);
+
+    /* Check if the interrupt is edge sensitive */
+    if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) {
+        if (level) {
+            qemu_set_irq(dev->dev.irq[0], !dev->int_set);
+            qemu_set_irq(dev->dev.irq[0],  dev->int_set);
+        }
+    } else {
+        unsigned i, j;
+        uint16_t level_status = dev->status;
+
+        /* Check if there are any level sensitive interrupts set by
+           removing the ones that are edge sensitive from the status
+           register */
+        for (i = 0; i < N_MODULES; i++) {
+            for (j = 0; j < 2; j++) {
+                if (dev->ctrl[i] & CTRL_INT_EDGE(j)) {
+                    level_status &= ~STATUS_INT(i, j);
+                }
+            }
+        }
+
+        if (level_status && !dev->int_set) {
+            qemu_irq_raise(dev->dev.irq[0]);
+            dev->int_set = 1;
+        } else if (!level_status && dev->int_set) {
+            qemu_irq_lower(dev->dev.irq[0]);
+            dev->int_set = 0;
+        }
+    }
+}
+
+static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint8_t ret = 0;
+    if (addr < ARRAY_SIZE(local_config_regs)) {
+        ret = local_config_regs[addr];
+    }
+    /* Endianness is stored in the first bit of these registers */
+    if ((addr == 0x2b && s->big_endian[0]) ||
+        (addr == 0x2f && s->big_endian[1]) ||
+        (addr == 0x33 && s->big_endian[2])) {
+        ret |= 1;
+    }
+    DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret);
+    return ret;
+}
+
+static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned size)
+{
+    TPCI200State *s = opaque;
+    /* Endianness is stored in the first bit of these registers */
+    if (addr == 0x2b || addr == 0x2f || addr == 0x33) {
+        unsigned las = (addr - 0x2b) / 4;
+        s->big_endian[las] = val & 1;
+        DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1);
+    } else {
+        DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val);
+    }
+}
+
+static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint64_t ret = 0;
+
+    switch (addr) {
+
+    case REG_REV_ID:
+        DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */
+        break;
+
+    case REG_IP_A_CTRL:
+    case REG_IP_B_CTRL:
+    case REG_IP_C_CTRL:
+    case REG_IP_D_CTRL:
+        {
+            unsigned ip_n = IP_N_FROM_REG(addr);
+            ret = s->ctrl[ip_n];
+            DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret);
+        }
+        break;
+
+    case REG_RESET:
+        DPRINTF("Read RESET\n"); /* Not implemented */
+        break;
+
+    case REG_STATUS:
+        ret = s->status;
+        DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret);
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr);
+        break;
+    }
+
+    return adjust_value(s->big_endian[0], &ret, size);
+}
+
+static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+
+    adjust_value(s->big_endian[0], &val, size);
+
+    switch (addr) {
+
+    case REG_REV_ID:
+        DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */
+        break;
+
+    case REG_IP_A_CTRL:
+    case REG_IP_B_CTRL:
+    case REG_IP_C_CTRL:
+    case REG_IP_D_CTRL:
+        {
+            unsigned ip_n = IP_N_FROM_REG(addr);
+            s->ctrl[ip_n] = val;
+            DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val);
+        }
+        break;
+
+    case REG_RESET:
+        DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */
+        break;
+
+    case REG_STATUS:
+        {
+            unsigned i;
+
+            for (i = 0; i < N_MODULES; i++) {
+                IPackDevice *ip = ipack_device_find(&s->bus, i);
+
+                if (ip != NULL) {
+                    if (val & STATUS_INT(i, 0)) {
+                        DPRINTF("Clear IP %c INT0# status\n", 'A' + i);
+                        qemu_irq_lower(ip->irq[0]);
+                    }
+                    if (val & STATUS_INT(i, 1)) {
+                        DPRINTF("Clear IP %c INT1# status\n", 'A' + i);
+                        qemu_irq_lower(ip->irq[1]);
+                    }
+                }
+
+                if (val & STATUS_TIMEOUT(i)) {
+                    DPRINTF("Clear IP %c timeout\n", 'A' + i);
+                    s->status &= ~STATUS_TIMEOUT(i);
+                }
+            }
+
+            if (val & STATUS_ERR_ANY) {
+                DPRINTF("Unexpected write to STATUS register: 0x%x\n",
+                        (unsigned) val);
+            }
+        }
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
+                (unsigned) addr, (unsigned) val);
+        break;
+    }
+}
+
+static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+
+    /*
+     * The address is divided into the IP module number (0-4), the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_read) {
+                ret = k->id_read(ip, offset);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+
+            /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
+            if (offset == 0 || offset == 2) {
+                unsigned intno = offset / 2;
+                bool int_set = s->status & STATUS_INT(ip_n, intno);
+                bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
+                if (int_set && !int_edge_sensitive) {
+                    qemu_irq_lower(ip->irq[intno]);
+                }
+            }
+
+            if (k->int_read) {
+                ret = k->int_read(ip, offset);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_read) {
+                ret = k->io_read(ip, offset);
+            }
+            break;
+        }
+    }
+
+    return adjust_value(s->big_endian[1], &ret, size);
+}
+
+static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+    adjust_value(s->big_endian[1], &val, size);
+
+    /*
+     * The address is divided into the IP module number, the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_write) {
+                k->id_write(ip, offset, val);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+            if (k->int_write) {
+                k->int_write(ip, offset, val);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_write) {
+                k->io_write(ip, offset, val);
+            }
+            break;
+        }
+    }
+}
+
+static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read16) {
+            ret = k->mem_read16(ip, offset);
+        }
+    }
+
+    return adjust_value(s->big_endian[2], &ret, size);
+}
+
+static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+    adjust_value(s->big_endian[2], &val, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write16) {
+            k->mem_write16(ip, offset, val);
+        }
+    }
+}
+
+static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read8) {
+            ret = k->mem_read8(ip, offset);
+        }
+    }
+
+    return ret;
+}
+
+static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write8) {
+            k->mem_write8(ip, offset, val);
+        }
+    }
+}
+
+static const MemoryRegionOps tpci200_cfg_ops = {
+    .read = tpci200_read_cfg,
+    .write = tpci200_write_cfg,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 4
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static const MemoryRegionOps tpci200_las0_ops = {
+    .read = tpci200_read_las0,
+    .write = tpci200_write_las0,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 2,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las1_ops = {
+    .read = tpci200_read_las1,
+    .write = tpci200_write_las1,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las2_ops = {
+    .read = tpci200_read_las2,
+    .write = tpci200_write_las2,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las3_ops = {
+    .read = tpci200_read_las3,
+    .write = tpci200_write_las3,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static int tpci200_initfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = DO_UPCAST(TPCI200State, dev, pci_dev);
+    uint8_t *c = s->dev.config;
+
+    pci_set_word(c + PCI_COMMAND, 0x0003);
+    pci_set_word(c + PCI_STATUS,  0x0280);
+
+    pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
+
+    pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
+    pci_set_long(c + 0x40, 0x48014801);
+    pci_set_long(c + 0x48, 0x00024C06);
+    pci_set_long(c + 0x4C, 0x00000003);
+
+    memory_region_init_io(&s->mmio, &tpci200_cfg_ops,
+                          s, "tpci200_mmio", 128);
+    memory_region_init_io(&s->io,   &tpci200_cfg_ops,
+                          s, "tpci200_io",   128);
+    memory_region_init_io(&s->las0, &tpci200_las0_ops,
+                          s, "tpci200_las0", 256);
+    memory_region_init_io(&s->las1, &tpci200_las1_ops,
+                          s, "tpci200_las1", 1024);
+    memory_region_init_io(&s->las2, &tpci200_las2_ops,
+                          s, "tpci200_las2", 1024*1024*32);
+    memory_region_init_io(&s->las3, &tpci200_las3_ops,
+                          s, "tpci200_las3", 1024*1024*16);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
+    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);
+    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
+    pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
+    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
+    pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
+
+    qbus_create_inplace(&s->bus.qbus, TYPE_IPACK_BUS, &s->dev.qdev, NULL);
+    s->bus.n_slots = N_MODULES;
+    s->bus.set_irq = tpci200_set_irq;
+
+    return 0;
+}
+
+static void tpci200_exitfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = DO_UPCAST(TPCI200State, dev, pci_dev);
+
+    memory_region_destroy(&s->mmio);
+    memory_region_destroy(&s->io);
+    memory_region_destroy(&s->las0);
+    memory_region_destroy(&s->las1);
+    memory_region_destroy(&s->las2);
+    memory_region_destroy(&s->las3);
+}
+
+static const VMStateDescription vmstate_tpci200 = {
+    .name = "tpci200",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, TPCI200State),
+        VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
+        VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES),
+        VMSTATE_UINT16(status, TPCI200State),
+        VMSTATE_UINT8(int_set, TPCI200State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void tpci200_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = tpci200_initfn;
+    k->exit = tpci200_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_TEWS;
+    k->device_id = PCI_DEVICE_ID_TEWS_TPCI200;
+    k->class_id = PCI_CLASS_BRIDGE_OTHER;
+    k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS;
+    k->subsystem_id = 0x300A;
+    dc->desc = "TEWS TPCI200 IndustryPack carrier";
+    dc->vmsd = &vmstate_tpci200;
+}
+
+static const TypeInfo tpci200_info = {
+    .name          = "tpci200",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(TPCI200State),
+    .class_init    = tpci200_class_init,
+};
+
+static void tpci200_register_types(void)
+{
+    type_register_static(&tpci200_info);
+}
+
+type_init(tpci200_register_types)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH v2 2/2] Add GE IP-Octal 232 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
                   ` (4 preceding siblings ...)
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 1/2] Add TEWS TPCI200 " Alberto Garcia
@ 2012-12-05 13:16 ` Alberto Garcia
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 0/2] Add TPCI200 and " Alberto Garcia
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-12-05 13:16 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anthony Liguori, Alberto Garcia, Blue Swirl, Avi Kivity,
	Paolo Bonzini, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 418 bytes --]


The GE IP-Octal 232 is an IndustryPack module that implements eight
RS-232 serial ports, each one of which can be redirected to a
character device in the host.

Signed-off-by: Alberto Garcia <agarcia@igalia.com>
---
 hw/Makefile.objs |    2 +-
 hw/ipoctal.c     |  613 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 ficheiros modificados, 614 adições(+), 1 eliminado(-)
 create mode 100644 hw/ipoctal.c


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0002-Add-GE-IP-Octal-232-IndustryPack-emulation.patch --]
[-- Type: text/x-patch; name="0002-Add-GE-IP-Octal-232-IndustryPack-emulation.patch", Size: 18071 bytes --]

diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 2ce5ec0..b43a67e 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -105,7 +105,7 @@ common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
 
 # IndustryPack
-common-obj-$(CONFIG_IPACK) += tpci200.o ipack.o
+common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal.o ipack.o
 
 # PCI network cards
 common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
diff --git a/hw/ipoctal.c b/hw/ipoctal.c
new file mode 100644
index 0000000..8cb34f9
--- /dev/null
+++ b/hw/ipoctal.c
@@ -0,0 +1,613 @@
+/*
+ * QEMU GE IP-Octal 232 IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "ipack.h"
+#include "bitops.h"
+
+/* #define DEBUG_IPOCTAL */
+
+#ifdef DEBUG_IPOCTAL
+#define DPRINTF2(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF2(fmt, ...) do { } while (0)
+#endif
+
+#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
+
+#define RX_FIFO_SIZE 3
+
+/* The IP-Octal has 8 channels (a-h)
+   divided into 4 blocks (A-D) */
+#define N_CHANNELS 8
+#define N_BLOCKS   4
+
+#define REG_MRa  0x01
+#define REG_MRb  0x11
+#define REG_SRa  0x03
+#define REG_SRb  0x13
+#define REG_CSRa 0x03
+#define REG_CSRb 0x13
+#define REG_CRa  0x05
+#define REG_CRb  0x15
+#define REG_RHRa 0x07
+#define REG_RHRb 0x17
+#define REG_THRa 0x07
+#define REG_THRb 0x17
+#define REG_ACR  0x09
+#define REG_ISR  0x0B
+#define REG_IMR  0x0B
+#define REG_OPCR 0x1B
+
+#define CR_ENABLE_RX    BIT(0)
+#define CR_DISABLE_RX   BIT(1)
+#define CR_ENABLE_TX    BIT(2)
+#define CR_DISABLE_TX   BIT(3)
+#define CR_CMD(cr)      ((cr) >> 4)
+#define CR_NO_OP        0
+#define CR_RESET_MR     1
+#define CR_RESET_RX     2
+#define CR_RESET_TX     3
+#define CR_RESET_ERR    4
+#define CR_RESET_BRKINT 5
+#define CR_START_BRK    6
+#define CR_STOP_BRK     7
+#define CR_ASSERT_RTSN  8
+#define CR_NEGATE_RTSN  9
+#define CR_TIMEOUT_ON   10
+#define CR_TIMEOUT_OFF  12
+
+#define SR_RXRDY   BIT(0)
+#define SR_FFULL   BIT(1)
+#define SR_TXRDY   BIT(2)
+#define SR_TXEMT   BIT(3)
+#define SR_OVERRUN BIT(4)
+#define SR_PARITY  BIT(5)
+#define SR_FRAMING BIT(6)
+#define SR_BREAK   BIT(7)
+
+#define ISR_TXRDYA BIT(0)
+#define ISR_RXRDYA BIT(1)
+#define ISR_BREAKA BIT(2)
+#define ISR_CNTRDY BIT(3)
+#define ISR_TXRDYB BIT(4)
+#define ISR_RXRDYB BIT(5)
+#define ISR_BREAKB BIT(6)
+#define ISR_MPICHG BIT(7)
+#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0))
+#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1))
+#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2))
+
+typedef struct IPOctalState IPOctalState;
+typedef struct SCC2698Channel SCC2698Channel;
+typedef struct SCC2698Block SCC2698Block;
+
+struct SCC2698Channel {
+    IPOctalState *ipoctal;
+    CharDriverState *dev;
+    char *devpath;
+    bool rx_enabled;
+    uint8_t mr[2];
+    uint8_t mr_idx;
+    uint8_t sr;
+    uint8_t rhr[RX_FIFO_SIZE];
+    uint8_t rhr_idx;
+    uint8_t rx_pending;
+};
+
+struct SCC2698Block {
+    uint8_t imr;
+    uint8_t isr;
+};
+
+struct IPOctalState {
+    IPackDevice dev;
+    SCC2698Channel ch[N_CHANNELS];
+    SCC2698Block blk[N_BLOCKS];
+    uint8_t irq_vector;
+};
+
+static const VMStateDescription vmstate_scc2698_channel = {
+    .name = "scc2698_channel",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(rx_enabled, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
+        VMSTATE_UINT8(mr_idx, SCC2698Channel),
+        VMSTATE_UINT8(sr, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE),
+        VMSTATE_UINT8(rhr_idx, SCC2698Channel),
+        VMSTATE_UINT8(rx_pending, SCC2698Channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_scc2698_block = {
+    .name = "scc2698_block",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(imr, SCC2698Block),
+        VMSTATE_UINT8(isr, SCC2698Block),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_ipoctal = {
+    .name = "ipoctal",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_IPACK_DEVICE(dev, IPOctalState),
+        VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
+                             vmstate_scc2698_channel, SCC2698Channel),
+        VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
+                             vmstate_scc2698_block, SCC2698Block),
+        VMSTATE_UINT8(irq_vector, IPOctalState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* data[10] is 0x0C, not 0x0B as the doc says */
+static const uint8_t id_prom_data[] = {
+    0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
+    0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
+};
+
+static void update_irq(IPOctalState *dev, unsigned block)
+{
+    /* Blocks A and B interrupt on INT0#, C and D on INT1#.
+       Thus, to get the status we have to check two blocks. */
+    SCC2698Block *blk0 = &dev->blk[block];
+    SCC2698Block *blk1 = &dev->blk[block^1];
+    unsigned intno = block / 2;
+
+    if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) {
+        qemu_irq_raise(dev->dev.irq[intno]);
+    } else {
+        qemu_irq_lower(dev->dev.irq[intno]);
+    }
+}
+
+static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
+{
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[channel / 2];
+
+    DPRINTF("Write CR%c %u: ", channel + 'a', val);
+
+    /* The lower 4 bits are used to enable and disable Tx and Rx */
+    if (val & CR_ENABLE_RX) {
+        DPRINTF2("Rx on, ");
+        ch->rx_enabled = true;
+    }
+    if (val & CR_DISABLE_RX) {
+        DPRINTF2("Rx off, ");
+        ch->rx_enabled = false;
+    }
+    if (val & CR_ENABLE_TX) {
+        DPRINTF2("Tx on, ");
+        ch->sr |= SR_TXRDY | SR_TXEMT;
+        blk->isr |= ISR_TXRDY(channel);
+    }
+    if (val & CR_DISABLE_TX) {
+        DPRINTF2("Tx off, ");
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+        blk->isr &= ~ISR_TXRDY(channel);
+    }
+
+    DPRINTF2("cmd: ");
+
+    /* The rest of the bits implement different commands */
+    switch (CR_CMD(val)) {
+    case CR_NO_OP:
+        DPRINTF2("none");
+        break;
+    case CR_RESET_MR:
+        DPRINTF2("reset MR");
+        ch->mr_idx = 0;
+        break;
+    case CR_RESET_RX:
+        DPRINTF2("reset Rx");
+        ch->rx_enabled = false;
+        ch->rx_pending = 0;
+        ch->sr &= ~SR_RXRDY;
+        blk->isr &= ~ISR_RXRDY(channel);
+        break;
+    case CR_RESET_TX:
+        DPRINTF2("reset Tx");
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+        blk->isr &= ~ISR_TXRDY(channel);
+        break;
+    case CR_RESET_ERR:
+        DPRINTF2("reset err");
+        ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
+        break;
+    case CR_RESET_BRKINT:
+        DPRINTF2("reset brk ch int");
+        blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
+        break;
+    default:
+        DPRINTF2("unsupported 0x%x", CR_CMD(val));
+    }
+
+    DPRINTF2("\n");
+}
+
+static uint16_t io_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    uint16_t ret = 0;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    uint8_t old_isr = blk->isr;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ret = ch->mr[ch->mr_idx];
+        DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
+        ch->mr_idx = 1;
+        break;
+
+    case REG_SRa:
+    case REG_SRb:
+        ret = ch->sr;
+        DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
+        break;
+
+    case REG_RHRa:
+    case REG_RHRb:
+        ret = ch->rhr[ch->rhr_idx];
+        if (ch->rx_pending > 0) {
+            ch->rx_pending--;
+            if (ch->rx_pending == 0) {
+                ch->sr &= ~SR_RXRDY;
+                blk->isr &= ~ISR_RXRDY(channel);
+                if (ch->dev) {
+                    qemu_chr_accept_input(ch->dev);
+                }
+            } else {
+                ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
+            }
+            if (ch->sr & SR_BREAK) {
+                ch->sr &= ~SR_BREAK;
+                blk->isr |= ISR_BREAK(channel);
+            }
+        }
+        DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret);
+        break;
+
+    case REG_ISR:
+        ret = blk->isr;
+        DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
+        break;
+
+    default:
+        DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
+    }
+
+    if (old_isr != blk->isr) {
+        update_irq(dev, block);
+    }
+
+    return ret;
+}
+
+static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    unsigned reg = val & 0xFF;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    uint8_t old_isr = blk->isr;
+    uint8_t old_imr = blk->imr;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ch->mr[ch->mr_idx] = reg;
+        DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
+        ch->mr_idx = 1;
+        break;
+
+    /* Not implemented */
+    case REG_CSRa:
+    case REG_CSRb:
+        DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
+        break;
+
+    case REG_CRa:
+    case REG_CRb:
+        write_cr(dev, channel, reg);
+        break;
+
+    case REG_THRa:
+    case REG_THRb:
+        if (ch->sr & SR_TXRDY) {
+            DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
+            if (ch->dev) {
+                uint8_t thr = reg;
+                qemu_chr_fe_write(ch->dev, &thr, 1);
+            }
+        } else {
+            DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
+        }
+        break;
+
+    /* Not implemented */
+    case REG_ACR:
+        DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
+        break;
+
+    case REG_IMR:
+        DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
+        blk->imr = reg;
+        break;
+
+    /* Not implemented */
+    case REG_OPCR:
+        DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
+        break;
+
+    default:
+        DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
+    }
+
+    if (old_isr != blk->isr || old_imr != blk->imr) {
+        update_irq(dev, block);
+    }
+}
+
+static uint16_t id_read(IPackDevice *ip, uint8_t addr)
+{
+    uint16_t ret = 0;
+    unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
+
+    if (pos < ARRAY_SIZE(id_prom_data)) {
+        ret = id_prom_data[pos];
+    } else {
+        DPRINTF("Attempt to read unavailable PROM data at 0x%x\n",  addr);
+    }
+
+    return ret;
+}
+
+static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val; /* Undocumented, but the hw works like that */
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static uint16_t int_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */
+    if (addr != 0 && addr != 2) {
+        DPRINTF("Attempt to read from 0x%x\n", addr);
+        return 0;
+    } else {
+        /* Update interrupts if necessary */
+        update_irq(dev, addr);
+        return dev->irq_vector;
+    }
+}
+
+static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val;
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static int hostdev_can_receive(void *opaque)
+{
+    SCC2698Channel *ch = opaque;
+    int available_bytes = RX_FIFO_SIZE - ch->rx_pending;
+    return ch->rx_enabled ? available_bytes : 0;
+}
+
+static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
+{
+    SCC2698Channel *ch = opaque;
+    IPOctalState *dev = ch->ipoctal;
+    unsigned pos = ch->rhr_idx + ch->rx_pending;
+    int i;
+
+    assert(size + ch->rx_pending <= RX_FIFO_SIZE);
+
+    /* Copy data to the RxFIFO */
+    for (i = 0; i < size; i++) {
+        pos %= RX_FIFO_SIZE;
+        ch->rhr[pos++] = buf[i];
+    }
+
+    ch->rx_pending += size;
+
+    /* If the RxFIFO was empty raise an interrupt */
+    if (!(ch->sr & SR_RXRDY)) {
+        unsigned block, channel = 0;
+        /* Find channel number to update the ISR register */
+        while (&dev->ch[channel] != ch) {
+            channel++;
+        }
+        block = channel / 2;
+        dev->blk[block].isr |= ISR_RXRDY(channel);
+        ch->sr |= SR_RXRDY;
+        update_irq(dev, block);
+    }
+}
+
+static void hostdev_event(void *opaque, int event)
+{
+    SCC2698Channel *ch = opaque;
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        DPRINTF("Device %s opened\n", ch->dev->label);
+        break;
+    case CHR_EVENT_BREAK: {
+        uint8_t zero = 0;
+        DPRINTF("Device %s received break\n", ch->dev->label);
+
+        if (!(ch->sr & SR_BREAK)) {
+            IPOctalState *dev = ch->ipoctal;
+            unsigned block, channel = 0;
+
+            while (&dev->ch[channel] != ch) {
+                channel++;
+            }
+            block = channel / 2;
+
+            ch->sr |= SR_BREAK;
+            dev->blk[block].isr |= ISR_BREAK(channel);
+        }
+
+        /* Put a zero character in the buffer */
+        hostdev_receive(ch, &zero, 1);
+    }
+        break;
+    default:
+        DPRINTF("Device %s received event %d\n", ch->dev->label, event);
+    }
+}
+
+static int ipoctal_init(IPackDevice *ip)
+{
+    IPOctalState *s = DO_UPCAST(IPOctalState, dev, ip);
+    unsigned i;
+
+    for (i = 0; i < N_CHANNELS; i++) {
+        SCC2698Channel *ch = &s->ch[i];
+        ch->ipoctal = s;
+
+        /* Redirect IP-Octal channels to host character devices */
+        if (ch->devpath) {
+            const char chr_name[] = "ipoctal";
+            char label[ARRAY_SIZE(chr_name) + 2];
+            static int index;
+
+            snprintf(label, sizeof(label), "%s%d", chr_name, index);
+
+            ch->dev = qemu_chr_new(label, ch->devpath, NULL);
+
+            if (ch->dev) {
+                index++;
+                qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
+                                      hostdev_receive, hostdev_event, ch);
+                DPRINTF("Redirecting channel %u to %s (%s)\n",
+                        i, ch->devpath, label);
+            } else {
+                DPRINTF("Could not redirect channel %u to %s\n",
+                        i, ch->devpath);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static Property ipoctal_properties[] = {
+    DEFINE_PROP_STRING("serial0", IPOctalState, ch[0].devpath),
+    DEFINE_PROP_STRING("serial1", IPOctalState, ch[1].devpath),
+    DEFINE_PROP_STRING("serial2", IPOctalState, ch[2].devpath),
+    DEFINE_PROP_STRING("serial3", IPOctalState, ch[3].devpath),
+    DEFINE_PROP_STRING("serial4", IPOctalState, ch[4].devpath),
+    DEFINE_PROP_STRING("serial5", IPOctalState, ch[5].devpath),
+    DEFINE_PROP_STRING("serial6", IPOctalState, ch[6].devpath),
+    DEFINE_PROP_STRING("serial7", IPOctalState, ch[7].devpath),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipoctal_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
+
+    ic->init        = ipoctal_init;
+    ic->io_read     = io_read;
+    ic->io_write    = io_write;
+    ic->id_read     = id_read;
+    ic->id_write    = id_write;
+    ic->int_read    = int_read;
+    ic->int_write   = int_write;
+    ic->mem_read16  = mem_read16;
+    ic->mem_write16 = mem_write16;
+    ic->mem_read8   = mem_read8;
+    ic->mem_write8  = mem_write8;
+
+    dc->desc    = "GE IP-Octal 232 8-channel RS-232 IndustryPack";
+    dc->props   = ipoctal_properties;
+    dc->vmsd    = &vmstate_ipoctal;
+}
+
+static const TypeInfo ipoctal_info = {
+    .name          = "ipoctal232",
+    .parent        = TYPE_IPACK_DEVICE,
+    .instance_size = sizeof(IPOctalState),
+    .class_init    = ipoctal_class_init,
+};
+
+static void ipoctal_register_types(void)
+{
+    type_register_static(&ipoctal_info);
+}
+
+type_init(ipoctal_register_types)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] [PATCH v2 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 " Alberto Garcia
@ 2012-12-05 14:03   ` Andreas Färber
  0 siblings, 0 replies; 27+ messages in thread
From: Andreas Färber @ 2012-12-05 14:03 UTC (permalink / raw)
  To: Alberto Garcia; +Cc: qemu-devel

Hello Alberto,

Am 05.12.2012 14:16, schrieb Alberto Garcia:
> 
> Hello again,
> 
> I'm sending the new version of my TPCI200 and IP-Octal 232 patches,
[snip]

You may want to resend v2 as inline patches - Thunderbird for instance
doesn't allow to quote / comment on attachments.

Andreas

-- 
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH v3 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
                   ` (5 preceding siblings ...)
  2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 2/2] Add GE IP-Octal 232 " Alberto Garcia
@ 2012-12-05 15:24 ` Alberto Garcia
  2012-12-17 15:27   ` [Qemu-devel] Ping " Alberto Garcia
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 1/2] Add TEWS TPCI200 " Alberto Garcia
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 2/2] Add GE IP-Octal 232 " Alberto Garcia
  8 siblings, 1 reply; 27+ messages in thread
From: Alberto Garcia @ 2012-12-05 15:24 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anthony Liguori, Alberto Garcia, Blue Swirl, Avi Kivity,
	Paolo Bonzini, Andreas Färber

As requested by Andreas, I'm sending them again as inline patches.

I also changed the name of the VMStateDescription struct from ipoctal
to ipoctal232. Otherwise they're the same.

Regards,

Alberto Garcia (2):
  Add TEWS TPCI200 IndustryPack emulation
  Add GE IP-Octal 232 IndustryPack emulation

 default-configs/pci.mak |    1 +
 hw/Makefile.objs        |    3 +
 hw/ipack.c              |  106 ++++++++
 hw/ipack.h              |   75 ++++++
 hw/ipoctal.c            |  613 +++++++++++++++++++++++++++++++++++++++++++
 hw/pci_ids.h            |    3 +
 hw/tpci200.c            |  667 +++++++++++++++++++++++++++++++++++++++++++++++
 7 ficheiros modificados, 1468 adições(+)
 create mode 100644 hw/ipack.c
 create mode 100644 hw/ipack.h
 create mode 100644 hw/ipoctal.c
 create mode 100644 hw/tpci200.c

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH v3 1/2] Add TEWS TPCI200 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
                   ` (6 preceding siblings ...)
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 0/2] Add TPCI200 and " Alberto Garcia
@ 2012-12-05 15:24 ` Alberto Garcia
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 2/2] Add GE IP-Octal 232 " Alberto Garcia
  8 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-12-05 15:24 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anthony Liguori, Alberto Garcia, Blue Swirl, Avi Kivity,
	Paolo Bonzini, Andreas Färber

The TPCI200 is a PCI board that supports up to 4 IndustryPack modules.

A new bus type called 'IndustryPack' has been created so any
compatible module can be attached to this board.

Signed-off-by: Alberto Garcia <agarcia@igalia.com>
---
 default-configs/pci.mak |    1 +
 hw/Makefile.objs        |    3 +
 hw/ipack.c              |  106 ++++++++
 hw/ipack.h              |   75 ++++++
 hw/pci_ids.h            |    3 +
 hw/tpci200.c            |  667 +++++++++++++++++++++++++++++++++++++++++++++++
 6 ficheiros modificados, 855 adições(+)
 create mode 100644 hw/ipack.c
 create mode 100644 hw/ipack.h
 create mode 100644 hw/tpci200.c

diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index ae9d1eb..ee2d18d 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -21,3 +21,4 @@ CONFIG_ESP=y
 CONFIG_ESP_PCI=y
 CONFIG_SERIAL=y
 CONFIG_SERIAL_PCI=y
+CONFIG_IPACK=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index d581d8d..2ce5ec0 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -104,6 +104,9 @@ common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 
 common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
 
+# IndustryPack
+common-obj-$(CONFIG_IPACK) += tpci200.o ipack.o
+
 # PCI network cards
 common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
 common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
diff --git a/hw/ipack.c b/hw/ipack.c
new file mode 100644
index 0000000..59e272b
--- /dev/null
+++ b/hw/ipack.c
@@ -0,0 +1,106 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "ipack.h"
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
+{
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        IPackDevice *ip = DO_UPCAST(IPackDevice, qdev, qdev);
+        if (ip->slot == slot) {
+            return ip;
+        }
+    }
+    return NULL;
+}
+
+static int ipack_device_dev_init(DeviceState *qdev)
+{
+    IPackBus *bus = DO_UPCAST(IPackBus, qbus, qdev->parent_bus);
+    IPackDevice *dev = DO_UPCAST(IPackDevice, qdev, qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (dev->slot < 0) {
+        dev->slot = bus->free_slot;
+    }
+    if (dev->slot >= bus->n_slots) {
+        return -1;
+    }
+    bus->free_slot = dev->slot + 1;
+
+    dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2);
+
+    return k->init(dev);
+}
+
+static int ipack_device_dev_exit(DeviceState *qdev)
+{
+    IPackDevice *dev = DO_UPCAST(IPackDevice, qdev, qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (k->exit) {
+        k->exit(dev);
+    }
+
+    qemu_free_irqs(dev->irq);
+
+    return 0;
+}
+
+static Property ipack_device_props[] = {
+    DEFINE_PROP_INT32("slot", IPackDevice, slot, -1),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void ipack_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_type = TYPE_IPACK_BUS;
+    k->init = ipack_device_dev_init;
+    k->exit = ipack_device_dev_exit;
+    k->props = ipack_device_props;
+}
+
+const VMStateDescription vmstate_ipack_device = {
+    .name = "ipack_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(slot, IPackDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const TypeInfo ipack_device_info = {
+    .name          = TYPE_IPACK_DEVICE,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(IPackDevice),
+    .class_size    = sizeof(IPackDeviceClass),
+    .class_init    = ipack_device_class_init,
+    .abstract      = true,
+};
+
+static const TypeInfo ipack_bus_info = {
+    .name = TYPE_IPACK_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(IPackBus),
+};
+
+static void ipack_register_types(void)
+{
+    type_register_static(&ipack_device_info);
+    type_register_static(&ipack_bus_info);
+}
+
+type_init(ipack_register_types)
diff --git a/hw/ipack.h b/hw/ipack.h
new file mode 100644
index 0000000..61a9019
--- /dev/null
+++ b/hw/ipack.h
@@ -0,0 +1,75 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#ifndef QEMU_IPACK_H
+#define QEMU_IPACK_H
+
+#include "qdev.h"
+
+typedef struct IPackBus IPackBus;
+
+#define TYPE_IPACK_BUS "IndustryPack"
+#define IPACK_BUS(obj) OBJECT_CHECK(IPackBus, (obj), TYPE_IPACK_BUS)
+
+struct IPackBus {
+    BusState qbus;
+    uint8_t n_slots;
+    uint8_t free_slot;
+    qemu_irq_handler set_irq;
+};
+
+typedef struct IPackDevice IPackDevice;
+typedef struct IPackDeviceClass IPackDeviceClass;
+
+#define TYPE_IPACK_DEVICE "ipack-device"
+#define IPACK_DEVICE(obj) \
+     OBJECT_CHECK(IPackDevice, (obj), TYPE_IPACK_DEVICE)
+#define IPACK_DEVICE_CLASS(klass)                                        \
+     OBJECT_CLASS_CHECK(IPackDeviceClass, (klass), TYPE_IPACK_DEVICE)
+#define IPACK_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(IPackDeviceClass, (obj), TYPE_IPACK_DEVICE)
+
+struct IPackDeviceClass {
+    DeviceClass parent_class;
+
+    int (*init)(IPackDevice *dev);
+    int (*exit)(IPackDevice *dev);
+
+    uint16_t (*io_read)(IPackDevice *dev, uint8_t addr);
+    void (*io_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*id_read)(IPackDevice *dev, uint8_t addr);
+    void (*id_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*int_read)(IPackDevice *dev, uint8_t addr);
+    void (*int_write)(IPackDevice *dev, uint8_t addr, uint16_t val);
+
+    uint16_t (*mem_read16)(IPackDevice *dev, uint32_t addr);
+    void (*mem_write16)(IPackDevice *dev, uint32_t addr, uint16_t val);
+
+    uint8_t (*mem_read8)(IPackDevice *dev, uint32_t addr);
+    void (*mem_write8)(IPackDevice *dev, uint32_t addr, uint8_t val);
+};
+
+struct IPackDevice {
+    DeviceState qdev;
+    int32_t slot;
+    /* IRQ objects for the IndustryPack INT0# and INT1# */
+    qemu_irq *irq;
+};
+
+extern const VMStateDescription vmstate_ipack_device;
+
+#define VMSTATE_IPACK_DEVICE(_field, _state)                            \
+    VMSTATE_STRUCT(_field, _state, 1, vmstate_ipack_device, IPackDevice)
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot);
+
+#endif
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 5df7245..38d6af5 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -145,3 +145,6 @@
 
 #define PCI_VENDOR_ID_NEC                0x1033
 #define PCI_DEVICE_ID_NEC_UPD720200      0x0194
+
+#define PCI_VENDOR_ID_TEWS               0x1498
+#define PCI_DEVICE_ID_TEWS_TPCI200       0x30C8
diff --git a/hw/tpci200.c b/hw/tpci200.c
new file mode 100644
index 0000000..f63f916
--- /dev/null
+++ b/hw/tpci200.c
@@ -0,0 +1,667 @@
+/*
+ * QEMU TEWS TPCI200 IndustryPack carrier emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "pci.h"
+#include "ipack.h"
+#include "bitops.h"
+#include <stdio.h>
+
+/* #define DEBUG_TPCI */
+
+#ifdef DEBUG_TPCI
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define N_MODULES 4
+
+#define IP_ID_SPACE  2
+#define IP_INT_SPACE 3
+#define IP_IO_SPACE_ADDR_MASK  0x7F
+#define IP_ID_SPACE_ADDR_MASK  0x3F
+#define IP_INT_SPACE_ADDR_MASK 0x3F
+
+#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO))
+#define STATUS_TIMEOUT(IP)    BIT((IP) + 12)
+#define STATUS_ERR_ANY        0xF00
+
+#define CTRL_CLKRATE          BIT(0)
+#define CTRL_RECOVER          BIT(1)
+#define CTRL_TIME_INT         BIT(2)
+#define CTRL_ERR_INT          BIT(3)
+#define CTRL_INT_EDGE(INTNO)  BIT(4 + (INTNO))
+#define CTRL_INT(INTNO)       BIT(6 + (INTNO))
+
+#define REG_REV_ID    0x00
+#define REG_IP_A_CTRL 0x02
+#define REG_IP_B_CTRL 0x04
+#define REG_IP_C_CTRL 0x06
+#define REG_IP_D_CTRL 0x08
+#define REG_RESET     0x0A
+#define REG_STATUS    0x0C
+#define IP_N_FROM_REG(REG) ((REG) / 2 - 1)
+
+typedef struct {
+    PCIDevice dev;
+    IPackBus bus;
+    MemoryRegion mmio;
+    MemoryRegion io;
+    MemoryRegion las0;
+    MemoryRegion las1;
+    MemoryRegion las2;
+    MemoryRegion las3;
+    bool big_endian[3];
+    uint8_t ctrl[N_MODULES];
+    uint16_t status;
+    uint8_t int_set;
+} TPCI200State;
+
+static const uint8_t local_config_regs[] = {
+    0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00,
+    0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4,
+    0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01,
+    0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02,
+    0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41,
+    0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02
+};
+
+static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size)
+{
+    /* During 8 bit access in big endian mode,
+       odd and even addresses are swapped */
+    if (big_endian && size == 1) {
+        *addr ^= 1;
+    }
+}
+
+static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size)
+{
+    /* Local spaces only support 8/16 bit access,
+     * so there's no need to care for sizes > 2 */
+    if (big_endian && size == 2) {
+        *val = bswap16(*val);
+    }
+    return *val;
+}
+
+static void tpci200_set_irq(void *opaque, int intno, int level)
+{
+    IPackDevice *ip = opaque;
+    IPackBus *bus = DO_UPCAST(IPackBus, qbus, ip->qdev.parent_bus);
+    PCIDevice *pcidev = DO_UPCAST(PCIDevice, qdev, bus->qbus.parent);
+    TPCI200State *dev = DO_UPCAST(TPCI200State, dev, pcidev);
+    unsigned ip_n = ip->slot;
+    uint16_t prev_status = dev->status;
+
+    assert(ip->slot >= 0 && ip->slot < N_MODULES);
+
+    /* The requested interrupt must be enabled in the IP CONTROL
+     * register */
+    if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) {
+        return;
+    }
+
+    /* Update the interrupt status in the IP STATUS register */
+    if (level) {
+        dev->status |=  STATUS_INT(ip_n, intno);
+    } else {
+        dev->status &= ~STATUS_INT(ip_n, intno);
+    }
+
+    /* Return if there are no changes */
+    if (dev->status == prev_status) {
+        return;
+    }
+
+    DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level);
+
+    /* Check if the interrupt is edge sensitive */
+    if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) {
+        if (level) {
+            qemu_set_irq(dev->dev.irq[0], !dev->int_set);
+            qemu_set_irq(dev->dev.irq[0],  dev->int_set);
+        }
+    } else {
+        unsigned i, j;
+        uint16_t level_status = dev->status;
+
+        /* Check if there are any level sensitive interrupts set by
+           removing the ones that are edge sensitive from the status
+           register */
+        for (i = 0; i < N_MODULES; i++) {
+            for (j = 0; j < 2; j++) {
+                if (dev->ctrl[i] & CTRL_INT_EDGE(j)) {
+                    level_status &= ~STATUS_INT(i, j);
+                }
+            }
+        }
+
+        if (level_status && !dev->int_set) {
+            qemu_irq_raise(dev->dev.irq[0]);
+            dev->int_set = 1;
+        } else if (!level_status && dev->int_set) {
+            qemu_irq_lower(dev->dev.irq[0]);
+            dev->int_set = 0;
+        }
+    }
+}
+
+static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint8_t ret = 0;
+    if (addr < ARRAY_SIZE(local_config_regs)) {
+        ret = local_config_regs[addr];
+    }
+    /* Endianness is stored in the first bit of these registers */
+    if ((addr == 0x2b && s->big_endian[0]) ||
+        (addr == 0x2f && s->big_endian[1]) ||
+        (addr == 0x33 && s->big_endian[2])) {
+        ret |= 1;
+    }
+    DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret);
+    return ret;
+}
+
+static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned size)
+{
+    TPCI200State *s = opaque;
+    /* Endianness is stored in the first bit of these registers */
+    if (addr == 0x2b || addr == 0x2f || addr == 0x33) {
+        unsigned las = (addr - 0x2b) / 4;
+        s->big_endian[las] = val & 1;
+        DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1);
+    } else {
+        DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val);
+    }
+}
+
+static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint64_t ret = 0;
+
+    switch (addr) {
+
+    case REG_REV_ID:
+        DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */
+        break;
+
+    case REG_IP_A_CTRL:
+    case REG_IP_B_CTRL:
+    case REG_IP_C_CTRL:
+    case REG_IP_D_CTRL:
+        {
+            unsigned ip_n = IP_N_FROM_REG(addr);
+            ret = s->ctrl[ip_n];
+            DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret);
+        }
+        break;
+
+    case REG_RESET:
+        DPRINTF("Read RESET\n"); /* Not implemented */
+        break;
+
+    case REG_STATUS:
+        ret = s->status;
+        DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret);
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr);
+        break;
+    }
+
+    return adjust_value(s->big_endian[0], &ret, size);
+}
+
+static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+
+    adjust_value(s->big_endian[0], &val, size);
+
+    switch (addr) {
+
+    case REG_REV_ID:
+        DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */
+        break;
+
+    case REG_IP_A_CTRL:
+    case REG_IP_B_CTRL:
+    case REG_IP_C_CTRL:
+    case REG_IP_D_CTRL:
+        {
+            unsigned ip_n = IP_N_FROM_REG(addr);
+            s->ctrl[ip_n] = val;
+            DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val);
+        }
+        break;
+
+    case REG_RESET:
+        DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */
+        break;
+
+    case REG_STATUS:
+        {
+            unsigned i;
+
+            for (i = 0; i < N_MODULES; i++) {
+                IPackDevice *ip = ipack_device_find(&s->bus, i);
+
+                if (ip != NULL) {
+                    if (val & STATUS_INT(i, 0)) {
+                        DPRINTF("Clear IP %c INT0# status\n", 'A' + i);
+                        qemu_irq_lower(ip->irq[0]);
+                    }
+                    if (val & STATUS_INT(i, 1)) {
+                        DPRINTF("Clear IP %c INT1# status\n", 'A' + i);
+                        qemu_irq_lower(ip->irq[1]);
+                    }
+                }
+
+                if (val & STATUS_TIMEOUT(i)) {
+                    DPRINTF("Clear IP %c timeout\n", 'A' + i);
+                    s->status &= ~STATUS_TIMEOUT(i);
+                }
+            }
+
+            if (val & STATUS_ERR_ANY) {
+                DPRINTF("Unexpected write to STATUS register: 0x%x\n",
+                        (unsigned) val);
+            }
+        }
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
+                (unsigned) addr, (unsigned) val);
+        break;
+    }
+}
+
+static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+
+    /*
+     * The address is divided into the IP module number (0-4), the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_read) {
+                ret = k->id_read(ip, offset);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+
+            /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
+            if (offset == 0 || offset == 2) {
+                unsigned intno = offset / 2;
+                bool int_set = s->status & STATUS_INT(ip_n, intno);
+                bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
+                if (int_set && !int_edge_sensitive) {
+                    qemu_irq_lower(ip->irq[intno]);
+                }
+            }
+
+            if (k->int_read) {
+                ret = k->int_read(ip, offset);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_read) {
+                ret = k->io_read(ip, offset);
+            }
+            break;
+        }
+    }
+
+    return adjust_value(s->big_endian[1], &ret, size);
+}
+
+static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+    adjust_value(s->big_endian[1], &val, size);
+
+    /*
+     * The address is divided into the IP module number, the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_write) {
+                k->id_write(ip, offset, val);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+            if (k->int_write) {
+                k->int_write(ip, offset, val);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_write) {
+                k->io_write(ip, offset, val);
+            }
+            break;
+        }
+    }
+}
+
+static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read16) {
+            ret = k->mem_read16(ip, offset);
+        }
+    }
+
+    return adjust_value(s->big_endian[2], &ret, size);
+}
+
+static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+    adjust_value(s->big_endian[2], &val, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write16) {
+            k->mem_write16(ip, offset, val);
+        }
+    }
+}
+
+static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read8) {
+            ret = k->mem_read8(ip, offset);
+        }
+    }
+
+    return ret;
+}
+
+static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write8) {
+            k->mem_write8(ip, offset, val);
+        }
+    }
+}
+
+static const MemoryRegionOps tpci200_cfg_ops = {
+    .read = tpci200_read_cfg,
+    .write = tpci200_write_cfg,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 4
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static const MemoryRegionOps tpci200_las0_ops = {
+    .read = tpci200_read_las0,
+    .write = tpci200_write_las0,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 2,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las1_ops = {
+    .read = tpci200_read_las1,
+    .write = tpci200_write_las1,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las2_ops = {
+    .read = tpci200_read_las2,
+    .write = tpci200_write_las2,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las3_ops = {
+    .read = tpci200_read_las3,
+    .write = tpci200_write_las3,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static int tpci200_initfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = DO_UPCAST(TPCI200State, dev, pci_dev);
+    uint8_t *c = s->dev.config;
+
+    pci_set_word(c + PCI_COMMAND, 0x0003);
+    pci_set_word(c + PCI_STATUS,  0x0280);
+
+    pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
+
+    pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
+    pci_set_long(c + 0x40, 0x48014801);
+    pci_set_long(c + 0x48, 0x00024C06);
+    pci_set_long(c + 0x4C, 0x00000003);
+
+    memory_region_init_io(&s->mmio, &tpci200_cfg_ops,
+                          s, "tpci200_mmio", 128);
+    memory_region_init_io(&s->io,   &tpci200_cfg_ops,
+                          s, "tpci200_io",   128);
+    memory_region_init_io(&s->las0, &tpci200_las0_ops,
+                          s, "tpci200_las0", 256);
+    memory_region_init_io(&s->las1, &tpci200_las1_ops,
+                          s, "tpci200_las1", 1024);
+    memory_region_init_io(&s->las2, &tpci200_las2_ops,
+                          s, "tpci200_las2", 1024*1024*32);
+    memory_region_init_io(&s->las3, &tpci200_las3_ops,
+                          s, "tpci200_las3", 1024*1024*16);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
+    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);
+    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
+    pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
+    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
+    pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
+
+    qbus_create_inplace(&s->bus.qbus, TYPE_IPACK_BUS, &s->dev.qdev, NULL);
+    s->bus.n_slots = N_MODULES;
+    s->bus.set_irq = tpci200_set_irq;
+
+    return 0;
+}
+
+static void tpci200_exitfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = DO_UPCAST(TPCI200State, dev, pci_dev);
+
+    memory_region_destroy(&s->mmio);
+    memory_region_destroy(&s->io);
+    memory_region_destroy(&s->las0);
+    memory_region_destroy(&s->las1);
+    memory_region_destroy(&s->las2);
+    memory_region_destroy(&s->las3);
+}
+
+static const VMStateDescription vmstate_tpci200 = {
+    .name = "tpci200",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, TPCI200State),
+        VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
+        VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES),
+        VMSTATE_UINT16(status, TPCI200State),
+        VMSTATE_UINT8(int_set, TPCI200State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void tpci200_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = tpci200_initfn;
+    k->exit = tpci200_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_TEWS;
+    k->device_id = PCI_DEVICE_ID_TEWS_TPCI200;
+    k->class_id = PCI_CLASS_BRIDGE_OTHER;
+    k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS;
+    k->subsystem_id = 0x300A;
+    dc->desc = "TEWS TPCI200 IndustryPack carrier";
+    dc->vmsd = &vmstate_tpci200;
+}
+
+static const TypeInfo tpci200_info = {
+    .name          = "tpci200",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(TPCI200State),
+    .class_init    = tpci200_class_init,
+};
+
+static void tpci200_register_types(void)
+{
+    type_register_static(&tpci200_info);
+}
+
+type_init(tpci200_register_types)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [Qemu-devel] [PATCH v3 2/2] Add GE IP-Octal 232 IndustryPack emulation
  2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
                   ` (7 preceding siblings ...)
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 1/2] Add TEWS TPCI200 " Alberto Garcia
@ 2012-12-05 15:24 ` Alberto Garcia
  8 siblings, 0 replies; 27+ messages in thread
From: Alberto Garcia @ 2012-12-05 15:24 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anthony Liguori, Alberto Garcia, Blue Swirl, Avi Kivity,
	Paolo Bonzini, Andreas Färber

The GE IP-Octal 232 is an IndustryPack module that implements eight
RS-232 serial ports, each one of which can be redirected to a
character device in the host.

Signed-off-by: Alberto Garcia <agarcia@igalia.com>
---
 hw/Makefile.objs |    2 +-
 hw/ipoctal.c     |  613 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 ficheiros modificados, 614 adições(+), 1 eliminado(-)
 create mode 100644 hw/ipoctal.c

diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 2ce5ec0..b43a67e 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -105,7 +105,7 @@ common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
 common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
 
 # IndustryPack
-common-obj-$(CONFIG_IPACK) += tpci200.o ipack.o
+common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal.o ipack.o
 
 # PCI network cards
 common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
diff --git a/hw/ipoctal.c b/hw/ipoctal.c
new file mode 100644
index 0000000..31a4d42
--- /dev/null
+++ b/hw/ipoctal.c
@@ -0,0 +1,613 @@
+/*
+ * QEMU GE IP-Octal 232 IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "ipack.h"
+#include "bitops.h"
+
+/* #define DEBUG_IPOCTAL */
+
+#ifdef DEBUG_IPOCTAL
+#define DPRINTF2(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF2(fmt, ...) do { } while (0)
+#endif
+
+#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
+
+#define RX_FIFO_SIZE 3
+
+/* The IP-Octal has 8 channels (a-h)
+   divided into 4 blocks (A-D) */
+#define N_CHANNELS 8
+#define N_BLOCKS   4
+
+#define REG_MRa  0x01
+#define REG_MRb  0x11
+#define REG_SRa  0x03
+#define REG_SRb  0x13
+#define REG_CSRa 0x03
+#define REG_CSRb 0x13
+#define REG_CRa  0x05
+#define REG_CRb  0x15
+#define REG_RHRa 0x07
+#define REG_RHRb 0x17
+#define REG_THRa 0x07
+#define REG_THRb 0x17
+#define REG_ACR  0x09
+#define REG_ISR  0x0B
+#define REG_IMR  0x0B
+#define REG_OPCR 0x1B
+
+#define CR_ENABLE_RX    BIT(0)
+#define CR_DISABLE_RX   BIT(1)
+#define CR_ENABLE_TX    BIT(2)
+#define CR_DISABLE_TX   BIT(3)
+#define CR_CMD(cr)      ((cr) >> 4)
+#define CR_NO_OP        0
+#define CR_RESET_MR     1
+#define CR_RESET_RX     2
+#define CR_RESET_TX     3
+#define CR_RESET_ERR    4
+#define CR_RESET_BRKINT 5
+#define CR_START_BRK    6
+#define CR_STOP_BRK     7
+#define CR_ASSERT_RTSN  8
+#define CR_NEGATE_RTSN  9
+#define CR_TIMEOUT_ON   10
+#define CR_TIMEOUT_OFF  12
+
+#define SR_RXRDY   BIT(0)
+#define SR_FFULL   BIT(1)
+#define SR_TXRDY   BIT(2)
+#define SR_TXEMT   BIT(3)
+#define SR_OVERRUN BIT(4)
+#define SR_PARITY  BIT(5)
+#define SR_FRAMING BIT(6)
+#define SR_BREAK   BIT(7)
+
+#define ISR_TXRDYA BIT(0)
+#define ISR_RXRDYA BIT(1)
+#define ISR_BREAKA BIT(2)
+#define ISR_CNTRDY BIT(3)
+#define ISR_TXRDYB BIT(4)
+#define ISR_RXRDYB BIT(5)
+#define ISR_BREAKB BIT(6)
+#define ISR_MPICHG BIT(7)
+#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0))
+#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1))
+#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2))
+
+typedef struct IPOctalState IPOctalState;
+typedef struct SCC2698Channel SCC2698Channel;
+typedef struct SCC2698Block SCC2698Block;
+
+struct SCC2698Channel {
+    IPOctalState *ipoctal;
+    CharDriverState *dev;
+    char *devpath;
+    bool rx_enabled;
+    uint8_t mr[2];
+    uint8_t mr_idx;
+    uint8_t sr;
+    uint8_t rhr[RX_FIFO_SIZE];
+    uint8_t rhr_idx;
+    uint8_t rx_pending;
+};
+
+struct SCC2698Block {
+    uint8_t imr;
+    uint8_t isr;
+};
+
+struct IPOctalState {
+    IPackDevice dev;
+    SCC2698Channel ch[N_CHANNELS];
+    SCC2698Block blk[N_BLOCKS];
+    uint8_t irq_vector;
+};
+
+static const VMStateDescription vmstate_scc2698_channel = {
+    .name = "scc2698_channel",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(rx_enabled, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
+        VMSTATE_UINT8(mr_idx, SCC2698Channel),
+        VMSTATE_UINT8(sr, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE),
+        VMSTATE_UINT8(rhr_idx, SCC2698Channel),
+        VMSTATE_UINT8(rx_pending, SCC2698Channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_scc2698_block = {
+    .name = "scc2698_block",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(imr, SCC2698Block),
+        VMSTATE_UINT8(isr, SCC2698Block),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_ipoctal = {
+    .name = "ipoctal232",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_IPACK_DEVICE(dev, IPOctalState),
+        VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
+                             vmstate_scc2698_channel, SCC2698Channel),
+        VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
+                             vmstate_scc2698_block, SCC2698Block),
+        VMSTATE_UINT8(irq_vector, IPOctalState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* data[10] is 0x0C, not 0x0B as the doc says */
+static const uint8_t id_prom_data[] = {
+    0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
+    0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
+};
+
+static void update_irq(IPOctalState *dev, unsigned block)
+{
+    /* Blocks A and B interrupt on INT0#, C and D on INT1#.
+       Thus, to get the status we have to check two blocks. */
+    SCC2698Block *blk0 = &dev->blk[block];
+    SCC2698Block *blk1 = &dev->blk[block^1];
+    unsigned intno = block / 2;
+
+    if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) {
+        qemu_irq_raise(dev->dev.irq[intno]);
+    } else {
+        qemu_irq_lower(dev->dev.irq[intno]);
+    }
+}
+
+static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
+{
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[channel / 2];
+
+    DPRINTF("Write CR%c %u: ", channel + 'a', val);
+
+    /* The lower 4 bits are used to enable and disable Tx and Rx */
+    if (val & CR_ENABLE_RX) {
+        DPRINTF2("Rx on, ");
+        ch->rx_enabled = true;
+    }
+    if (val & CR_DISABLE_RX) {
+        DPRINTF2("Rx off, ");
+        ch->rx_enabled = false;
+    }
+    if (val & CR_ENABLE_TX) {
+        DPRINTF2("Tx on, ");
+        ch->sr |= SR_TXRDY | SR_TXEMT;
+        blk->isr |= ISR_TXRDY(channel);
+    }
+    if (val & CR_DISABLE_TX) {
+        DPRINTF2("Tx off, ");
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+        blk->isr &= ~ISR_TXRDY(channel);
+    }
+
+    DPRINTF2("cmd: ");
+
+    /* The rest of the bits implement different commands */
+    switch (CR_CMD(val)) {
+    case CR_NO_OP:
+        DPRINTF2("none");
+        break;
+    case CR_RESET_MR:
+        DPRINTF2("reset MR");
+        ch->mr_idx = 0;
+        break;
+    case CR_RESET_RX:
+        DPRINTF2("reset Rx");
+        ch->rx_enabled = false;
+        ch->rx_pending = 0;
+        ch->sr &= ~SR_RXRDY;
+        blk->isr &= ~ISR_RXRDY(channel);
+        break;
+    case CR_RESET_TX:
+        DPRINTF2("reset Tx");
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+        blk->isr &= ~ISR_TXRDY(channel);
+        break;
+    case CR_RESET_ERR:
+        DPRINTF2("reset err");
+        ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
+        break;
+    case CR_RESET_BRKINT:
+        DPRINTF2("reset brk ch int");
+        blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
+        break;
+    default:
+        DPRINTF2("unsupported 0x%x", CR_CMD(val));
+    }
+
+    DPRINTF2("\n");
+}
+
+static uint16_t io_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    uint16_t ret = 0;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    uint8_t old_isr = blk->isr;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ret = ch->mr[ch->mr_idx];
+        DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
+        ch->mr_idx = 1;
+        break;
+
+    case REG_SRa:
+    case REG_SRb:
+        ret = ch->sr;
+        DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
+        break;
+
+    case REG_RHRa:
+    case REG_RHRb:
+        ret = ch->rhr[ch->rhr_idx];
+        if (ch->rx_pending > 0) {
+            ch->rx_pending--;
+            if (ch->rx_pending == 0) {
+                ch->sr &= ~SR_RXRDY;
+                blk->isr &= ~ISR_RXRDY(channel);
+                if (ch->dev) {
+                    qemu_chr_accept_input(ch->dev);
+                }
+            } else {
+                ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
+            }
+            if (ch->sr & SR_BREAK) {
+                ch->sr &= ~SR_BREAK;
+                blk->isr |= ISR_BREAK(channel);
+            }
+        }
+        DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret);
+        break;
+
+    case REG_ISR:
+        ret = blk->isr;
+        DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
+        break;
+
+    default:
+        DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
+    }
+
+    if (old_isr != blk->isr) {
+        update_irq(dev, block);
+    }
+
+    return ret;
+}
+
+static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    unsigned reg = val & 0xFF;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    uint8_t old_isr = blk->isr;
+    uint8_t old_imr = blk->imr;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ch->mr[ch->mr_idx] = reg;
+        DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
+        ch->mr_idx = 1;
+        break;
+
+    /* Not implemented */
+    case REG_CSRa:
+    case REG_CSRb:
+        DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
+        break;
+
+    case REG_CRa:
+    case REG_CRb:
+        write_cr(dev, channel, reg);
+        break;
+
+    case REG_THRa:
+    case REG_THRb:
+        if (ch->sr & SR_TXRDY) {
+            DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
+            if (ch->dev) {
+                uint8_t thr = reg;
+                qemu_chr_fe_write(ch->dev, &thr, 1);
+            }
+        } else {
+            DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
+        }
+        break;
+
+    /* Not implemented */
+    case REG_ACR:
+        DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
+        break;
+
+    case REG_IMR:
+        DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
+        blk->imr = reg;
+        break;
+
+    /* Not implemented */
+    case REG_OPCR:
+        DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
+        break;
+
+    default:
+        DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
+    }
+
+    if (old_isr != blk->isr || old_imr != blk->imr) {
+        update_irq(dev, block);
+    }
+}
+
+static uint16_t id_read(IPackDevice *ip, uint8_t addr)
+{
+    uint16_t ret = 0;
+    unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
+
+    if (pos < ARRAY_SIZE(id_prom_data)) {
+        ret = id_prom_data[pos];
+    } else {
+        DPRINTF("Attempt to read unavailable PROM data at 0x%x\n",  addr);
+    }
+
+    return ret;
+}
+
+static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val; /* Undocumented, but the hw works like that */
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static uint16_t int_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */
+    if (addr != 0 && addr != 2) {
+        DPRINTF("Attempt to read from 0x%x\n", addr);
+        return 0;
+    } else {
+        /* Update interrupts if necessary */
+        update_irq(dev, addr);
+        return dev->irq_vector;
+    }
+}
+
+static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
+{
+    IPOctalState *dev = DO_UPCAST(IPOctalState, dev, ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val;
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static int hostdev_can_receive(void *opaque)
+{
+    SCC2698Channel *ch = opaque;
+    int available_bytes = RX_FIFO_SIZE - ch->rx_pending;
+    return ch->rx_enabled ? available_bytes : 0;
+}
+
+static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
+{
+    SCC2698Channel *ch = opaque;
+    IPOctalState *dev = ch->ipoctal;
+    unsigned pos = ch->rhr_idx + ch->rx_pending;
+    int i;
+
+    assert(size + ch->rx_pending <= RX_FIFO_SIZE);
+
+    /* Copy data to the RxFIFO */
+    for (i = 0; i < size; i++) {
+        pos %= RX_FIFO_SIZE;
+        ch->rhr[pos++] = buf[i];
+    }
+
+    ch->rx_pending += size;
+
+    /* If the RxFIFO was empty raise an interrupt */
+    if (!(ch->sr & SR_RXRDY)) {
+        unsigned block, channel = 0;
+        /* Find channel number to update the ISR register */
+        while (&dev->ch[channel] != ch) {
+            channel++;
+        }
+        block = channel / 2;
+        dev->blk[block].isr |= ISR_RXRDY(channel);
+        ch->sr |= SR_RXRDY;
+        update_irq(dev, block);
+    }
+}
+
+static void hostdev_event(void *opaque, int event)
+{
+    SCC2698Channel *ch = opaque;
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        DPRINTF("Device %s opened\n", ch->dev->label);
+        break;
+    case CHR_EVENT_BREAK: {
+        uint8_t zero = 0;
+        DPRINTF("Device %s received break\n", ch->dev->label);
+
+        if (!(ch->sr & SR_BREAK)) {
+            IPOctalState *dev = ch->ipoctal;
+            unsigned block, channel = 0;
+
+            while (&dev->ch[channel] != ch) {
+                channel++;
+            }
+            block = channel / 2;
+
+            ch->sr |= SR_BREAK;
+            dev->blk[block].isr |= ISR_BREAK(channel);
+        }
+
+        /* Put a zero character in the buffer */
+        hostdev_receive(ch, &zero, 1);
+    }
+        break;
+    default:
+        DPRINTF("Device %s received event %d\n", ch->dev->label, event);
+    }
+}
+
+static int ipoctal_init(IPackDevice *ip)
+{
+    IPOctalState *s = DO_UPCAST(IPOctalState, dev, ip);
+    unsigned i;
+
+    for (i = 0; i < N_CHANNELS; i++) {
+        SCC2698Channel *ch = &s->ch[i];
+        ch->ipoctal = s;
+
+        /* Redirect IP-Octal channels to host character devices */
+        if (ch->devpath) {
+            const char chr_name[] = "ipoctal";
+            char label[ARRAY_SIZE(chr_name) + 2];
+            static int index;
+
+            snprintf(label, sizeof(label), "%s%d", chr_name, index);
+
+            ch->dev = qemu_chr_new(label, ch->devpath, NULL);
+
+            if (ch->dev) {
+                index++;
+                qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
+                                      hostdev_receive, hostdev_event, ch);
+                DPRINTF("Redirecting channel %u to %s (%s)\n",
+                        i, ch->devpath, label);
+            } else {
+                DPRINTF("Could not redirect channel %u to %s\n",
+                        i, ch->devpath);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static Property ipoctal_properties[] = {
+    DEFINE_PROP_STRING("serial0", IPOctalState, ch[0].devpath),
+    DEFINE_PROP_STRING("serial1", IPOctalState, ch[1].devpath),
+    DEFINE_PROP_STRING("serial2", IPOctalState, ch[2].devpath),
+    DEFINE_PROP_STRING("serial3", IPOctalState, ch[3].devpath),
+    DEFINE_PROP_STRING("serial4", IPOctalState, ch[4].devpath),
+    DEFINE_PROP_STRING("serial5", IPOctalState, ch[5].devpath),
+    DEFINE_PROP_STRING("serial6", IPOctalState, ch[6].devpath),
+    DEFINE_PROP_STRING("serial7", IPOctalState, ch[7].devpath),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipoctal_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
+
+    ic->init        = ipoctal_init;
+    ic->io_read     = io_read;
+    ic->io_write    = io_write;
+    ic->id_read     = id_read;
+    ic->id_write    = id_write;
+    ic->int_read    = int_read;
+    ic->int_write   = int_write;
+    ic->mem_read16  = mem_read16;
+    ic->mem_write16 = mem_write16;
+    ic->mem_read8   = mem_read8;
+    ic->mem_write8  = mem_write8;
+
+    dc->desc    = "GE IP-Octal 232 8-channel RS-232 IndustryPack";
+    dc->props   = ipoctal_properties;
+    dc->vmsd    = &vmstate_ipoctal;
+}
+
+static const TypeInfo ipoctal_info = {
+    .name          = "ipoctal232",
+    .parent        = TYPE_IPACK_DEVICE,
+    .instance_size = sizeof(IPOctalState),
+    .class_init    = ipoctal_class_init,
+};
+
+static void ipoctal_register_types(void)
+{
+    type_register_static(&ipoctal_info);
+}
+
+type_init(ipoctal_register_types)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH v3 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 0/2] Add TPCI200 and " Alberto Garcia
@ 2012-12-17 15:27   ` Alberto Garcia
  2013-01-07 20:32     ` Anthony Liguori
  0 siblings, 1 reply; 27+ messages in thread
From: Alberto Garcia @ 2012-12-17 15:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: Blue Swirl, Paolo Bonzini, Anthony Liguori, Avi Kivity,
	Andreas Färber

Ping,

Here's a QEMU image (124 MB) of a Debian system with the tpci200 and
ipoctal drivers, in case you want to try them out:

http://dl.dropbox.com/u/1667385/qemu-ipoctal.qcow2

The root password is just 'root'.

The patches for QEMU are here:

http://patchwork.ozlabs.org/patch/203892/
http://patchwork.ozlabs.org/patch/203896/

And you need to run it with these options:

-device tpci200 -device ipoctal232,serialX=DEV

where X is a channel number (from 0 to 7) and DEV a host character
device (e.g. "pty").

Regards,

Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [Qemu-devel] Ping [PATCH v3 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation
  2012-12-17 15:27   ` [Qemu-devel] Ping " Alberto Garcia
@ 2013-01-07 20:32     ` Anthony Liguori
  0 siblings, 0 replies; 27+ messages in thread
From: Anthony Liguori @ 2013-01-07 20:32 UTC (permalink / raw)
  To: Alberto Garcia, qemu-devel
  Cc: Blue Swirl, Paolo Bonzini, Avi Kivity, Andreas Färber

Alberto Garcia <agarcia@igalia.com> writes:

> Ping,
>
> Here's a QEMU image (124 MB) of a Debian system with the tpci200 and
> ipoctal drivers, in case you want to try them out:

Please rebase and top-post.  Don't send the new series as a response to
a previous thread.

Regards,

Anthony Liguori

>
> http://dl.dropbox.com/u/1667385/qemu-ipoctal.qcow2
>
> The root password is just 'root'.
>
> The patches for QEMU are here:
>
> http://patchwork.ozlabs.org/patch/203892/
> http://patchwork.ozlabs.org/patch/203896/
>
> And you need to run it with these options:
>
> -device tpci200 -device ipoctal232,serialX=DEV
>
> where X is a channel number (from 0 to 7) and DEV a host character
> device (e.g. "pty").
>
> Regards,
>
> Berto

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2013-01-07 20:33 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-23 13:59 [Qemu-devel] [PATCH 0/2] Add TPCI200 and IP-Octal 232 IndustryPack emulation Alberto Garcia
2012-08-23 13:59 ` [Qemu-devel] [PATCH 1/2] Add TEWS TPCI200 " Alberto Garcia
2012-08-23 13:59 ` [Qemu-devel] [PATCH 2/2] Add IP-Octal 232 " Alberto Garcia
2012-08-31 14:12 ` [Qemu-devel] Ping [PATCH 0/2] Add TPCI200 and " Alberto Garcia
2012-08-31 16:09   ` Andreas Färber
2012-09-10 13:10     ` Alberto Garcia
2012-10-05 13:20     ` Alberto Garcia
2012-10-05 14:28       ` Paolo Bonzini
2012-10-05 14:52         ` Anthony Liguori
2012-10-05 16:24         ` Blue Swirl
2012-10-06 11:29           ` Alberto Garcia
2012-10-07 10:13           ` Avi Kivity
2012-10-07 10:19             ` Avi Kivity
2012-10-08  8:02               ` Alberto Garcia
2012-10-10 10:24             ` Alberto Garcia
2012-10-10 11:35               ` Avi Kivity
2012-10-10 17:59                 ` Alberto Garcia
2012-10-05 14:54       ` Andreas Färber
2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 " Alberto Garcia
2012-12-05 14:03   ` Andreas Färber
2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 1/2] Add TEWS TPCI200 " Alberto Garcia
2012-12-05 13:16 ` [Qemu-devel] [PATCH v2 2/2] Add GE IP-Octal 232 " Alberto Garcia
2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 0/2] Add TPCI200 and " Alberto Garcia
2012-12-17 15:27   ` [Qemu-devel] Ping " Alberto Garcia
2013-01-07 20:32     ` Anthony Liguori
2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 1/2] Add TEWS TPCI200 " Alberto Garcia
2012-12-05 15:24 ` [Qemu-devel] [PATCH v3 2/2] Add GE IP-Octal 232 " Alberto Garcia

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.