Merge branch 'pci/reset'

- Log device readiness timeouts as errors, not warnings (Bjorn Helgaas)

- Wait for device readiness after soft reset (D3hot -> D0uninitialized
  transition), when the device may respond with Request Retry Status if it
  needs more time to initialize (Bjorn Helgaas)

- Drop unnecessary retries when restoring BARs (Lukas Wunner)

* pci/reset:
  PCI: Drop unnecessary retries when restoring BARs
  PCI: Wait for device readiness after D3hot -> D0uninitialized transition
  PCI: Log device readiness timeouts as errors
This commit is contained in:
Bjorn Helgaas 2026-06-23 17:32:02 -05:00
commit 77d94e1436

View File

@ -1244,8 +1244,8 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
}
if (delay > timeout) {
pci_warn(dev, "not ready %dms after %s; giving up\n",
delay - 1, reset_type);
pci_err(dev, "not ready %dms after %s; giving up\n",
delay - 1, reset_type);
return -ENOTTY;
}
@ -1291,7 +1291,18 @@ int pci_power_up(struct pci_dev *dev)
bool need_restore;
pci_power_t state;
u16 pmcsr;
int ret;
/*
* When setting power state to D0, platform_pci_set_power_state()
* ensures main power is on. If it puts the device in D0, it also
* completes any required delays after the transition; if it leaves
* the device in D1, D2, or D3hot, we use the PM Capability to
* transition to D0.
*
* In all cases, the device is either Configuration-Ready or
* inaccessible upon return.
*/
platform_pci_set_power_state(dev, PCI_D0);
if (!dev->pm_cap) {
@ -1332,10 +1343,19 @@ int pci_power_up(struct pci_dev *dev)
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, 0);
/* Mandatory transition delays; see PCI PM 1.2. */
if (state == PCI_D3hot)
if (state == PCI_D3hot) {
pci_dev_d3_sleep(dev);
else if (state == PCI_D2)
if (!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) {
ret = pci_dev_wait(dev, "power up D3hot->D0uninitialized",
PCIE_RESET_READY_POLL_MS);
if (ret) {
dev->current_state = PCI_D3cold;
return -EIO;
}
}
} else if (state == PCI_D2) {
udelay(PCI_PM_D2_DELAY);
}
end:
dev->current_state = PCI_D0;
@ -1752,7 +1772,7 @@ int pci_save_state(struct pci_dev *dev)
EXPORT_SYMBOL(pci_save_state);
static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
u32 saved_val, int retry, bool force)
u32 saved_val, bool force)
{
u32 val;
@ -1760,52 +1780,42 @@ static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
if (!force && val == saved_val)
return;
for (;;) {
pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n",
offset, val, saved_val);
pci_write_config_dword(pdev, offset, saved_val);
if (retry-- <= 0)
return;
pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n", offset, val,
saved_val);
pci_read_config_dword(pdev, offset, &val);
if (val == saved_val)
return;
mdelay(1);
}
pci_write_config_dword(pdev, offset, saved_val);
}
static void pci_restore_config_space_range(struct pci_dev *pdev,
int start, int end, int retry,
bool force)
int start, int end, bool force)
{
int index;
for (index = end; index >= start; index--)
pci_restore_config_dword(pdev, 4 * index,
pdev->saved_config_space[index],
retry, force);
force);
}
static void pci_restore_config_space(struct pci_dev *pdev)
{
if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
pci_restore_config_space_range(pdev, 10, 15, 0, false);
pci_restore_config_space_range(pdev, 10, 15, false);
/* Restore BARs before the command register. */
pci_restore_config_space_range(pdev, 4, 9, 10, false);
pci_restore_config_space_range(pdev, 0, 3, 0, false);
pci_restore_config_space_range(pdev, 4, 9, false);
pci_restore_config_space_range(pdev, 0, 3, false);
} else if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
pci_restore_config_space_range(pdev, 12, 15, 0, false);
pci_restore_config_space_range(pdev, 12, 15, false);
/*
* Force rewriting of prefetch registers to avoid S3 resume
* issues on Intel PCI bridges that occur when these
* registers are not explicitly written.
*/
pci_restore_config_space_range(pdev, 9, 11, 0, true);
pci_restore_config_space_range(pdev, 0, 8, 0, false);
pci_restore_config_space_range(pdev, 9, 11, true);
pci_restore_config_space_range(pdev, 0, 8, false);
} else {
pci_restore_config_space_range(pdev, 0, 15, 0, false);
pci_restore_config_space_range(pdev, 0, 15, false);
}
}