diff options
author | Pierre Morel <pmorel@linux.vnet.ibm.com> | 2016-11-23 14:26:34 +0800 |
---|---|---|
committer | Cornelia Huck <cornelia.huck@de.ibm.com> | 2017-01-20 10:02:02 +0100 |
commit | d2f07120a35ab31715e9c73438705b635873d06a (patch) | |
tree | d2d0b9497267b73181055ce56e8e4c2e0a4ef5c3 /hw/s390x/s390-pci-bus.c | |
parent | df8dd91b99ed7d27c1cf9a0e6283b2452192d9a1 (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.c | 52 |
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; |