mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
Merge branch 'pci/pm'
- Set power state to 'unknown' for all devices, not just those with drivers, during suspend (Lukas Wunner) - Skip restoring Resizable BARs and VF Resizable BARs if device doesn't respond to config reads, to avoid invalid array accesses (Marco Nenciarini) - Add pci_suspend_retains_context() so drivers can tell whether devices may be reset while resuming from suspend due to platform issues; use this in nvme to avoid issues on Qcom RCs (Manivannan Sadhasivam) * pci/pm: nvme-pci: Use pci_suspend_retains_context() during suspend PCI: qcom: Indicate broken L1SS exit during resume from system suspend PCI: Indicate context lost if L1SS exit is broken during resume from system suspend PCI: Add pci_suspend_retains_context() to check if device state is preserved during suspend PCI/IOV: Skip VF Resizable BAR restore on read error PCI: Skip Resizable BAR restore on read error PCI: Stop setting cached power state to 'unknown' on unbind
This commit is contained in:
commit
3a81c66054
|
|
@ -3915,6 +3915,7 @@ static int nvme_suspend(struct device *dev)
|
|||
* use host managed nvme power settings for lowest idle power if
|
||||
* possible. This should have quicker resume latency than a full device
|
||||
* shutdown. But if the firmware is involved after the suspend or the
|
||||
* platform has any limitation in waking from low power states or the
|
||||
* device does not support any non-default power states, shut down the
|
||||
* device fully.
|
||||
*
|
||||
|
|
@ -3923,7 +3924,7 @@ static int nvme_suspend(struct device *dev)
|
|||
* down, so as to allow the platform to achieve its minimum low-power
|
||||
* state (which may not be possible if the link is up).
|
||||
*/
|
||||
if (pm_suspend_via_firmware() || !ctrl->npss ||
|
||||
if (!pci_suspend_retains_context(pdev) || !ctrl->npss ||
|
||||
!pcie_aspm_enabled(pdev) ||
|
||||
(ndev->ctrl.quirks & NVME_QUIRK_SIMPLE_SUSPEND))
|
||||
return nvme_disable_prepare_reset(ndev, true);
|
||||
|
|
|
|||
|
|
@ -1368,6 +1368,19 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp)
|
|||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct qcom_pcie *pcie = to_qcom_pcie(pci);
|
||||
|
||||
/*
|
||||
* During system suspend, the Qcom RC driver may turn off the
|
||||
* analog circuitry of PHY and remove controller votes to save
|
||||
* power. If the link is in L1SS and the endpoint asserts CLKREQ#
|
||||
* to exit L1SS, the time required to wake the system and restore
|
||||
* the PHY/REFCLK may exceed the L1SS exit timing (L10_REFCLK_ON +
|
||||
* T_COMMONMODE), resulting in Link Down (LDn) and a reset of the
|
||||
* endpoint. Set this flag to indicate this limitation to client
|
||||
* drivers so that they can avoid relying on device state being
|
||||
* preserved during system suspend.
|
||||
*/
|
||||
pp->bridge->broken_l1ss_resume = true;
|
||||
|
||||
if (pcie->cfg->ops->host_post_init)
|
||||
pcie->cfg->ops->host_post_init(pcie);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -938,12 +938,18 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev)
|
|||
return;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
|
||||
if (PCI_POSSIBLE_ERROR(ctrl))
|
||||
return;
|
||||
|
||||
nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl);
|
||||
|
||||
for (i = 0; i < nbars; i++, pos += 8) {
|
||||
int bar_idx, size;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
|
||||
if (PCI_POSSIBLE_ERROR(ctrl))
|
||||
return;
|
||||
|
||||
bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl);
|
||||
size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]);
|
||||
ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE;
|
||||
|
|
|
|||
|
|
@ -512,13 +512,6 @@ static void pci_device_remove(struct device *dev)
|
|||
/* Undo the runtime PM settings in local_pci_probe() */
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
/*
|
||||
* If the device is still on, set the power state as "unknown",
|
||||
* since it might change by the next time we load the driver.
|
||||
*/
|
||||
if (pci_dev->current_state == PCI_D0)
|
||||
pci_dev->current_state = PCI_UNKNOWN;
|
||||
|
||||
/*
|
||||
* We would love to complain here if pci_dev->is_enabled is set, that
|
||||
* the driver should have called pci_disable_device(), but the
|
||||
|
|
@ -893,7 +886,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
|
|||
|
||||
if (!pm) {
|
||||
pci_save_state(pci_dev);
|
||||
goto Fixup;
|
||||
goto set_unknown;
|
||||
}
|
||||
|
||||
if (pm->suspend_noirq) {
|
||||
|
|
@ -945,6 +938,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
|
|||
goto Fixup;
|
||||
}
|
||||
|
||||
set_unknown:
|
||||
pci_pm_set_unknown_state(pci_dev);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <asm/dma.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/suspend.h>
|
||||
#include "pci.h"
|
||||
|
||||
DEFINE_MUTEX(pci_slot_mutex);
|
||||
|
|
@ -2886,6 +2887,40 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
|
|||
pm_runtime_put_sync(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_suspend_retains_context - Check if the platform can retain the device
|
||||
* context during system suspend
|
||||
* @pdev: PCI device to check
|
||||
*
|
||||
* Return: true if the platform can guarantee to retain the device context,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool pci_suspend_retains_context(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
|
||||
|
||||
/*
|
||||
* If the platform firmware (like ACPI) is involved at the end of
|
||||
* system suspend, device context may not be retained.
|
||||
*/
|
||||
if (pm_suspend_via_firmware())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Some host bridges power off the PHY to enter deep low-power
|
||||
* modes during system suspend. Exiting L1SS from this condition
|
||||
* may violate timing requirements and result in Link Down (LDn),
|
||||
* which causes a reset of the device. On such platforms, the
|
||||
* endpoint must be prepared for context loss.
|
||||
*/
|
||||
if (bridge && bridge->broken_l1ss_resume)
|
||||
return false;
|
||||
|
||||
/* Assume that the context is retained by default */
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_suspend_retains_context);
|
||||
|
||||
static const struct dmi_system_id bridge_d3_blacklist[] = {
|
||||
#ifdef CONFIG_X86
|
||||
{
|
||||
|
|
|
|||
|
|
@ -231,6 +231,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
|
|||
return;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
if (PCI_POSSIBLE_ERROR(ctrl))
|
||||
return;
|
||||
|
||||
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
||||
|
||||
for (i = 0; i < nbars; i++, pos += 8) {
|
||||
|
|
@ -238,6 +241,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
|
|||
int bar_idx, size;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
if (PCI_POSSIBLE_ERROR(ctrl))
|
||||
return;
|
||||
|
||||
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
|
||||
res = pci_resource_n(pdev, bar_idx);
|
||||
size = pci_rebar_bytes_to_size(resource_size(res));
|
||||
|
|
|
|||
|
|
@ -660,6 +660,8 @@ struct pci_host_bridge {
|
|||
unsigned int preserve_config:1; /* Preserve FW resource setup */
|
||||
unsigned int size_windows:1; /* Enable root bus sizing */
|
||||
unsigned int msi_domain:1; /* Bridge wants MSI domain */
|
||||
unsigned int broken_l1ss_resume:1; /* Resuming from L1SS during
|
||||
system suspend is broken */
|
||||
|
||||
/* Resource alignment requirements */
|
||||
resource_size_t (*align_resource)(struct pci_dev *dev,
|
||||
|
|
@ -2086,6 +2088,8 @@ pci_release_mem_regions(struct pci_dev *pdev)
|
|||
pci_select_bars(pdev, IORESOURCE_MEM));
|
||||
}
|
||||
|
||||
bool pci_suspend_retains_context(struct pci_dev *pdev);
|
||||
|
||||
#else /* CONFIG_PCI is not enabled */
|
||||
|
||||
static inline void pci_set_flags(int flags) { }
|
||||
|
|
@ -2244,6 +2248,11 @@ pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
|
|||
static inline void pci_free_irq_vectors(struct pci_dev *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool pci_suspend_retains_context(struct pci_dev *pdev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/* Include architecture-dependent settings and functions */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user