mirror of
https://github.com/qemu/qemu.git
synced 2025-08-09 19:15:32 +00:00
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:
parent
23ab143dcc
commit
4e5c9bfecf
@ -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 = {
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user