Merge branch 'pci/pm'

- Blacklist Gigabyte X299 Root Port power management to fix Thunderbolt
    hotplug (Mika Westerberg)

  - Revert runtime PM suspend/resume callbacks that broke PME on network
    cable plug (Mika Westerberg)

  - Disable Data Link State Changed interrupts to prevent wakeup
    immediately after suspend (Mika Westerberg)

* pci/pm:
  PCI/PME: Fix possible use-after-free on remove
  PCI/PME: Fix hotplug/sysfs remove deadlock in pcie_pme_remove()
  PCI: pciehp: Disable Data Link Layer State Changed event on suspend
  Revert "PCI/PME: Implement runtime PM callbacks"
  PCI: Blacklist power management of Gigabyte X299 DESIGNARE EX PCIe ports
This commit is contained in:
Bjorn Helgaas 2019-03-06 15:30:15 -06:00
commit 7733f69288
3 changed files with 53 additions and 36 deletions

View File

@ -736,12 +736,25 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
void pcie_enable_interrupt(struct controller *ctrl)
{
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE);
u16 mask;
mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
pcie_write_cmd(ctrl, mask, mask);
}
void pcie_disable_interrupt(struct controller *ctrl)
{
pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE);
u16 mask;
/*
* Mask hot-plug interrupt to prevent it triggering immediately
* when the link goes inactive (we still get PME when any of the
* enabled events is detected). Same goes with Link Layer State
* changed event which generates PME immediately when the link goes
* inactive so mask it as well.
*/
mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
pcie_write_cmd(ctrl, 0, mask);
}
/*

View File

@ -2545,6 +2545,25 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
pm_runtime_put_sync(parent);
}
static const struct dmi_system_id bridge_d3_blacklist[] = {
#ifdef CONFIG_X86
{
/*
* Gigabyte X299 root port is not marked as hotplug capable
* which allows Linux to power manage it. However, this
* confuses the BIOS SMI handler so don't power manage root
* ports on that system.
*/
.ident = "X299 DESIGNARE EX-CF",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"),
},
},
#endif
{ }
};
/**
* pci_bridge_d3_possible - Is it possible to put the bridge into D3
* @bridge: Bridge to check
@ -2590,6 +2609,9 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
if (bridge->is_hotplug_bridge)
return false;
if (dmi_check_system(bridge_d3_blacklist))
return false;
/*
* It should be safe to put PCIe ports from 2015 or newer
* to D3.

View File

@ -363,6 +363,16 @@ static bool pcie_pme_check_wakeup(struct pci_bus *bus)
return false;
}
static void pcie_pme_disable_interrupt(struct pci_dev *port,
struct pcie_pme_service_data *data)
{
spin_lock_irq(&data->lock);
pcie_pme_interrupt_enable(port, false);
pcie_clear_root_pme_status(port);
data->noirq = true;
spin_unlock_irq(&data->lock);
}
/**
* pcie_pme_suspend - Suspend PCIe PME service device.
* @srv: PCIe service device to suspend.
@ -387,11 +397,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
return 0;
}
spin_lock_irq(&data->lock);
pcie_pme_interrupt_enable(port, false);
pcie_clear_root_pme_status(port);
data->noirq = true;
spin_unlock_irq(&data->lock);
pcie_pme_disable_interrupt(port, data);
synchronize_irq(srv->irq);
@ -427,34 +433,12 @@ static int pcie_pme_resume(struct pcie_device *srv)
*/
static void pcie_pme_remove(struct pcie_device *srv)
{
pcie_pme_suspend(srv);
struct pcie_pme_service_data *data = get_service_data(srv);
pcie_pme_disable_interrupt(srv->port, data);
free_irq(srv->irq, srv);
kfree(get_service_data(srv));
}
static int pcie_pme_runtime_suspend(struct pcie_device *srv)
{
struct pcie_pme_service_data *data = get_service_data(srv);
spin_lock_irq(&data->lock);
pcie_pme_interrupt_enable(srv->port, false);
pcie_clear_root_pme_status(srv->port);
data->noirq = true;
spin_unlock_irq(&data->lock);
return 0;
}
static int pcie_pme_runtime_resume(struct pcie_device *srv)
{
struct pcie_pme_service_data *data = get_service_data(srv);
spin_lock_irq(&data->lock);
pcie_pme_interrupt_enable(srv->port, true);
data->noirq = false;
spin_unlock_irq(&data->lock);
return 0;
cancel_work_sync(&data->work);
kfree(data);
}
static struct pcie_port_service_driver pcie_pme_driver = {
@ -464,8 +448,6 @@ static struct pcie_port_service_driver pcie_pme_driver = {
.probe = pcie_pme_probe,
.suspend = pcie_pme_suspend,
.runtime_suspend = pcie_pme_runtime_suspend,
.runtime_resume = pcie_pme_runtime_resume,
.resume = pcie_pme_resume,
.remove = pcie_pme_remove,
};