diff options
| -rw-r--r-- | default-configs/pci.mak | 1 | ||||
| -rw-r--r-- | hw/Makefile.objs | 3 | ||||
| -rw-r--r-- | hw/dataplane/virtio-blk.c | 4 | ||||
| -rw-r--r-- | hw/ipack.c | 115 | ||||
| -rw-r--r-- | hw/ipack.h | 79 | ||||
| -rw-r--r-- | hw/ipoctal232.c | 619 | ||||
| -rw-r--r-- | hw/pci/pci_ids.h | 3 | ||||
| -rw-r--r-- | hw/tpci200.c | 671 | ||||
| -rw-r--r-- | hw/virtio-pci.c | 2 | ||||
| -rw-r--r-- | hw/virtio-pci.h | 1 |
10 files changed, 1495 insertions, 3 deletions
diff --git a/default-configs/pci.mak b/default-configs/pci.mak index ae9d1eb487..ee2d18d5f2 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 aa55ce9873..74b07a7e7e 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -106,6 +106,9 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o # PCI watchdog devices common-obj-$(CONFIG_PCI) += wdt_i6300esb.o +# IndustryPack +common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal232.o ipack.o + # PCI network cards common-obj-$(CONFIG_NE2000_PCI) += ne2000.o common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o diff --git a/hw/dataplane/virtio-blk.c b/hw/dataplane/virtio-blk.c index 1f7346ea19..4b26faa6c2 100644 --- a/hw/dataplane/virtio-blk.c +++ b/hw/dataplane/virtio-blk.c @@ -447,7 +447,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) event_poll_init(&s->event_poll); /* Set up guest notifier (irq) */ - if (s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, + if (s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, 1, true) != 0) { fprintf(stderr, "virtio-blk failed to set guest notifier, " "ensure -enable-kvm is set\n"); @@ -508,7 +508,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) event_poll_cleanup(&s->event_poll); /* Clean up guest notifier (irq) */ - s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, false); + s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, 1, false); vring_teardown(&s->vring); } diff --git a/hw/ipack.c b/hw/ipack.c new file mode 100644 index 0000000000..e15540d5cd --- /dev/null +++ b/hw/ipack.c @@ -0,0 +1,115 @@ +/* + * 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(bus)->children, sibling) { + DeviceState *qdev = kid->child; + IPackDevice *ip = IPACK_DEVICE(qdev); + if (ip->slot == slot) { + return ip; + } + } + return NULL; +} + +void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent, + const char *name, uint8_t n_slots, + qemu_irq_handler handler) +{ + qbus_create_inplace(&bus->qbus, TYPE_IPACK_BUS, parent, name); + bus->n_slots = n_slots; + bus->set_irq = handler; +} + +static int ipack_device_dev_init(DeviceState *qdev) +{ + IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(qdev)); + IPackDevice *dev = IPACK_DEVICE(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 = IPACK_DEVICE(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 0000000000..69e26282d3 --- /dev/null +++ b/hw/ipack.h @@ -0,0 +1,79 @@ +/* + * 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; + /* All fields are private */ + 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); +void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent, + const char *name, uint8_t n_slots, + qemu_irq_handler handler); + +#endif diff --git a/hw/ipoctal232.c b/hw/ipoctal232.c new file mode 100644 index 0000000000..c1e3b197b5 --- /dev/null +++ b/hw/ipoctal232.c @@ -0,0 +1,619 @@ +/* + * 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 "qemu/bitops.h" +#include "char/char.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; +}; + +#define TYPE_IPOCTAL "ipoctal232" + +#define IPOCTAL(obj) \ + OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL) + +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 = IPOCTAL(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 = IPOCTAL(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 = IPOCTAL(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 = IPOCTAL(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 = IPOCTAL(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 = IPOCTAL(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 = TYPE_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) diff --git a/hw/pci/pci_ids.h b/hw/pci/pci_ids.h index 271d935bc7..d8dc2f1bf7 100644 --- a/hw/pci/pci_ids.h +++ b/hw/pci/pci_ids.h @@ -148,4 +148,7 @@ #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 + #endif diff --git a/hw/tpci200.c b/hw/tpci200.c new file mode 100644 index 0000000000..e082bca965 --- /dev/null +++ b/hw/tpci200.c @@ -0,0 +1,671 @@ +/* + * 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 "ipack.h" +#include "pci/pci.h" +#include "qemu/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; + +#define TYPE_TPCI200 "tpci200" + +#define TPCI200(obj) \ + OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200) + +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 = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip))); + PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent); + TPCI200State *dev = TPCI200(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 = TPCI200(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); + + ipack_bus_new_inplace(&s->bus, DEVICE(&s->dev), NULL, + N_MODULES, tpci200_set_irq); + + return 0; +} + +static void tpci200_exitfn(PCIDevice *pci_dev) +{ + TPCI200State *s = TPCI200(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 = TYPE_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) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 0b49739946..09342463ad 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -616,7 +616,7 @@ static int kvm_virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); EventNotifier *n = virtio_queue_get_guest_notifier(vq); VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - int ret; + int ret = 0; if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) { ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg); diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index 9ff3139fe9..917b465874 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -15,6 +15,7 @@ #ifndef QEMU_VIRTIO_PCI_H #define QEMU_VIRTIO_PCI_H +#include "hw/pci/msi.h" #include "virtio-blk.h" #include "virtio-net.h" #include "virtio-rng.h" |