+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
-#include <strings.h>
-#include <stdint.h>
+#include "qemu/host-utils.h"
#include "qemu/range.h"
#include "qemu/error-report.h"
#include "hw/pci/shpc.h"
#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 */
/* Same slot state masks are used for command and status registers */
#define SHPC_SLOT_STATE_MASK 0x03
#define SHPC_SLOT_STATE_SHIFT \
- (ffs(SHPC_SLOT_STATE_MASK) - 1)
+ ctz32(SHPC_SLOT_STATE_MASK)
#define SHPC_STATE_NO 0x0
#define SHPC_STATE_PWRONLY 0x1
#define SHPC_SLOT_PWR_LED_MASK 0xC
#define SHPC_SLOT_PWR_LED_SHIFT \
- (ffs(SHPC_SLOT_PWR_LED_MASK) - 1)
+ ctz32(SHPC_SLOT_PWR_LED_MASK)
#define SHPC_SLOT_ATTN_LED_MASK 0x30
#define SHPC_SLOT_ATTN_LED_SHIFT \
- (ffs(SHPC_SLOT_ATTN_LED_MASK) - 1)
+ ctz32(SHPC_SLOT_ATTN_LED_MASK)
#define SHPC_LED_NO 0x0
#define SHPC_LED_ON 0x1
#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1)
#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1)
-static int roundup_pow_of_two(int x)
-{
- x |= (x >> 1);
- x |= (x >> 2);
- x |= (x >> 4);
- x |= (x >> 8);
- x |= (x >> 16);
- return x + 1;
-}
-
static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk)
{
uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot);
- return (pci_get_word(status) & msk) >> (ffs(msk) - 1);
+ return (pci_get_word(status) & msk) >> ctz32(msk);
}
static void shpc_set_status(SHPCDevice *shpc,
{
uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot);
pci_word_test_and_clear_mask(status, msk);
- pci_word_test_and_set_mask(status, value << (ffs(msk) - 1));
+ pci_word_test_and_set_mask(status, value << ctz32(msk));
}
static void shpc_interrupt_update(PCIDevice *d)
static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot)
{
+ HotplugHandler *hotplug_ctrl;
int devfn;
int pci_slot = SHPC_IDX_TO_PCI(slot);
for (devfn = PCI_DEVFN(pci_slot, 0);
++devfn) {
PCIDevice *affected_dev = shpc->sec_bus->devices[devfn];
if (affected_dev) {
+ hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(affected_dev));
+ hotplug_handler_unplug(hotplug_ctrl, DEVICE(affected_dev),
+ &error_abort);
object_unparent(OBJECT(affected_dev));
}
}
}
/* Add SHPC capability to the config space for the device. */
-static int shpc_cap_add_config(PCIDevice *d)
+static int shpc_cap_add_config(PCIDevice *d, Error **errp)
{
uint8_t *config;
int config_offset;
config_offset = pci_add_capability(d, PCI_CAP_ID_SHPC,
- 0, SHPC_CAP_LENGTH);
+ 0, SHPC_CAP_LENGTH,
+ errp);
if (config_offset < 0) {
return config_offset;
}
.max_access_size = 4,
},
};
-static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot,
- SHPCDevice *shpc, Error **errp)
+static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot,
+ SHPCDevice *shpc, Error **errp)
{
int pci_slot = PCI_SLOT(affected_dev->devfn);
*slot = SHPC_PCI_TO_IDX(pci_slot);
}
}
-void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
Error *local_err = NULL;
SHPCDevice *shpc = pci_hotplug_dev->shpc;
int slot;
- shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
+ shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
shpc_interrupt_update(pci_hotplug_dev);
}
-void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
+void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ object_property_set_bool(OBJECT(dev), false, "realized", NULL);
+}
+
+void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
Error *local_err = NULL;
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
uint8_t led;
int slot;
- shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
+ shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
/* Initialize the SHPC structure in bridge's BAR. */
-int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset)
+int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar,
+ unsigned offset, Error **errp)
{
int i, ret;
int nslots = SHPC_MAX_SLOTS; /* TODO: qdev property? */
SHPCDevice *shpc = d->shpc = g_malloc0(sizeof(*d->shpc));
shpc->sec_bus = sec_bus;
- ret = shpc_cap_add_config(d);
+ ret = shpc_cap_add_config(d, errp);
if (ret) {
g_free(d->shpc);
return ret;
shpc_cap_update_dword(d);
memory_region_add_subregion(bar, offset, &shpc->mmio);
- qbus_set_hotplug_handler(BUS(sec_bus), DEVICE(d), NULL);
+ qbus_set_hotplug_handler(BUS(sec_bus), OBJECT(d), NULL);
d->cap_present |= QEMU_PCI_CAP_SHPC;
return 0;
int shpc_bar_size(PCIDevice *d)
{
- return roundup_pow_of_two(SHPC_SLOT_REG(SHPC_MAX_SLOTS));
+ return pow2roundup32(SHPC_SLOT_REG(SHPC_MAX_SLOTS));
}
void shpc_cleanup(PCIDevice *d, MemoryRegion *bar)
shpc_cap_update_dword(d);
}
-static void shpc_save(QEMUFile *f, void *pv, size_t size)
+static int shpc_save(QEMUFile *f, void *pv, size_t size,
+ const VMStateField *field, QJSON *vmdesc)
{
PCIDevice *d = container_of(pv, PCIDevice, shpc);
qemu_put_buffer(f, d->shpc->config, SHPC_SIZEOF(d));
+
+ return 0;
}
-static int shpc_load(QEMUFile *f, void *pv, size_t size)
+static int shpc_load(QEMUFile *f, void *pv, size_t size,
+ const VMStateField *field)
{
PCIDevice *d = container_of(pv, PCIDevice, shpc);
int ret = qemu_get_buffer(f, d->shpc->config, SHPC_SIZEOF(d));