mirror of
https://github.com/xemu-project/xemu.git
synced 2025-01-19 10:24:37 +00:00
pci/shpc: convert SHPC hotplug to use hotplug-handler API
Split shpc_device_hotplug() into hotplug/unplug callbacks and register them as "hotplug-handler" interface implementation of PCI_BRIDGE_DEV device. Replace pci_bus_hotplug() wiring with setting link on PCI BUS "hotplug-handler" property to PCI_BRIDGE_DEV device. Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
c24d5e0b91
commit
5d268704d7
@ -26,6 +26,7 @@
|
||||
#include "hw/pci/slotid_cap.h"
|
||||
#include "exec/memory.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/hotplug.h"
|
||||
|
||||
#define TYPE_PCI_BRIDGE_DEV "pci-bridge"
|
||||
#define PCI_BRIDGE_DEV(obj) \
|
||||
@ -136,6 +137,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||
|
||||
k->init = pci_bridge_dev_initfn;
|
||||
k->exit = pci_bridge_dev_exitfn;
|
||||
k->config_write = pci_bridge_dev_write_config;
|
||||
@ -148,6 +151,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
|
||||
dc->props = pci_bridge_dev_properties;
|
||||
dc->vmsd = &pci_bridge_dev_vmstate;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
hc->plug = shpc_device_hotplug_cb;
|
||||
hc->unplug = shpc_device_hot_unplug_cb;
|
||||
}
|
||||
|
||||
static const TypeInfo pci_bridge_dev_info = {
|
||||
@ -155,6 +160,10 @@ static const TypeInfo pci_bridge_dev_info = {
|
||||
.parent = TYPE_PCI_BRIDGE,
|
||||
.instance_size = sizeof(PCIBridgeDev),
|
||||
.class_init = pci_bridge_dev_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_HOTPLUG_HANDLER },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void pci_bridge_dev_register(void)
|
||||
|
124
hw/pci/shpc.c
124
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;
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "qemu-common.h"
|
||||
#include "exec/memory.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hotplug.h"
|
||||
|
||||
struct SHPCDevice {
|
||||
/* Capability offset in device's config space */
|
||||
@ -41,6 +43,12 @@ int shpc_init(PCIDevice *dev, PCIBus *sec_bus, MemoryRegion *bar, unsigned off);
|
||||
void shpc_cleanup(PCIDevice *dev, MemoryRegion *bar);
|
||||
void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len);
|
||||
|
||||
|
||||
void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
Error **errp);
|
||||
void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||
Error **errp);
|
||||
|
||||
extern VMStateInfo shpc_vmstate_info;
|
||||
#define SHPC_VMSTATE(_field, _type) \
|
||||
VMSTATE_BUFFER_UNSAFE_INFO(_field, _type, 0, shpc_vmstate_info, 0)
|
||||
|
Loading…
x
Reference in New Issue
Block a user