diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-02-13 15:02:03 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-02-13 15:02:04 +0000 |
commit | 0888a29caac6e1b668e498a0ad4d1fea15de012b (patch) | |
tree | 8e87d4d5ca1f0ec7ea82a3b0087b3f613518791f /hw/pci | |
parent | f673e70ccc668607620cd6d30fd0b9bc7a54151d (diff) | |
parent | 417c45ab2f847c0a47b1232f611aa886df6a97d5 (diff) |
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
acpi,pc,pci fixes and enhancements
Most changes here are hotplug related:
This merges hotplug infrastructure changes by Igor,
some acpi related fixes, and PC fixes.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
# gpg: Signature made Mon 10 Feb 2014 09:13:26 GMT using RSA key ID D28D5469
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>"
# gpg: aka "Michael S. Tsirkin <mst@redhat.com>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67
# Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469
* remotes/mst/tags/for_upstream:
ACPI: Remove commented-out code from HPET._CRS
hw/pci: switch to a generic hotplug handling for PCIDevice
pci/pcie: convert PCIE hotplug to use hotplug-handler API
pci/shpc: convert SHPC hotplug to use hotplug-handler API
acpi/piix4pm: convert ACPI PCI hotplug to use hotplug-handler API
qdev:pci: refactor PCIDevice to use generic "hotpluggable" property
hw/acpi: move typeinfo to the file end
qdev: add "hotpluggable" property to Device
qdev: add to BusState "hotplug-handler" link
define hotplug interface
loader: document that errno is set
pc.c: better error message on initrd sizing failure
pc_piix: enable legacy hotplug for Xen
qtest: don't report signals if qtest driver enabled
hw:piix4:acpi: reuse pcihp code for legacy PCI hotplug
pcihp: remove unused AcpiPciHpPciStatus.device_present field
pcihp: make pci_read() mmio calback compatible with legacy ACPI hotplug
pcihp: make PCI hotplug mmio handlers indifferent to PCI_HOTPLUG_ADDR
pcihp: replace enable|disable_device() with oneliners
pcihp: reduce number of device check events
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/pci')
-rw-r--r-- | hw/pci/pci.c | 40 | ||||
-rw-r--r-- | hw/pci/pcie.c | 65 | ||||
-rw-r--r-- | hw/pci/pcie_port.c | 8 | ||||
-rw-r--r-- | hw/pci/shpc.c | 124 |
4 files changed, 127 insertions, 110 deletions
diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1221f32847..4e0701df38 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -35,6 +35,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "exec/address-spaces.h" +#include "hw/hotplug.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -346,13 +347,6 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); } -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev) -{ - bus->qbus.allow_hotplug = 1; - bus->hotplug = hotplug; - bus->hotplug_qdev = qdev; -} - PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, @@ -1761,11 +1755,7 @@ static int pci_qdev_init(DeviceState *qdev) pci_dev->devfn); if (pci_dev == NULL) return -1; - if (qdev->hotplugged && pc->no_hotplug) { - qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(pci_dev))); - do_pci_unregister_device(pci_dev); - return -1; - } + if (pc->init) { rc = pc->init(pci_dev); if (rc != 0) { @@ -1782,34 +1772,9 @@ static int pci_qdev_init(DeviceState *qdev) } pci_add_option_rom(pci_dev, is_default_rom); - if (bus->hotplug) { - /* Let buses differentiate between hotplug and when device is - * enabled during qemu machine creation. */ - rc = bus->hotplug(bus->hotplug_qdev, pci_dev, - qdev->hotplugged ? PCI_HOTPLUG_ENABLED: - PCI_COLDPLUG_ENABLED); - if (rc != 0) { - int r = pci_unregister_device(&pci_dev->qdev); - assert(!r); - return rc; - } - } return 0; } -static int pci_unplug_device(DeviceState *qdev) -{ - PCIDevice *dev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - - if (pc->no_hotplug) { - qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev))); - return -1; - } - return dev->bus->hotplug(dev->bus->hotplug_qdev, dev, - PCI_HOTPLUG_DISABLED); -} - PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, const char *name) { @@ -2280,7 +2245,6 @@ static void pci_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->init = pci_qdev_init; - k->unplug = pci_unplug_device; k->exit = pci_unregister_device; k->bus_type = TYPE_PCI_BUS; k->props = pci_props; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index ca60cf2177..8ecd11eca2 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -26,6 +26,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pcie_regs.h" #include "qemu/range.h" +#include "qapi/qmp/qerror.h" //#define DEBUG_PCIE #ifdef DEBUG_PCIE @@ -216,28 +217,20 @@ static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) hotplug_event_notify(dev); } -static int pcie_cap_slot_hotplug(DeviceState *qdev, - PCIDevice *pci_dev, PCIHotplugState state) +static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev, + DeviceState *dev, + uint8_t **exp_cap, Error **errp) { - PCIDevice *d = PCI_DEVICE(qdev); - uint8_t *exp_cap = d->config + d->exp.exp_cap; - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (state == PCI_COLDPLUG_ENABLED) { - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - return 0; - } + PCIDevice *pci_dev = PCI_DEVICE(dev); + *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; + uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA); PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state); if (sltsta & PCI_EXP_SLTSTA_EIS) { /* the slot is electromechanically locked. * This error is propagated up to qdev and then to HMP/QMP. */ - return -EBUSY; + error_setg_errno(errp, -EBUSY, "slot is electromechanically locked"); } /* TODO: multifunction hot-plug. @@ -245,18 +238,40 @@ static int pcie_cap_slot_hotplug(DeviceState *qdev, * hot plugged/unplugged. */ assert(PCI_FUNC(pci_dev->devfn) == 0); +} - if (state == PCI_HOTPLUG_ENABLED) { +void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + uint8_t *exp_cap; + + pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (!dev->hotplugged) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); - } else { - object_unparent(OBJECT(pci_dev)); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); + return; } - return 0; + + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC); +} + +void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + uint8_t *exp_cap; + + pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + + object_unparent(OBJECT(dev)); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC); } /* pci express slot for pci express root/downstream port @@ -305,8 +320,8 @@ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) dev->exp.hpev_notified = false; - pci_bus_hotplug(pci_bridge_get_sec_bus(PCI_BRIDGE(dev)), - pcie_cap_slot_hotplug, &dev->qdev); + qbus_set_hotplug_handler(BUS(pci_bridge_get_sec_bus(PCI_BRIDGE(dev))), + DEVICE(dev), NULL); } void pcie_cap_slot_reset(PCIDevice *dev) diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 2adb0300f4..fa24877955 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -19,6 +19,7 @@ */ #include "hw/pci/pcie_port.h" +#include "hw/hotplug.h" void pcie_port_init_reg(PCIDevice *d) { @@ -149,8 +150,11 @@ static Property pcie_slot_props[] = { static void pcie_slot_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); dc->props = pcie_slot_props; + hc->plug = pcie_cap_slot_hotplug_cb; + hc->unplug = pcie_cap_slot_hot_unplug_cb; } static const TypeInfo pcie_slot_type_info = { @@ -159,6 +163,10 @@ static const TypeInfo pcie_slot_type_info = { .instance_size = sizeof(PCIESlot), .abstract = true, .class_init = pcie_slot_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void pcie_port_register_types(void) diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 576244b9f6..180faa7adb 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -7,6 +7,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/msi.h" +#include "qapi/qmp/qerror.h" /* TODO: model power only and disabled slot states. */ /* TODO: handle SERR and wakeups */ @@ -490,65 +491,93 @@ static const MemoryRegionOps shpc_mmio_ops = { .max_access_size = 4, }, }; - -static int shpc_device_hotplug(DeviceState *qdev, PCIDevice *affected_dev, - PCIHotplugState hotplug_state) +static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot, + SHPCDevice *shpc, Error **errp) { int pci_slot = PCI_SLOT(affected_dev->devfn); - uint8_t state; - uint8_t led; - PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); - SHPCDevice *shpc = d->shpc; - int slot = SHPC_PCI_TO_IDX(pci_slot); - if (pci_slot < SHPC_IDX_TO_PCI(0) || slot >= shpc->nslots) { - error_report("Unsupported PCI slot %d for standard hotplug " - "controller. Valid slots are between %d and %d.", - pci_slot, SHPC_IDX_TO_PCI(0), - SHPC_IDX_TO_PCI(shpc->nslots) - 1); - return -1; + *slot = SHPC_PCI_TO_IDX(pci_slot); + + if (pci_slot < SHPC_IDX_TO_PCI(0) || *slot >= shpc->nslots) { + error_setg(errp, "Unsupported PCI slot %d for standard hotplug " + "controller. Valid slots are between %d and %d.", + pci_slot, SHPC_IDX_TO_PCI(0), + SHPC_IDX_TO_PCI(shpc->nslots) - 1); + return; + } +} + +void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + Error *local_err = NULL; + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + SHPCDevice *shpc = pci_hotplug_dev->shpc; + int slot; + + shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } + /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an * event when the device is disabled later. */ - if (hotplug_state == PCI_COLDPLUG_ENABLED) { + if (!dev->hotplugged) { shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, SHPC_SLOT_STATUS_PRSNT_MASK); - return 0; + return; } - if (hotplug_state == PCI_HOTPLUG_DISABLED) { - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; - state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } + + /* This could be a cancellation of the previous removal. + * We check MRL state to figure out. */ + if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; } else { - /* This could be a cancellation of the previous removal. - * We check MRL state to figure out. */ - if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } else { - /* Press attention button to cancel removal */ - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON; - } + /* Press attention button to cancel removal */ + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON; } shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); - shpc_interrupt_update(d); - return 0; + shpc_interrupt_update(pci_hotplug_dev); +} + +void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + Error *local_err = NULL; + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + SHPCDevice *shpc = pci_hotplug_dev->shpc; + uint8_t state; + uint8_t led; + int slot; + + shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, errp); + if (local_err) { + return; + } + + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; + state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); + led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { + shpc_free_devices_in_slot(shpc, slot); + shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; + } + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); + shpc_interrupt_update(pci_hotplug_dev); } /* Initialize the SHPC structure in bridge's BAR. */ @@ -616,7 +645,8 @@ int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset) d, "shpc-mmio", SHPC_SIZEOF(d)); shpc_cap_update_dword(d); memory_region_add_subregion(bar, offset, &shpc->mmio); - pci_bus_hotplug(sec_bus, shpc_device_hotplug, &d->qdev); + + qbus_set_hotplug_handler(BUS(sec_bus), DEVICE(d), NULL); d->cap_present |= QEMU_PCI_CAP_SHPC; return 0; |