hw/pci-bridge: introduce "shpc" property

In the PCI expander bridge, we will want to disable those features of
pci-bridge that relate to SHPC (standard hotplug controller):

- SHPC bar and underlying MemoryRegion
- interrupt (INTx or MSI)
- effective hotplug callbacks
- other SHPC hooks (initialization, cleanup, migration etc)

Introduce a new feature request bit in the PCIBridgeDev.flags field, and
turn off the above if the bit is explicitly cleared.

Suggested-by: Michael S. Tsirkin <mst@redhat.com>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Marcel Apfelbaum <marcel@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Laszlo Ersek 2015-06-19 04:40:13 +02:00 committed by Michael S. Tsirkin
parent 23ab143dcc
commit 4e5c9bfecf
2 changed files with 71 additions and 16 deletions

View File

@ -41,6 +41,7 @@ struct PCIBridgeDev {
MemoryRegion bar; MemoryRegion bar;
uint8_t chassis_nr; uint8_t chassis_nr;
#define PCI_BRIDGE_DEV_F_MSI_REQ 0 #define PCI_BRIDGE_DEV_F_MSI_REQ 0
#define PCI_BRIDGE_DEV_F_SHPC_REQ 1
uint32_t flags; uint32_t flags;
}; };
typedef struct PCIBridgeDev PCIBridgeDev; typedef struct PCIBridgeDev PCIBridgeDev;
@ -55,11 +56,17 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
if (err) { if (err) {
goto bridge_error; goto bridge_error;
} }
dev->config[PCI_INTERRUPT_PIN] = 0x1; if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) {
memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); dev->config[PCI_INTERRUPT_PIN] = 0x1;
err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
if (err) { shpc_bar_size(dev));
goto shpc_error; err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
if (err) {
goto shpc_error;
}
} else {
/* MSI is not applicable without SHPC */
bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ);
} }
err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0);
if (err) { if (err) {
@ -72,15 +79,19 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
goto msi_error; goto msi_error;
} }
} }
/* TODO: spec recommends using 64 bit prefetcheable BAR. if (shpc_present(dev)) {
* Check whether that works well. */ /* TODO: spec recommends using 64 bit prefetcheable BAR.
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | * Check whether that works well. */
PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
}
return 0; return 0;
msi_error: msi_error:
slotid_cap_cleanup(dev); slotid_cap_cleanup(dev);
slotid_error: slotid_error:
shpc_cleanup(dev, &bridge_dev->bar); if (shpc_present(dev)) {
shpc_cleanup(dev, &bridge_dev->bar);
}
shpc_error: shpc_error:
pci_bridge_exitfn(dev); pci_bridge_exitfn(dev);
bridge_error: bridge_error:
@ -94,12 +105,15 @@ static void pci_bridge_dev_exitfn(PCIDevice *dev)
msi_uninit(dev); msi_uninit(dev);
} }
slotid_cap_cleanup(dev); slotid_cap_cleanup(dev);
shpc_cleanup(dev, &bridge_dev->bar); if (shpc_present(dev)) {
shpc_cleanup(dev, &bridge_dev->bar);
}
pci_bridge_exitfn(dev); pci_bridge_exitfn(dev);
} }
static void pci_bridge_dev_instance_finalize(Object *obj) static void pci_bridge_dev_instance_finalize(Object *obj)
{ {
/* this function is idempotent and handles (PCIDevice.shpc == NULL) */
shpc_free(PCI_DEVICE(obj)); shpc_free(PCI_DEVICE(obj));
} }
@ -110,7 +124,9 @@ static void pci_bridge_dev_write_config(PCIDevice *d,
if (msi_present(d)) { if (msi_present(d)) {
msi_write_config(d, address, val, len); msi_write_config(d, address, val, len);
} }
shpc_cap_write_config(d, address, val, len); if (shpc_present(d)) {
shpc_cap_write_config(d, address, val, len);
}
} }
static void qdev_pci_bridge_dev_reset(DeviceState *qdev) static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
@ -118,7 +134,9 @@ static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
PCIDevice *dev = PCI_DEVICE(qdev); PCIDevice *dev = PCI_DEVICE(qdev);
pci_bridge_reset(qdev); pci_bridge_reset(qdev);
shpc_reset(dev); if (shpc_present(dev)) {
shpc_reset(dev);
}
} }
static Property pci_bridge_dev_properties[] = { static Property pci_bridge_dev_properties[] = {
@ -127,18 +145,54 @@ static Property pci_bridge_dev_properties[] = {
0), 0),
DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags, DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags,
PCI_BRIDGE_DEV_F_MSI_REQ, true), PCI_BRIDGE_DEV_F_MSI_REQ, true),
DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags,
PCI_BRIDGE_DEV_F_SHPC_REQ, true),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),
}; };
static bool pci_device_shpc_present(void *opaque, int version_id)
{
PCIDevice *dev = opaque;
return shpc_present(dev);
}
static const VMStateDescription pci_bridge_dev_vmstate = { static const VMStateDescription pci_bridge_dev_vmstate = {
.name = "pci_bridge", .name = "pci_bridge",
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
SHPC_VMSTATE(shpc, PCIDevice, NULL), SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
VMSTATE_END_OF_LIST() VMSTATE_END_OF_LIST()
} }
}; };
static void pci_bridge_dev_hotplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
if (!shpc_present(pci_hotplug_dev)) {
error_setg(errp, "standard hotplug controller has been disabled for "
"this %s", TYPE_PCI_BRIDGE_DEV);
return;
}
shpc_device_hotplug_cb(hotplug_dev, dev, errp);
}
static void pci_bridge_dev_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev,
Error **errp)
{
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
if (!shpc_present(pci_hotplug_dev)) {
error_setg(errp, "standard hotplug controller has been disabled for "
"this %s", TYPE_PCI_BRIDGE_DEV);
return;
}
shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp);
}
static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
@ -157,8 +211,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
dc->props = pci_bridge_dev_properties; dc->props = pci_bridge_dev_properties;
dc->vmsd = &pci_bridge_dev_vmstate; dc->vmsd = &pci_bridge_dev_vmstate;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
hc->plug = shpc_device_hotplug_cb; hc->plug = pci_bridge_dev_hotplug_cb;
hc->unplug_request = shpc_device_hot_unplug_request_cb; hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb;
} }
static const TypeInfo pci_bridge_dev_info = { static const TypeInfo pci_bridge_dev_info = {

View File

@ -30,6 +30,7 @@
#define PCI_BRIDGE_DEV_PROP_CHASSIS_NR "chassis_nr" #define PCI_BRIDGE_DEV_PROP_CHASSIS_NR "chassis_nr"
#define PCI_BRIDGE_DEV_PROP_MSI "msi" #define PCI_BRIDGE_DEV_PROP_MSI "msi"
#define PCI_BRIDGE_DEV_PROP_SHPC "shpc"
int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
uint16_t svid, uint16_t ssid); uint16_t svid, uint16_t ssid);