aboutsummaryrefslogtreecommitdiff
path: root/hw/pci/shpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci/shpc.c')
-rw-r--r--hw/pci/shpc.c124
1 files changed, 77 insertions, 47 deletions
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;