The Standard Hot-Plug Controller (SHPC) specification (PI=1) defines 66MHz conventional PCI as a valid bus speed mode. While QEMU defined the relevant constants, it hardcoded the secondary bus speed to 33MHz and advertised 0 slots as 66MHz capable.
This patch enables 66MHz support by: 1. Allowing SHPC_SEC_BUS_66 in shpc_set_sec_bus_speed(). 2. Advertising all slots as 66MHz capable in shpc_reset(). 3. Dynamically checking and reporting a slot's 66MHz capability based on the plugged PCI device's Status Register (PCI_STATUS_66MHZ). PCI-X speeds remain unsupported and will continue to trigger an INVALID_MODE command status error. Signed-off-by: khaled saleh <[email protected]> --- hw/pci/shpc.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 938602866d..3becde7f10 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -12,7 +12,7 @@ /* TODO: model power only and disabled slot states. */ /* TODO: handle SERR and wakeups */ -/* TODO: consider enabling 66MHz support */ + /* TODO: remove fully only on state DISABLED and LED off. * track state to properly record this. */ @@ -30,7 +30,7 @@ #define SHPC_PHYS_BUTTON 0x8000 #define SHPC_SEC_BUS 0x10 /* 2 bytes */ #define SHPC_SEC_BUS_33 0x0 -#define SHPC_SEC_BUS_66 0x1 /* Unused */ +#define SHPC_SEC_BUS_66 0x1 #define SHPC_SEC_BUS_MASK 0x7 #define SHPC_MSI_CTL 0x12 /* 1 byte */ #define SHPC_PROG_IFC 0x13 /* 1 byte */ @@ -169,6 +169,17 @@ static void shpc_set_status(SHPCDevice *shpc, pci_word_test_and_set_mask(status, value << ctz32(msk)); } +static bool shpc_device_is_66mhz_capable(SHPCDevice *shpc, int slot) +{ + int pci_slot = SHPC_IDX_TO_PCI(slot); + PCIDevice *dev = shpc->sec_bus->devices[PCI_DEVFN(pci_slot, 0)]; + + if (!dev) { + return false; + } + return pci_get_word(dev->config + PCI_STATUS) & PCI_STATUS_66MHZ; +} + static void shpc_interrupt_update(PCIDevice *d) { SHPCDevice *shpc = d->shpc; @@ -203,6 +214,7 @@ static void shpc_set_sec_bus_speed(SHPCDevice *shpc, uint8_t speed) { switch (speed) { case SHPC_SEC_BUS_33: + case SHPC_SEC_BUS_66: shpc->config[SHPC_SEC_BUS] &= ~SHPC_SEC_BUS_MASK; shpc->config[SHPC_SEC_BUS] |= speed; break; @@ -220,7 +232,7 @@ void shpc_reset(PCIDevice *d) memset(shpc->config, 0, SHPC_SIZEOF(d)); pci_set_byte(shpc->config + SHPC_NSLOTS, nslots); pci_set_long(shpc->config + SHPC_SLOTS_33, nslots); - pci_set_long(shpc->config + SHPC_SLOTS_66, 0); + pci_set_long(shpc->config + SHPC_SLOTS_66, nslots); pci_set_byte(shpc->config + SHPC_FIRST_DEV, SHPC_IDX_TO_PCI(0)); pci_set_word(shpc->config + SHPC_PHYS_SLOT, SHPC_IDX_TO_PHYSICAL(0) | @@ -256,7 +268,9 @@ void shpc_reset(PCIDevice *d) 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_status(shpc, i, + shpc_device_is_66mhz_capable(shpc, i) ? 1 : 0, + SHPC_SLOT_STATUS_66); } shpc_set_sec_bus_speed(shpc, SHPC_SEC_BUS_33); shpc->msi_requested = 0; @@ -578,6 +592,9 @@ void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, 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); + shpc_set_status(shpc, slot, + shpc_device_is_66mhz_capable(shpc, slot) ? 1 : 0, + SHPC_SLOT_STATUS_66); return; } @@ -596,7 +613,9 @@ void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; } - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); + shpc_set_status(shpc, slot, + shpc_device_is_66mhz_capable(shpc, slot) ? 1 : 0, + SHPC_SLOT_STATUS_66); shpc_interrupt_update(pci_hotplug_dev); } -- 2.34.1
