Skip to content

Commit 75c47c7

Browse files
khfengbjorn-helgaas
authored andcommitted
PCI/DPC: Disable DPC service on suspend
If the link is powered off during suspend, electrical noise may cause errors that trigger DPC. If the DPC interrupt is enabled and shares an IRQ with PME, that causes a spurious wakeup during suspend. Disable DPC triggering and the DPC interrupt during suspend to prevent this. Clear DPC interrupt status before re-enabling DPC interrupts during resume so we don't get an interrupt for errors that occurred during the suspend/resume process. Link: https://bugzilla.kernel.org/show_bug.cgi?id=209149 Link: https://bugzilla.kernel.org/show_bug.cgi?id=216295 Link: https://bugzilla.kernel.org/show_bug.cgi?id=218090 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Kai-Heng Feng <[email protected]> [bhelgaas: clear status on resume, add comments, commit log] Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 5afc2f7 commit 75c47c7

File tree

1 file changed

+48
-12
lines changed

1 file changed

+48
-12
lines changed

drivers/pci/pcie/dpc.c

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -412,13 +412,44 @@ void pci_dpc_init(struct pci_dev *pdev)
412412
}
413413
}
414414

415+
static void dpc_enable(struct pcie_device *dev)
416+
{
417+
struct pci_dev *pdev = dev->port;
418+
int dpc = pdev->dpc_cap;
419+
u16 ctl;
420+
421+
/*
422+
* Clear DPC Interrupt Status so we don't get an interrupt for an
423+
* old event when setting DPC Interrupt Enable.
424+
*/
425+
pci_write_config_word(pdev, dpc + PCI_EXP_DPC_STATUS,
426+
PCI_EXP_DPC_STATUS_INTERRUPT);
427+
428+
pci_read_config_word(pdev, dpc + PCI_EXP_DPC_CTL, &ctl);
429+
ctl &= ~PCI_EXP_DPC_CTL_EN_MASK;
430+
ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
431+
pci_write_config_word(pdev, dpc + PCI_EXP_DPC_CTL, ctl);
432+
}
433+
434+
static void dpc_disable(struct pcie_device *dev)
435+
{
436+
struct pci_dev *pdev = dev->port;
437+
int dpc = pdev->dpc_cap;
438+
u16 ctl;
439+
440+
/* Disable DPC triggering and DPC interrupts */
441+
pci_read_config_word(pdev, dpc + PCI_EXP_DPC_CTL, &ctl);
442+
ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
443+
pci_write_config_word(pdev, dpc + PCI_EXP_DPC_CTL, ctl);
444+
}
445+
415446
#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
416447
static int dpc_probe(struct pcie_device *dev)
417448
{
418449
struct pci_dev *pdev = dev->port;
419450
struct device *device = &dev->device;
420451
int status;
421-
u16 ctl, cap;
452+
u16 cap;
422453

423454
if (!pcie_aer_is_native(pdev) && !pcie_ports_dpc_native)
424455
return -ENOTSUPP;
@@ -433,11 +464,7 @@ static int dpc_probe(struct pcie_device *dev)
433464
}
434465

435466
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CAP, &cap);
436-
437-
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
438-
ctl &= ~PCI_EXP_DPC_CTL_EN_MASK;
439-
ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
440-
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
467+
dpc_enable(dev);
441468

442469
pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
443470
pci_info(pdev, "error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
@@ -450,21 +477,30 @@ static int dpc_probe(struct pcie_device *dev)
450477
return status;
451478
}
452479

453-
static void dpc_remove(struct pcie_device *dev)
480+
static int dpc_suspend(struct pcie_device *dev)
454481
{
455-
struct pci_dev *pdev = dev->port;
456-
u16 ctl;
482+
dpc_disable(dev);
483+
return 0;
484+
}
457485

458-
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
459-
ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
460-
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
486+
static int dpc_resume(struct pcie_device *dev)
487+
{
488+
dpc_enable(dev);
489+
return 0;
490+
}
491+
492+
static void dpc_remove(struct pcie_device *dev)
493+
{
494+
dpc_disable(dev);
461495
}
462496

463497
static struct pcie_port_service_driver dpcdriver = {
464498
.name = "dpc",
465499
.port_type = PCIE_ANY_PORT,
466500
.service = PCIE_PORT_SERVICE_DPC,
467501
.probe = dpc_probe,
502+
.suspend = dpc_suspend,
503+
.resume = dpc_resume,
468504
.remove = dpc_remove,
469505
};
470506

0 commit comments

Comments
 (0)