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


Reply via email to