Commit 5d268704 authored by Igor Mammedov's avatar Igor Mammedov Committed by Michael S. Tsirkin
Browse files

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: default avatarIgor Mammedov <imammedo@redhat.com>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent c24d5e0b
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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)
+77 −47
Original line number Diff line number Diff line
@@ -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,46 +491,45 @@ 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 "
    *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 -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;
    }
    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;
        return;
    }
    } 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)) {
@@ -545,10 +545,39 @@ static int shpc_device_hotplug(DeviceState *qdev, PCIDevice *affected_dev,
        shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |=
            SHPC_SLOT_EVENT_BUTTON;
    }
    shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66);
    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(d);
    return 0;
    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;
+8 −0
Original line number Diff line number Diff line
@@ -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)