diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index dedc55a..7e0835c 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -932,6 +932,7 @@ pcib_probe_hotplug(struct pcib_softc *sc) sc->pcie_link_cap = pcie_read_config(dev, PCIER_LINK_CAP, 4); sc->pcie_slot_cap = pcie_read_config(dev, PCIER_SLOT_CAP, 4); + sc->pcie_slot_ctl = pcie_read_config(dev, PCIER_SLOT_CTL, 2); if ((sc->pcie_slot_cap & PCIEM_SLOT_CAP_HPC) == 0) return; @@ -977,13 +978,22 @@ pcib_pcie_hotplug_command(struct pcib_softc *sc, uint16_t val, uint16_t mask) if (sc->flags & PCIB_HOTPLUG_CMD_PENDING) return; - ctl = pcie_read_config(dev, PCIER_SLOT_CTL, 2); + /* + * A read from the PCIEM_SLOT_CTL_PCC bit (among others) should return + * the last value written; however, some chips behave differently, + * apparently returning the actual power state. Save the last value + * written and use that saved value instead of reading the register. + * Do not save the PCIEM_SLOT_CTL_EIC bit: it is a transient command; + * a read should always return zero. + */ + ctl = sc->pcie_slot_ctl; new = (ctl & ~mask) | val; if (new == ctl) return; if (bootverbose) device_printf(dev, "HotPlug command: %04x -> %04x\n", ctl, new); pcie_write_config(dev, PCIER_SLOT_CTL, new, 2); + sc->pcie_slot_ctl = (new & ~PCIEM_SLOT_CTL_EIC); if (!(sc->pcie_slot_cap & PCIEM_SLOT_CAP_NCCS) && (ctl & new) & PCIEM_SLOT_CTL_CCIE) { sc->flags |= PCIB_HOTPLUG_CMD_PENDING; diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h index 65aec8d..5d2b41c 100644 --- a/sys/dev/pci/pcib_private.h +++ b/sys/dev/pci/pcib_private.h @@ -132,6 +132,7 @@ struct pcib_softc uint16_t bridgectl; /* bridge control register */ uint16_t pcie_link_sta; uint16_t pcie_slot_sta; + uint16_t pcie_slot_ctl; /* last value written to SLOT_CTL register */ uint32_t pcie_link_cap; uint32_t pcie_slot_cap; struct resource *pcie_irq;