Commit 7733f692 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
parents 9c926ec7 7cf58b79
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -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);
}

/*
+22 −0
Original line number Diff line number Diff line
@@ -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.
+15 −33
Original line number Diff line number Diff line
@@ -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);

@@ -426,35 +432,13 @@ static int pcie_pme_resume(struct pcie_device *srv)
 * @srv - PCIe service device to remove.
 */
static void pcie_pme_remove(struct pcie_device *srv)
{
	pcie_pme_suspend(srv);
	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;
	pcie_pme_disable_interrupt(srv->port, data);
	free_irq(srv->irq, srv);
	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,
};