aboutsummaryrefslogtreecommitdiff
path: root/hw/pci/shpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci/shpc.c')
-rw-r--r--hw/pci/shpc.c107
1 files changed, 57 insertions, 50 deletions
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c
index fca7f6691a..e7bc7192f1 100644
--- a/hw/pci/shpc.c
+++ b/hw/pci/shpc.c
@@ -123,10 +123,13 @@
#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1)
#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1)
-static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk)
+static uint8_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) >> ctz32(msk);
+ uint16_t result = (pci_get_word(status) & msk) >> ctz32(msk);
+
+ assert(result <= UINT8_MAX);
+ return result;
}
static void shpc_set_status(SHPCDevice *shpc,
@@ -223,6 +226,7 @@ void shpc_reset(PCIDevice *d)
SHPC_SLOT_STATUS_PRSNT_MASK);
shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_PWR_LED_MASK);
}
+ shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_ATTN_LED_MASK);
shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_66);
}
shpc_set_sec_bus_speed(shpc, SHPC_SEC_BUS_33);
@@ -254,60 +258,66 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot)
}
}
-static void shpc_slot_command(SHPCDevice *shpc, uint8_t target,
+static bool shpc_slot_is_off(uint8_t state, uint8_t power, uint8_t attn)
+{
+ return state == SHPC_STATE_DISABLED && power == SHPC_LED_OFF;
+}
+
+static void shpc_slot_command(PCIDevice *d, uint8_t target,
uint8_t state, uint8_t power, uint8_t attn)
{
- uint8_t current_state;
+ SHPCDevice *shpc = d->shpc;
int slot = SHPC_LOGICAL_TO_IDX(target);
+ uint8_t old_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK);
+ uint8_t old_power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK);
+ uint8_t old_attn = shpc_get_status(shpc, slot, SHPC_SLOT_ATTN_LED_MASK);
+
if (target < SHPC_CMD_TRGT_MIN || slot >= shpc->nslots) {
shpc_invalid_command(shpc);
return;
}
- current_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK);
- if (current_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) {
+
+ if (old_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) {
shpc_invalid_command(shpc);
return;
}
- switch (power) {
- case SHPC_LED_NO:
- break;
- default:
+ if (power == SHPC_LED_NO) {
+ power = old_power;
+ } else {
/* TODO: send event to monitor */
shpc_set_status(shpc, slot, power, SHPC_SLOT_PWR_LED_MASK);
}
- switch (attn) {
- case SHPC_LED_NO:
- break;
- default:
+
+ if (attn == SHPC_LED_NO) {
+ attn = old_attn;
+ } else {
/* TODO: send event to monitor */
shpc_set_status(shpc, slot, attn, SHPC_SLOT_ATTN_LED_MASK);
}
- if ((current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_PWRONLY) ||
- (current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_ENABLED)) {
- shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK);
- } else if ((current_state == SHPC_STATE_ENABLED ||
- current_state == SHPC_STATE_PWRONLY) &&
- state == SHPC_STATE_DISABLED) {
+ if (state == SHPC_STATE_NO) {
+ state = old_state;
+ } else {
shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK);
- power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK);
- /* TODO: track what monitor requested. */
- /* Look at LED to figure out whether it's ok to remove the device. */
- if (power == 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;
- }
+ }
+
+ if (!shpc_slot_is_off(old_state, old_power, old_attn) &&
+ shpc_slot_is_off(state, power, attn))
+ {
+ 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;
}
}
-static void shpc_command(SHPCDevice *shpc)
+static void shpc_command(PCIDevice *d)
{
+ SHPCDevice *shpc = d->shpc;
uint8_t code = pci_get_byte(shpc->config + SHPC_CMD_CODE);
uint8_t speed;
uint8_t target;
@@ -328,7 +338,7 @@ static void shpc_command(SHPCDevice *shpc)
state = (code & SHPC_SLOT_STATE_MASK) >> SHPC_SLOT_STATE_SHIFT;
power = (code & SHPC_SLOT_PWR_LED_MASK) >> SHPC_SLOT_PWR_LED_SHIFT;
attn = (code & SHPC_SLOT_ATTN_LED_MASK) >> SHPC_SLOT_ATTN_LED_SHIFT;
- shpc_slot_command(shpc, target, state, power, attn);
+ shpc_slot_command(d, target, state, power, attn);
break;
case 0x40 ... 0x47:
speed = code & SHPC_SEC_BUS_MASK;
@@ -346,10 +356,10 @@ static void shpc_command(SHPCDevice *shpc)
}
for (i = 0; i < shpc->nslots; ++i) {
if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) {
- shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN,
+ shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN,
SHPC_STATE_PWRONLY, SHPC_LED_ON, SHPC_LED_NO);
} else {
- shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN,
+ shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN,
SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO);
}
}
@@ -367,10 +377,10 @@ static void shpc_command(SHPCDevice *shpc)
}
for (i = 0; i < shpc->nslots; ++i) {
if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) {
- shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN,
+ shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN,
SHPC_STATE_ENABLED, SHPC_LED_ON, SHPC_LED_NO);
} else {
- shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN,
+ shpc_slot_command(d, i + SHPC_CMD_TRGT_MIN,
SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO);
}
}
@@ -402,7 +412,7 @@ static void shpc_write(PCIDevice *d, unsigned addr, uint64_t val, int l)
shpc->config[a] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
}
if (ranges_overlap(addr, l, SHPC_CMD_CODE, 2)) {
- shpc_command(shpc);
+ shpc_command(d);
}
shpc_interrupt_update(d);
}
@@ -486,8 +496,9 @@ static const MemoryRegionOps shpc_mmio_ops = {
.max_access_size = 4,
},
};
-static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot,
- SHPCDevice *shpc, Error **errp)
+
+static bool shpc_device_get_slot(PCIDevice *affected_dev, int *slot,
+ SHPCDevice *shpc, Error **errp)
{
int pci_slot = PCI_SLOT(affected_dev->devfn);
*slot = SHPC_PCI_TO_IDX(pci_slot);
@@ -497,21 +508,20 @@ static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot,
"controller. Valid slots are between %d and %d.",
pci_slot, SHPC_IDX_TO_PCI(0),
SHPC_IDX_TO_PCI(shpc->nslots) - 1);
- return;
+ return false;
}
+
+ return true;
}
void shpc_device_plug_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_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!shpc_device_get_slot(PCI_DEVICE(dev), &slot, shpc, errp)) {
return;
}
@@ -553,16 +563,13 @@ void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
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);
SHPCDevice *shpc = pci_hotplug_dev->shpc;
uint8_t state;
uint8_t led;
int slot;
- shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!shpc_device_get_slot(PCI_DEVICE(dev), &slot, shpc, errp)) {
return;
}