summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs2
-rw-r--r--hw/apb_pci.c1
-rw-r--r--hw/dec_pci.c1
-rw-r--r--hw/pci.c177
-rw-r--r--hw/pci.h5
-rw-r--r--hw/pci_bridge.c208
-rw-r--r--hw/pci_bridge.h48
7 files changed, 262 insertions, 180 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 67f1b215b1..594894bbed 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -139,7 +139,7 @@ user-obj-y += cutils.o cache-utils.o
hw-obj-y =
hw-obj-y += vl.o loader.o
hw-obj-y += virtio.o virtio-console.o
-hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o
+hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o
hw-obj-y += watchdog.o
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
hw-obj-$(CONFIG_ECC) += ecc.o
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index 0ecac55792..88ee4a9d92 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -29,6 +29,7 @@
#include "sysbus.h"
#include "pci.h"
#include "pci_host.h"
+#include "pci_bridge.h"
#include "rwhandler.h"
#include "apb_pci.h"
#include "sysemu.h"
diff --git a/hw/dec_pci.c b/hw/dec_pci.c
index ee49d5adf0..f7a9cdcfc3 100644
--- a/hw/dec_pci.c
+++ b/hw/dec_pci.c
@@ -27,6 +27,7 @@
#include "sysbus.h"
#include "pci.h"
#include "pci_host.h"
+#include "pci_bridge.h"
/* debug DEC */
//#define DEBUG_DEC
diff --git a/hw/pci.c b/hw/pci.c
index 9c83d747ea..2dc157724a 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -23,6 +23,7 @@
*/
#include "hw.h"
#include "pci.h"
+#include "pci_bridge.h"
#include "pci_internals.h"
#include "monitor.h"
#include "net.h"
@@ -272,26 +273,6 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name,
return bus;
}
-static void pci_register_secondary_bus(PCIBus *parent,
- PCIBus *bus,
- PCIDevice *dev,
- pci_map_irq_fn map_irq,
- const char *name)
-{
- qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name);
- bus->map_irq = map_irq;
- bus->parent_dev = dev;
-
- QLIST_INIT(&bus->child);
- QLIST_INSERT_HEAD(&parent->child, bus, sibling);
-}
-
-static void pci_unregister_secondary_bus(PCIBus *bus)
-{
- assert(QLIST_EMPTY(&bus->child));
- QLIST_REMOVE(bus, sibling);
-}
-
int pci_bus_num(PCIBus *s)
{
if (!s->parent_dev)
@@ -799,75 +780,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
}
}
-static uint32_t pci_config_get_io_base(PCIDevice *d,
- uint32_t base, uint32_t base_upper16)
-{
- uint32_t val;
-
- val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8;
- if (d->config[base] & PCI_IO_RANGE_TYPE_32) {
- val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16;
- }
- return val;
-}
-
-static pcibus_t pci_config_get_memory_base(PCIDevice *d, uint32_t base)
-{
- return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK)
- << 16;
-}
-
-static pcibus_t pci_config_get_pref_base(PCIDevice *d,
- uint32_t base, uint32_t upper)
-{
- pcibus_t tmp;
- pcibus_t val;
-
- tmp = (pcibus_t)pci_get_word(d->config + base);
- val = (tmp & PCI_PREF_RANGE_MASK) << 16;
- if (tmp & PCI_PREF_RANGE_TYPE_64) {
- val |= (pcibus_t)pci_get_long(d->config + upper) << 32;
- }
- return val;
-}
-
-static pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type)
-{
- pcibus_t base;
- if (type & PCI_BASE_ADDRESS_SPACE_IO) {
- base = pci_config_get_io_base(bridge,
- PCI_IO_BASE, PCI_IO_BASE_UPPER16);
- } else {
- if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
- base = pci_config_get_pref_base(
- bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32);
- } else {
- base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
- }
- }
-
- return base;
-}
-
-static pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type)
-{
- pcibus_t limit;
- if (type & PCI_BASE_ADDRESS_SPACE_IO) {
- limit = pci_config_get_io_base(bridge,
- PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16);
- limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */
- } else {
- if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
- limit = pci_config_get_pref_base(
- bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32);
- } else {
- limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT);
- }
- limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */
- }
- return limit;
-}
-
static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
uint8_t type)
{
@@ -1518,7 +1430,7 @@ static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d)
pci_update_mappings(d);
}
-static void pci_bridge_update_mappings(PCIBus *b)
+void pci_bridge_update_mappings(PCIBus *b)
{
PCIBus *child;
@@ -1529,23 +1441,6 @@ static void pci_bridge_update_mappings(PCIBus *b)
}
}
-static void pci_bridge_write_config(PCIDevice *d,
- uint32_t address, uint32_t val, int len)
-{
- pci_default_write_config(d, address, val, len);
-
- if (/* io base/limit */
- ranges_overlap(address, len, PCI_IO_BASE, 2) ||
-
- /* memory base/limit, prefetchable base/limit and
- io base/limit upper 16 */
- ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) {
- PCIBridge *s = container_of(d, PCIBridge, dev);
- PCIBus *secondary_bus = &s->bus;
- pci_bridge_update_mappings(secondary_bus);
- }
-}
-
PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
{
PCIBus *sec;
@@ -1589,54 +1484,6 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function)
return bus->devices[PCI_DEVFN(slot, function)];
}
-static int pci_bridge_initfn(PCIDevice *dev)
-{
- PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev);
-
- pci_config_set_vendor_id(s->dev.config, s->vid);
- pci_config_set_device_id(s->dev.config, s->did);
-
- pci_set_word(dev->config + PCI_STATUS,
- PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
- pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
- dev->config[PCI_HEADER_TYPE] =
- (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
- PCI_HEADER_TYPE_BRIDGE;
- pci_set_word(dev->config + PCI_SEC_STATUS,
- PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
- return 0;
-}
-
-static int pci_bridge_exitfn(PCIDevice *pci_dev)
-{
- PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev);
- PCIBus *bus = &s->bus;
- pci_unregister_secondary_bus(bus);
- return 0;
-}
-
-PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
- uint16_t vid, uint16_t did,
- pci_map_irq_fn map_irq, const char *name)
-{
- PCIDevice *dev;
- PCIBridge *s;
-
- dev = pci_create_multifunction(bus, devfn, multifunction, "pci-bridge");
- qdev_prop_set_uint32(&dev->qdev, "vendorid", vid);
- qdev_prop_set_uint32(&dev->qdev, "deviceid", did);
- qdev_init_nofail(&dev->qdev);
-
- s = DO_UPCAST(PCIBridge, dev, dev);
- pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name);
- return &s->bus;
-}
-
-PCIDevice *pci_bridge_get_device(PCIBus *bus)
-{
- return bus->parent_dev;
-}
-
static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
{
PCIDevice *pci_dev = (PCIDevice *)qdev;
@@ -1942,23 +1789,3 @@ static char *pcibus_get_dev_path(DeviceState *dev)
return strdup(path);
}
-static PCIDeviceInfo bridge_info = {
- .qdev.name = "pci-bridge",
- .qdev.size = sizeof(PCIBridge),
- .init = pci_bridge_initfn,
- .exit = pci_bridge_exitfn,
- .config_write = pci_bridge_write_config,
- .is_bridge = 1,
- .qdev.props = (Property[]) {
- DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),
- DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0),
- DEFINE_PROP_END_OF_LIST(),
- }
-};
-
-static void pci_register_devices(void)
-{
- pci_qdev_register(&bridge_info);
-}
-
-device_init(pci_register_devices)
diff --git a/hw/pci.h b/hw/pci.h
index 1eab7e7dda..c551f9661c 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -233,10 +233,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
void do_pci_info_print(Monitor *mon, const QObject *data);
void do_pci_info(Monitor *mon, QObject **ret_data);
-PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
- uint16_t vid, uint16_t did,
- pci_map_irq_fn map_irq, const char *name);
-PCIDevice *pci_bridge_get_device(PCIBus *bus);
+void pci_bridge_update_mappings(PCIBus *b);
static inline void
pci_set_byte(uint8_t *config, uint8_t val)
diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
new file mode 100644
index 0000000000..7f2787091c
--- /dev/null
+++ b/hw/pci_bridge.c
@@ -0,0 +1,208 @@
+/*
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to dea
+
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
+
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * split out from pci.c
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ */
+
+#include "pci_bridge.h"
+#include "pci_internals.h"
+
+PCIDevice *pci_bridge_get_device(PCIBus *bus)
+{
+ return bus->parent_dev;
+}
+
+static void pci_register_secondary_bus(PCIBus *parent,
+ PCIBus *bus,
+ PCIDevice *dev,
+ pci_map_irq_fn map_irq,
+ const char *name)
+{
+ qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name);
+ bus->map_irq = map_irq;
+ bus->parent_dev = dev;
+
+ QLIST_INIT(&bus->child);
+ QLIST_INSERT_HEAD(&parent->child, bus, sibling);
+}
+
+static void pci_unregister_secondary_bus(PCIBus *bus)
+{
+ assert(QLIST_EMPTY(&bus->child));
+ QLIST_REMOVE(bus, sibling);
+}
+
+static uint32_t pci_config_get_io_base(PCIDevice *d,
+ uint32_t base, uint32_t base_upper16)
+{
+ uint32_t val;
+
+ val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8;
+ if (d->config[base] & PCI_IO_RANGE_TYPE_32) {
+ val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16;
+ }
+ return val;
+}
+
+static pcibus_t pci_config_get_memory_base(PCIDevice *d, uint32_t base)
+{
+ return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK)
+ << 16;
+}
+
+static pcibus_t pci_config_get_pref_base(PCIDevice *d,
+ uint32_t base, uint32_t upper)
+{
+ pcibus_t tmp;
+ pcibus_t val;
+
+ tmp = (pcibus_t)pci_get_word(d->config + base);
+ val = (tmp & PCI_PREF_RANGE_MASK) << 16;
+ if (tmp & PCI_PREF_RANGE_TYPE_64) {
+ val |= (pcibus_t)pci_get_long(d->config + upper) << 32;
+ }
+ return val;
+}
+
+pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type)
+{
+ pcibus_t base;
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ base = pci_config_get_io_base(bridge,
+ PCI_IO_BASE, PCI_IO_BASE_UPPER16);
+ } else {
+ if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ base = pci_config_get_pref_base(
+ bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32);
+ } else {
+ base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
+ }
+ }
+
+ return base;
+}
+
+pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type)
+{
+ pcibus_t limit;
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ limit = pci_config_get_io_base(bridge,
+ PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16);
+ limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */
+ } else {
+ if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ limit = pci_config_get_pref_base(
+ bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32);
+ } else {
+ limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT);
+ }
+ limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */
+ }
+ return limit;
+}
+
+static void pci_bridge_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ pci_default_write_config(d, address, val, len);
+
+ if (/* io base/limit */
+ ranges_overlap(address, len, PCI_IO_BASE, 2) ||
+
+ /* memory base/limit, prefetchable base/limit and
+ io base/limit upper 16 */
+ ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) {
+ PCIBridge *s = container_of(d, PCIBridge, dev);
+ PCIBus *secondary_bus = &s->bus;
+ pci_bridge_update_mappings(secondary_bus);
+ }
+}
+
+static int pci_bridge_initfn(PCIDevice *dev)
+{
+ PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev);
+
+ pci_config_set_vendor_id(s->dev.config, s->vid);
+ pci_config_set_device_id(s->dev.config, s->did);
+
+ pci_set_word(dev->config + PCI_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
+ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
+ dev->config[PCI_HEADER_TYPE] =
+ (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
+ PCI_HEADER_TYPE_BRIDGE;
+ pci_set_word(dev->config + PCI_SEC_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
+ return 0;
+}
+
+static int pci_bridge_exitfn(PCIDevice *pci_dev)
+{
+ PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev);
+ PCIBus *bus = &s->bus;
+ pci_unregister_secondary_bus(bus);
+ return 0;
+}
+
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
+ uint16_t vid, uint16_t did,
+ pci_map_irq_fn map_irq, const char *name)
+{
+ PCIDevice *dev;
+ PCIBridge *s;
+
+ dev = pci_create_multifunction(bus, devfn, multifunction, "pci-bridge");
+ qdev_prop_set_uint32(&dev->qdev, "vendorid", vid);
+ qdev_prop_set_uint32(&dev->qdev, "deviceid", did);
+ qdev_init_nofail(&dev->qdev);
+
+ s = DO_UPCAST(PCIBridge, dev, dev);
+ pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name);
+ return &s->bus;
+}
+
+static PCIDeviceInfo bridge_info = {
+ .qdev.name = "pci-bridge",
+ .qdev.size = sizeof(PCIBridge),
+ .init = pci_bridge_initfn,
+ .exit = pci_bridge_exitfn,
+ .config_write = pci_bridge_write_config,
+ .is_bridge = 1,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),
+ DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void pci_register_devices(void)
+{
+ pci_qdev_register(&bridge_info);
+}
+
+device_init(pci_register_devices)
diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h
new file mode 100644
index 0000000000..ddb2c82e25
--- /dev/null
+++ b/hw/pci_bridge.h
@@ -0,0 +1,48 @@
+/*
+ * QEMU PCI bridge
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc]
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ */
+
+#ifndef QEMU_PCI_BRIDGE_H
+#define QEMU_PCI_BRIDGE_H
+
+#include "pci.h"
+
+PCIDevice *pci_bridge_get_device(PCIBus *bus);
+
+pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type);
+pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type);
+
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
+ uint16_t vid, uint16_t did,
+ pci_map_irq_fn map_irq, const char *name);
+
+#endif /* QEMU_PCI_BRIDGE_H */
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */