UBUNTU: SAUCE: PCI/DPC: Disable DPC service during suspend, again
BugLink: https://bugs.launchpad.net/bugs/2033025 When the power rail gets cut off, the hardware can create some electric noise on the link that triggers AER. If IRQ is shared between AER with PME, such AER noise will cause a spurious wakeup on system suspend. When the power rail gets back, the firmware of the device resets itself and can create unexpected behavior like sending PTM messages. For this case, the driver will always be too late to toggle off features should be disabled. As Per PCIe Base Spec 5.0, section 5.2, titled "Link State Power Management", TLP and DLLP transmission are disabled for a Link in L2/L3 Ready (D3hot), L2 (D3cold with aux power) and L3 (D3cold) states. So if the power will be turned off during suspend process, disable DPC service and re-enable it during the resume process. This should not affect the basic functionality. Since DPC depends on AER to function, also disable DPC. This patch was in the Ubuntu kernel but was dropped later in the hope of upstream version would come to light soon. However, the patch is still under discussion, and PCI maintainer and reviewers don't have a concensus on the final approach (D3hot vs D3cold), so let's reinstate the fix until upstream has a final version. Link: https://bugzilla.kernel.org/show_bug.cgi?id=209149 Link: https://bugzilla.kernel.org/show_bug.cgi?id=216295 Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com> [ arighi: rewrite dpc_enable() to match the new implementation in v6.7 ] Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
This commit is contained in:
committed by
Paolo Pisati
parent
9050d2f837
commit
04cc3c1d6e
+39
-13
@@ -358,13 +358,34 @@ void pci_dpc_init(struct pci_dev *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
static void dpc_enable(struct pcie_device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = dev->port;
|
||||
u16 ctl;
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
|
||||
ctl &= ~PCI_EXP_DPC_CTL_EN_MASK;
|
||||
ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
|
||||
}
|
||||
|
||||
static void dpc_disable(struct pcie_device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = dev->port;
|
||||
u16 ctl;
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
|
||||
ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
|
||||
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
|
||||
}
|
||||
|
||||
#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
|
||||
static int dpc_probe(struct pcie_device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = dev->port;
|
||||
struct device *device = &dev->device;
|
||||
int status;
|
||||
u16 ctl, cap;
|
||||
u16 cap;
|
||||
|
||||
if (!pcie_aer_is_native(pdev) && !pcie_ports_dpc_native)
|
||||
return -ENOTSUPP;
|
||||
@@ -379,13 +400,9 @@ static int dpc_probe(struct pcie_device *dev)
|
||||
}
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CAP, &cap);
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
|
||||
ctl &= ~PCI_EXP_DPC_CTL_EN_MASK;
|
||||
ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
|
||||
|
||||
dpc_enable(dev);
|
||||
pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
|
||||
|
||||
pci_info(pdev, "error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
|
||||
cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
|
||||
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
||||
@@ -396,14 +413,21 @@ static int dpc_probe(struct pcie_device *dev)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int dpc_suspend(struct pcie_device *dev)
|
||||
{
|
||||
dpc_disable(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpc_resume(struct pcie_device *dev)
|
||||
{
|
||||
dpc_enable(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpc_remove(struct pcie_device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = dev->port;
|
||||
u16 ctl;
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
|
||||
ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
|
||||
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
|
||||
dpc_disable(dev);
|
||||
}
|
||||
|
||||
static struct pcie_port_service_driver dpcdriver = {
|
||||
@@ -411,6 +435,8 @@ static struct pcie_port_service_driver dpcdriver = {
|
||||
.port_type = PCIE_ANY_PORT,
|
||||
.service = PCIE_PORT_SERVICE_DPC,
|
||||
.probe = dpc_probe,
|
||||
.suspend = dpc_suspend,
|
||||
.resume = dpc_resume,
|
||||
.remove = dpc_remove,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user