aboutsummaryrefslogtreecommitdiff
path: root/hw/s390x/s390-pci-bus.c
diff options
context:
space:
mode:
authorPierre Morel <pmorel@linux.vnet.ibm.com>2016-11-23 14:26:34 +0800
committerCornelia Huck <cornelia.huck@de.ibm.com>2017-01-20 10:02:02 +0100
commitd2f07120a35ab31715e9c73438705b635873d06a (patch)
treed2d0b9497267b73181055ce56e8e4c2e0a4ef5c3 /hw/s390x/s390-pci-bus.c
parentdf8dd91b99ed7d27c1cf9a0e6283b2452192d9a1 (diff)
s390x/pci: handle PCIBridge bus number
The PCI bus number is usually set by the host during the enumeration. In the s390 architecture we neither get a Device Tree nor have an enumeration understanding bridge devices. Let's fake the enumeration on reset and set the PCI_PRIMARY_BUS, PCI_SECONDARY_BUS and PCI_SUBORDINATE_BUS config entries for the bridges. Let's add the configuration of these three config entries on bridge hot plug. The bus number is calculated based on a new entry, bus_num of the S390pciState device. This commit is inspired by what spapr pci does. Signed-off-by: Pierre Morel <pmorel@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Diffstat (limited to 'hw/s390x/s390-pci-bus.c')
-rw-r--r--hw/s390x/s390-pci-bus.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index c2ae01c9a6..5cf97b4f7d 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -576,6 +576,7 @@ static int s390_pcihost_init(SysBusDevice *dev)
s->iommu_table = g_hash_table_new_full(g_int64_hash, g_int64_equal,
NULL, g_free);
s->zpci_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL);
+ s->bus_no = 0;
QTAILQ_INIT(&s->pending_sei);
QTAILQ_INIT(&s->zpci_devs);
return 0;
@@ -673,12 +674,24 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
BusState *bus;
PCIBridge *pb = PCI_BRIDGE(dev);
+ PCIDevice *pdev = PCI_DEVICE(dev);
pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq);
pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s);
bus = BUS(&pb->sec_bus);
qbus_set_hotplug_handler(bus, DEVICE(s), errp);
+
+ if (dev->hotplugged) {
+ pci_default_write_config(pdev, PCI_PRIMARY_BUS, s->bus_no, 1);
+ s->bus_no += 1;
+ pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1);
+ do {
+ pdev = pdev->bus->parent_dev;
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS,
+ s->bus_no, 1);
+ } while (pdev->bus && pci_bus_num(pdev->bus));
+ }
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
pdev = PCI_DEVICE(dev);
@@ -812,6 +825,44 @@ out:
object_unparent(OBJECT(pbdev));
}
+static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
+ void *opaque)
+{
+ S390pciState *s = opaque;
+ unsigned int primary = s->bus_no;
+ unsigned int subordinate = 0xff;
+ PCIBus *sec_bus = NULL;
+
+ if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) !=
+ PCI_HEADER_TYPE_BRIDGE)) {
+ return;
+ }
+
+ (s->bus_no)++;
+ pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1);
+ pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1);
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1);
+
+ sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
+ if (!sec_bus) {
+ return;
+ }
+
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1);
+ pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
+ s390_pci_enumerate_bridge, s);
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1);
+}
+
+static void s390_pcihost_reset(DeviceState *dev)
+{
+ S390pciState *s = S390_PCI_HOST_BRIDGE(dev);
+ PCIBus *bus = s->parent_obj.bus;
+
+ s->bus_no = 0;
+ pci_for_each_device(bus, pci_bus_num(bus), s390_pci_enumerate_bridge, s);
+}
+
static void s390_pcihost_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
@@ -819,6 +870,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
dc->cannot_instantiate_with_device_add_yet = true;
+ dc->reset = s390_pcihost_reset;
k->init = s390_pcihost_init;
hc->plug = s390_pcihost_hot_plug;
hc->unplug = s390_pcihost_hot_unplug;