|
@@ -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;
|