mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
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:
commit
7733f69288
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user