Commit 68db9bc8 authored by Lukas Wunner's avatar Lukas Wunner Committed by Bjorn Helgaas

PCI: pciehp: Add runtime PM support for PCIe hotplug ports

Linux 4.8 added support for runtime suspending PCIe ports to D3hot with
commit 006d44e4 ("PCI: Add runtime PM support for PCIe ports"), but
excluded hotplug ports.  Those are now afforded runtime PM by the present
commit.

Hotplug ports require a few extra considerations:

- The configuration space of the port remains accessible in D3hot, so all
  the functions to read or modify the Slot Status and Slot Control
  registers need not be modified.  Even turning on slot power doesn't seem
  to require the port to be in D0, at least the PCIe spec doesn't say so
  and I confirmed that by testing with a Thunderbolt controller.

- However D0 is required to access devices on the secondary bus.  This
  happens in pciehp_check_link_status() and pciehp_configure_device() (both
  called from board_added()) and in pciehp_unconfigure_device() (called
  from remove_board()), so acquire a runtime PM ref for their invocation.

- The hotplug port stays active as long as it has active children.  If all
  hotplugged devices below the port runtime suspend, the port is allowed to
  runtime suspend as well.  Plug and unplug detection continues to work in
  D3hot.

- Hotplug interrupts are delivered in-band, so while the hotplug port
  itself is allowed to go to D3hot, its parent ports must stay in D0 for
  interrupts to come through.  Add a corresponding restriction to
  pci_dev_check_d3cold().

- Runtime PM may only be allowed if the hotplug port is handled natively by
  the OS.  On ACPI systems, the port may alternatively be handled by the
  firmware and things break if the OS puts the port into D3 behind the
  firmware's back:  E.g. Thunderbolt hotplug ports on non-Macs are handled
  by Intel's firmware in System Management Mode and the firmware is known
  to access devices on the port's secondary bus without checking first if
  the port is in D0: https://bugzilla.kernel.org/show_bug.cgi?id=53811Signed-off-by: default avatarLukas Wunner <lukas@wunner.de>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
CC: Mika Westerberg <mika.westerberg@linux.intel.com>
parent 437eb7bf
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h> #include <linux/pci.h>
#include "../pci.h" #include "../pci.h"
#include "pciehp.h" #include "pciehp.h"
...@@ -98,6 +99,7 @@ static int board_added(struct slot *p_slot) ...@@ -98,6 +99,7 @@ static int board_added(struct slot *p_slot)
pciehp_green_led_blink(p_slot); pciehp_green_led_blink(p_slot);
/* Check link training status */ /* Check link training status */
pm_runtime_get_sync(&ctrl->pcie->port->dev);
retval = pciehp_check_link_status(ctrl); retval = pciehp_check_link_status(ctrl);
if (retval) { if (retval) {
ctrl_err(ctrl, "Failed to check link status\n"); ctrl_err(ctrl, "Failed to check link status\n");
...@@ -118,12 +120,14 @@ static int board_added(struct slot *p_slot) ...@@ -118,12 +120,14 @@ static int board_added(struct slot *p_slot)
if (retval != -EEXIST) if (retval != -EEXIST)
goto err_exit; goto err_exit;
} }
pm_runtime_put(&ctrl->pcie->port->dev);
pciehp_green_led_on(p_slot); pciehp_green_led_on(p_slot);
pciehp_set_attention_status(p_slot, 0); pciehp_set_attention_status(p_slot, 0);
return 0; return 0;
err_exit: err_exit:
pm_runtime_put(&ctrl->pcie->port->dev);
set_slot_off(ctrl, p_slot); set_slot_off(ctrl, p_slot);
return retval; return retval;
} }
...@@ -137,7 +141,9 @@ static int remove_board(struct slot *p_slot) ...@@ -137,7 +141,9 @@ static int remove_board(struct slot *p_slot)
int retval; int retval;
struct controller *ctrl = p_slot->ctrl; struct controller *ctrl = p_slot->ctrl;
pm_runtime_get_sync(&ctrl->pcie->port->dev);
retval = pciehp_unconfigure_device(p_slot); retval = pciehp_unconfigure_device(p_slot);
pm_runtime_put(&ctrl->pcie->port->dev);
if (retval) if (retval)
return retval; return retval;
......
...@@ -2245,13 +2245,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) ...@@ -2245,13 +2245,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
return false; return false;
/* /*
* Hotplug interrupts cannot be delivered if the link is down, * Hotplug ports handled by firmware in System Management Mode
* so parents of a hotplug port must stay awake. In addition,
* hotplug ports handled by firmware in System Management Mode
* may not be put into D3 by the OS (Thunderbolt on non-Macs). * may not be put into D3 by the OS (Thunderbolt on non-Macs).
* For simplicity, disallow in general for now.
*/ */
if (bridge->is_hotplug_bridge) if (bridge->is_hotplug_bridge && !pciehp_is_native(bridge))
return false; return false;
if (pci_bridge_d3_force) if (pci_bridge_d3_force)
...@@ -2283,7 +2280,10 @@ static int pci_dev_check_d3cold(struct pci_dev *dev, void *data) ...@@ -2283,7 +2280,10 @@ static int pci_dev_check_d3cold(struct pci_dev *dev, void *data)
!pci_pme_capable(dev, PCI_D3cold)) || !pci_pme_capable(dev, PCI_D3cold)) ||
/* If it is a bridge it must be allowed to go to D3. */ /* If it is a bridge it must be allowed to go to D3. */
!pci_power_manageable(dev)) !pci_power_manageable(dev) ||
/* Hotplug interrupts cannot be delivered if the link is down. */
dev->is_hotplug_bridge)
*d3cold_ok = false; *d3cold_ok = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment