Patchwork [RFC,06/23] prep: Add i82378 PCI-to-ISA bridge emulation

login
register
mail settings
Submitter Andreas Färber
Date June 14, 2011, 2:37 a.m.
Message ID <1308019077-61957-7-git-send-email-andreas.faerber@web.de>
Download mbox | patch
Permalink /patch/100272/
State New
Headers show

Comments

Andreas Färber - June 14, 2011, 2:37 a.m.
Signed-off-by: Hervé Poussineau <hpoussin@reactos.org>

Inverse endianness in order to work on x86 and ppc host.
Create ISA bus in this device (suggested by Markus).

Cc: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Andreas Färber <andreas.faerber@web.de>
---
 Makefile.objs                   |    1 +
 default-configs/ppc-softmmu.mak |    2 +
 hw/i82378.c                     |  298 +++++++++++++++++++++++++++++++++++++++
 hw/pci_ids.h                    |    1 +
 4 files changed, 302 insertions(+), 0 deletions(-)
 create mode 100644 hw/i82378.c

Patch

diff --git a/Makefile.objs b/Makefile.objs
index b0e4c09..fb57bbf 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -210,6 +210,7 @@  hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
 hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
+hw-obj-$(CONFIG_I82378) += i82378.o
 # Mac shared devices
 hw-obj-$(CONFIG_MACIO) += macio.o
 hw-obj-$(CONFIG_CUDA) += cuda.o
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 1d1a7c2..df64ee6 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -14,7 +14,9 @@  CONFIG_DMA=y
 CONFIG_I82374=y
 CONFIG_OPENPIC=y
 CONFIG_PREP_PCI=y
+CONFIG_I82378=y
 CONFIG_MACIO=y
+CONFIG_PCSPK=y
 CONFIG_CUDA=y
 CONFIG_ADB=y
 CONFIG_MAC_NVRAM=y
diff --git a/hw/i82378.c b/hw/i82378.c
new file mode 100644
index 0000000..181e441
--- /dev/null
+++ b/hw/i82378.c
@@ -0,0 +1,298 @@ 
+/*
+ * QEMU Intel i82378 emulation (PCI to ISA bridge)
+ *
+ * Copyright (c) 2010-2011 Herve Poussineau
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ * Copyright (c) 2010 Andreas Faerber
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pci.h"
+#include "pc.h"
+
+//#define DEBUG_I82378
+
+#ifdef DEBUG_I82378
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "i82378: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "i82378 ERROR: " fmt , ## __VA_ARGS__); } while (0)
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define DEVICE_INVERSE_ENDIAN DEVICE_LITTLE_ENDIAN
+#else
+#define DEVICE_INVERSE_ENDIAN DEVICE_BIG_ENDIAN
+#endif
+
+typedef struct I82378State {
+    qemu_irq out[2];
+    int s_io;
+    int s_mem;
+} I82378State;
+
+typedef struct PCIi82378State {
+    PCIDevice pci_dev;
+    uint32_t isa_io_base;
+    uint32_t isa_mem_base;
+    I82378State state;
+} PCIi82378State;
+
+static inline target_phys_addr_t i82378_io_address(I82378State *state,
+                                                   target_phys_addr_t addr)
+{
+    if (true) {
+        return addr & 0xFFFF;
+    } else {
+        return (addr & 0x1F) | ((addr & 0x007FFF000) >> 7);
+    }
+}
+
+static void i82378_io_writeb(void *opaque,
+                             target_phys_addr_t addr, uint32_t value)
+{
+    I82378State *s = opaque;
+    DPRINTF("%s: " TARGET_FMT_plx "=%02x\n", __func__, addr, value);
+    addr = i82378_io_address(s, addr);
+    cpu_outb(addr, value);
+}
+
+static void i82378_io_writew(void *opaque,
+                             target_phys_addr_t addr, uint32_t value)
+{
+    I82378State *s = opaque;
+    DPRINTF("%s: " TARGET_FMT_plx "=%04x\n", __func__, addr, value);
+    addr = i82378_io_address(s, addr);
+    cpu_outw(addr, value);
+}
+
+static void i82378_io_writel(void *opaque,
+                             target_phys_addr_t addr, uint32_t value)
+{
+    I82378State *s = opaque;
+    DPRINTF("%s: " TARGET_FMT_plx "=%08x\n", __func__, addr, value);
+    addr = i82378_io_address(s, addr);
+    cpu_outl(addr, value);
+}
+
+static uint32_t i82378_io_readb(void *opaque, target_phys_addr_t addr)
+{
+    I82378State *s = opaque;
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    addr = i82378_io_address(s, addr);
+    return cpu_inb(addr);
+}
+
+static uint32_t i82378_io_readw(void *opaque, target_phys_addr_t addr)
+{
+    I82378State *s = opaque;
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    addr = i82378_io_address(s, addr);
+    return cpu_inw(addr);
+}
+
+static uint32_t i82378_io_readl(void *opaque, target_phys_addr_t addr)
+{
+    I82378State *s = opaque;
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    addr = i82378_io_address(s, addr);
+    return cpu_inl(addr);
+}
+
+static CPUWriteMemoryFunc * const i82378_io_write[] = {
+    i82378_io_writeb,
+    i82378_io_writew,
+    i82378_io_writel,
+};
+
+static CPUReadMemoryFunc * const i82378_io_read[] = {
+    i82378_io_readb,
+    i82378_io_readw,
+    i82378_io_readl,
+};
+
+static void i82378_mem_writeb(void *opaque,
+                              target_phys_addr_t addr, uint32_t value)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "=%02x\n", __func__, addr, value);
+    cpu_outb(addr, value);
+}
+
+static void i82378_mem_writew(void *opaque,
+                              target_phys_addr_t addr, uint32_t value)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "=%04x\n", __func__, addr, value);
+    cpu_outw(addr, value);
+}
+
+static void i82378_mem_writel(void *opaque,
+                              target_phys_addr_t addr, uint32_t value)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "=%08x\n", __func__, addr, value);
+    cpu_outl(addr, value);
+}
+
+static uint32_t i82378_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    return cpu_inb(addr);
+}
+
+static uint32_t i82378_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    return cpu_inw(addr);
+}
+
+static uint32_t i82378_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    return cpu_inl(addr);
+}
+
+static CPUWriteMemoryFunc * const i82378_mem_write[] = {
+    i82378_mem_writeb,
+    i82378_mem_writew,
+    i82378_mem_writel,
+};
+
+static CPUReadMemoryFunc * const i82378_mem_read[] = {
+    i82378_mem_readb,
+    i82378_mem_readw,
+    i82378_mem_readl,
+};
+
+static void i82378_init(DeviceState *dev, I82378State *s)
+{
+    ISADevice *pit;
+
+    isa_bus_new(dev);
+
+    /* This device has:
+       2 82C59 (irq)
+       1 82C54 (pit)
+       2 82C37 (dma)
+       NMI
+       Utility Bus Support Registers
+
+       All devices accept byte access only, except timer
+    */
+
+    /* 2 82C59 (irq) */
+    qdev_init_gpio_out(dev, s->out, 2);
+    isa_bus_irqs(i8259_init(s->out[0]));
+
+    /* 1 82C54 (pit) */
+    pit = pit_init(0x40, 0);
+
+    /* speaker */
+    pcspk_init(pit);
+
+    /* 2 82C37 (dma) */
+    DMA_init(1, &s->out[1]);
+    isa_create_simple("i82374");
+
+    /* timer */
+    isa_create_simple("mc146818rtc");
+
+    s->s_io = cpu_register_io_memory(i82378_io_read,
+                                     i82378_io_write, s, DEVICE_INVERSE_ENDIAN);
+    s->s_mem = cpu_register_io_memory(i82378_mem_read,
+                                      i82378_mem_write, s, DEVICE_INVERSE_ENDIAN);
+}
+
+static void pci_i82378_ioport_map(PCIDevice *pci_dev, int region_num,
+                                  pcibus_t addr, pcibus_t size, int type)
+{
+    I82378State *s = &DO_UPCAST(PCIi82378State, pci_dev, pci_dev)->state;
+
+    DPRINTF("%s: %s addr=0x%" FMT_PCIBUS " size=0x%" FMT_PCIBUS "\n",
+            __func__, pci_dev->name, addr, size);
+
+    cpu_register_physical_memory(addr, size, s->s_io);
+}
+
+static void pci_i82378_mmio_map(PCIDevice *pci_dev, int region_num,
+                                pcibus_t addr, pcibus_t size, int type)
+{
+    I82378State *s = &DO_UPCAST(PCIi82378State, pci_dev, pci_dev)->state;
+
+    DPRINTF("%s: %s addr=0x%" FMT_PCIBUS " size=0x%" FMT_PCIBUS "\n",
+            __func__, pci_dev->name, addr, size);
+
+    cpu_register_physical_memory(addr, size, s->s_mem);
+    qemu_register_coalesced_mmio(addr, size);
+}
+
+static int pci_i82378_init(PCIDevice *pci_dev)
+{
+    PCIi82378State *pci = DO_UPCAST(PCIi82378State, pci_dev, pci_dev);
+    I82378State *s = &pci->state;
+    uint8_t *pci_conf;
+
+    pci_conf = pci_dev->config;
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82378);
+    pci_set_word(pci_conf + PCI_COMMAND,
+                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+    pci_set_word(pci_conf + PCI_STATUS,
+                 PCI_STATUS_DEVSEL_MEDIUM);
+    pci_conf[PCI_REVISION_ID] = 0x03;
+    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
+
+    pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0);
+    pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0);
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */
+
+    pci_register_bar(pci_dev, 0, 0x00010000,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, pci_i82378_ioport_map);
+
+    pci_register_bar(pci_dev, 1, 0x01000000,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, pci_i82378_mmio_map);
+
+    /* Make addresses read only */
+    pci_set_word(pci_dev->wmask + PCI_COMMAND,
+                 PCI_COMMAND_SPECIAL);
+    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0 + 0 * 4, pci->isa_io_base);
+    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0 + 1 * 4, pci->isa_mem_base);
+
+    isa_mem_base = pci->isa_mem_base;
+    i82378_init(&pci_dev->qdev, s);
+
+    return 0;
+}
+
+static PCIDeviceInfo pci_i82378_info = {
+    .init = pci_i82378_init,
+    .qdev.name = "i82378",
+    .qdev.size = sizeof(PCIi82378State),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_HEX32("iobase", PCIi82378State, isa_io_base, 0x80000000),
+        DEFINE_PROP_HEX32("membase", PCIi82378State, isa_mem_base, 0xc0000000),
+        DEFINE_PROP_END_OF_LIST()
+    },
+};
+
+static void i82378_register_devices(void)
+{
+    pci_qdev_register(&pci_i82378_info);
+}
+
+device_init(i82378_register_devices)
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index d9457ed..d3bef0e 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -98,6 +98,7 @@ 
 #define PCI_DEVICE_ID_MPC8533E           0x0030
 
 #define PCI_VENDOR_ID_INTEL              0x8086
+#define PCI_DEVICE_ID_INTEL_82378        0x0484
 #define PCI_DEVICE_ID_INTEL_82441        0x1237
 #define PCI_DEVICE_ID_INTEL_82801AA_5    0x2415
 #define PCI_DEVICE_ID_INTEL_82801D       0x24CD