Merge branch 'pci/pm' into next

* pci/pm:
  x86/platform/intel-mid: Constify mid_pci_platform_pm
  PCI: pciehp: Add runtime PM support for PCIe hotplug ports
  ACPI / hotplug / PCI: Make device_is_managed_by_native_pciehp() public
  ACPI / hotplug / PCI: Use cached copy of PCI_EXP_SLTCAP_HPC bit
  PCI: Unfold conditions to block runtime PM on PCIe ports
  PCI: Consolidate conditions to allow runtime PM on PCIe ports
  PCI: Activate runtime PM on a PCIe port only if it can suspend
  PCI: Speed up algorithm in pci_bridge_d3_update()
  PCI: Autosense device removal in pci_bridge_d3_update()
  PCI: Don't acquire ref on parent in pci_bridge_d3_update()
  USB: UHCI: report non-PME wakeup signalling for Intel hardware
  PCI: Check for PME in targeted sleep state
This commit is contained in:
Bjorn Helgaas 2016-12-12 11:25:04 -06:00
commit daaed10443
11 changed files with 97 additions and 97 deletions

View File

@ -320,7 +320,7 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_fixup_device(pci_fixup_final, dev);
pci_create_sysfs_dev_files(dev);
pci_proc_attach_device(dev);
pci_bridge_d3_device_changed(dev);
pci_bridge_d3_update(dev);
dev->match_driver = true;
retval = device_attach(&dev->dev);

View File

@ -222,35 +222,6 @@ static void acpiphp_post_dock_fixup(struct acpi_device *adev)
acpiphp_let_context_go(context);
}
/* Check whether the PCI device is managed by native PCIe hotplug driver */
static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
{
u32 reg32;
acpi_handle tmp;
struct acpi_pci_root *root;
/* Check whether the PCIe port supports native PCIe hotplug */
if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32))
return false;
if (!(reg32 & PCI_EXP_SLTCAP_HPC))
return false;
/*
* Check whether native PCIe hotplug has been enabled for
* this PCIe hierarchy.
*/
tmp = acpi_find_root_bridge_handle(pdev);
if (!tmp)
return false;
root = acpi_pci_find_root(tmp);
if (!root)
return false;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
return false;
return true;
}
/**
* acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
* @handle: ACPI handle of the object to add a context to.
@ -334,7 +305,7 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
* expose slots to user space in those cases.
*/
if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev))
&& !(pdev && device_is_managed_by_native_pciehp(pdev))) {
&& !(pdev && pdev->is_hotplug_bridge && pciehp_is_native(pdev))) {
unsigned long long sun;
int retval;

View File

@ -31,6 +31,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h>
#include "../pci.h"
#include "pciehp.h"
@ -98,6 +99,7 @@ static int board_added(struct slot *p_slot)
pciehp_green_led_blink(p_slot);
/* Check link training status */
pm_runtime_get_sync(&ctrl->pcie->port->dev);
retval = pciehp_check_link_status(ctrl);
if (retval) {
ctrl_err(ctrl, "Failed to check link status\n");
@ -118,12 +120,14 @@ static int board_added(struct slot *p_slot)
if (retval != -EEXIST)
goto err_exit;
}
pm_runtime_put(&ctrl->pcie->port->dev);
pciehp_green_led_on(p_slot);
pciehp_set_attention_status(p_slot, 0);
return 0;
err_exit:
pm_runtime_put(&ctrl->pcie->port->dev);
set_slot_off(ctrl, p_slot);
return retval;
}
@ -137,7 +141,9 @@ static int remove_board(struct slot *p_slot)
int retval;
struct controller *ctrl = p_slot->ctrl;
pm_runtime_get_sync(&ctrl->pcie->port->dev);
retval = pciehp_unconfigure_device(p_slot);
pm_runtime_put(&ctrl->pcie->port->dev);
if (retval)
return retval;

View File

@ -369,6 +369,30 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
}
EXPORT_SYMBOL_GPL(pci_get_hp_params);
/**
* pciehp_is_native - Check whether a hotplug port is handled by the OS
* @pdev: Hotplug port to check
*
* Walk up from @pdev to the host bridge, obtain its cached _OSC Control Field
* and return the value of the "PCI Express Native Hot Plug control" bit.
* On failure to obtain the _OSC Control Field return %false.
*/
bool pciehp_is_native(struct pci_dev *pdev)
{
struct acpi_pci_root *root;
acpi_handle handle;
handle = acpi_find_root_bridge_handle(pdev);
if (!handle)
return false;
root = acpi_pci_find_root(handle);
if (!root)
return false;
return root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL;
}
/**
* pci_acpi_wake_bus - Root bus wakeup notification fork function.
* @work: Work item to handle.

View File

@ -49,7 +49,7 @@ static bool mid_pci_need_resume(struct pci_dev *dev)
return false;
}
static struct pci_platform_pm_ops mid_pci_platform_pm = {
static const struct pci_platform_pm_ops mid_pci_platform_pm = {
.is_manageable = mid_pci_power_manageable,
.set_state = mid_pci_set_power_state,
.choose_state = mid_pci_choose_state,

View File

@ -2106,6 +2106,10 @@ bool pci_dev_run_wake(struct pci_dev *dev)
if (!dev->pme_support)
return false;
/* PME-capable in principle, but not from the intended sleep state */
if (!pci_pme_capable(dev, pci_target_state(dev)))
return false;
while (bus->parent) {
struct pci_dev *bridge = bus->self;
@ -2226,7 +2230,7 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
* This function checks if it is possible to move the bridge to D3.
* Currently we only allow D3 for recent enough PCIe ports.
*/
static bool pci_bridge_d3_possible(struct pci_dev *bridge)
bool pci_bridge_d3_possible(struct pci_dev *bridge)
{
unsigned int year;
@ -2239,6 +2243,14 @@ static bool pci_bridge_d3_possible(struct pci_dev *bridge)
case PCI_EXP_TYPE_DOWNSTREAM:
if (pci_bridge_d3_disable)
return false;
/*
* Hotplug ports handled by firmware in System Management Mode
* may not be put into D3 by the OS (Thunderbolt on non-Macs).
*/
if (bridge->is_hotplug_bridge && !pciehp_is_native(bridge))
return false;
if (pci_bridge_d3_force)
return true;
@ -2259,32 +2271,36 @@ static bool pci_bridge_d3_possible(struct pci_dev *bridge)
static int pci_dev_check_d3cold(struct pci_dev *dev, void *data)
{
bool *d3cold_ok = data;
bool no_d3cold;
/*
* The device needs to be allowed to go D3cold and if it is wake
* capable to do so from D3cold.
*/
no_d3cold = dev->no_d3cold || !dev->d3cold_allowed ||
(device_may_wakeup(&dev->dev) && !pci_pme_capable(dev, PCI_D3cold)) ||
!pci_power_manageable(dev);
if (/* The device needs to be allowed to go D3cold ... */
dev->no_d3cold || !dev->d3cold_allowed ||
*d3cold_ok = !no_d3cold;
/* ... and if it is wakeup capable to do so from D3cold. */
(device_may_wakeup(&dev->dev) &&
!pci_pme_capable(dev, PCI_D3cold)) ||
return no_d3cold;
/* If it is a bridge it must be allowed to go to D3. */
!pci_power_manageable(dev) ||
/* Hotplug interrupts cannot be delivered if the link is down. */
dev->is_hotplug_bridge)
*d3cold_ok = false;
return !*d3cold_ok;
}
/*
* pci_bridge_d3_update - Update bridge D3 capabilities
* @dev: PCI device which is changed
* @remove: Is the device being removed
*
* Update upstream bridge PM capabilities accordingly depending on if the
* device PM configuration was changed or the device is being removed. The
* change is also propagated upstream.
*/
static void pci_bridge_d3_update(struct pci_dev *dev, bool remove)
void pci_bridge_d3_update(struct pci_dev *dev)
{
bool remove = !device_is_registered(&dev->dev);
struct pci_dev *bridge;
bool d3cold_ok = true;
@ -2292,55 +2308,39 @@ static void pci_bridge_d3_update(struct pci_dev *dev, bool remove)
if (!bridge || !pci_bridge_d3_possible(bridge))
return;
pci_dev_get(bridge);
/*
* If the device is removed we do not care about its D3cold
* capabilities.
* If D3 is currently allowed for the bridge, removing one of its
* children won't change that.
*/
if (remove && bridge->bridge_d3)
return;
/*
* If D3 is currently allowed for the bridge and a child is added or
* changed, disallowance of D3 can only be caused by that child, so
* we only need to check that single device, not any of its siblings.
*
* If D3 is currently not allowed for the bridge, checking the device
* first may allow us to skip checking its siblings.
*/
if (!remove)
pci_dev_check_d3cold(dev, &d3cold_ok);
if (d3cold_ok) {
/*
* We need to go through all children to find out if all of
* them can still go to D3cold.
*/
/*
* If D3 is currently not allowed for the bridge, this may be caused
* either by the device being changed/removed or any of its siblings,
* so we need to go through all children to find out if one of them
* continues to block D3.
*/
if (d3cold_ok && !bridge->bridge_d3)
pci_walk_bus(bridge->subordinate, pci_dev_check_d3cold,
&d3cold_ok);
}
if (bridge->bridge_d3 != d3cold_ok) {
bridge->bridge_d3 = d3cold_ok;
/* Propagate change to upstream bridges */
pci_bridge_d3_update(bridge, false);
pci_bridge_d3_update(bridge);
}
pci_dev_put(bridge);
}
/**
* pci_bridge_d3_device_changed - Update bridge D3 capabilities on change
* @dev: PCI device that was changed
*
* If a device is added or its PM configuration, such as is it allowed to
* enter D3cold, is changed this function updates upstream bridge PM
* capabilities accordingly.
*/
void pci_bridge_d3_device_changed(struct pci_dev *dev)
{
pci_bridge_d3_update(dev, false);
}
/**
* pci_bridge_d3_device_removed - Update bridge D3 capabilities on remove
* @dev: PCI device being removed
*
* Function updates upstream bridge PM capabilities based on other devices
* still left on the bus.
*/
void pci_bridge_d3_device_removed(struct pci_dev *dev)
{
pci_bridge_d3_update(dev, true);
}
/**
@ -2355,7 +2355,7 @@ void pci_d3cold_enable(struct pci_dev *dev)
{
if (dev->no_d3cold) {
dev->no_d3cold = false;
pci_bridge_d3_device_changed(dev);
pci_bridge_d3_update(dev);
}
}
EXPORT_SYMBOL_GPL(pci_d3cold_enable);
@ -2372,7 +2372,7 @@ void pci_d3cold_disable(struct pci_dev *dev)
{
if (!dev->no_d3cold) {
dev->no_d3cold = true;
pci_bridge_d3_device_changed(dev);
pci_bridge_d3_update(dev);
}
}
EXPORT_SYMBOL_GPL(pci_d3cold_disable);

View File

@ -82,8 +82,8 @@ void pci_pm_init(struct pci_dev *dev);
void pci_ea_init(struct pci_dev *dev);
void pci_allocate_cap_save_buffers(struct pci_dev *dev);
void pci_free_cap_save_buffers(struct pci_dev *dev);
void pci_bridge_d3_device_changed(struct pci_dev *dev);
void pci_bridge_d3_device_removed(struct pci_dev *dev);
bool pci_bridge_d3_possible(struct pci_dev *dev);
void pci_bridge_d3_update(struct pci_dev *dev);
static inline void pci_wakeup_event(struct pci_dev *dev)
{

View File

@ -19,6 +19,7 @@
#include <linux/dmi.h>
#include <linux/pci-aspm.h>
#include "../pci.h"
#include "portdrv.h"
#include "aer/aerdrv.h"
@ -149,15 +150,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
pci_save_state(dev);
/*
* Prevent runtime PM if the port is advertising support for PCIe
* hotplug. Otherwise the BIOS hotplug SMI code might not be able
* to enumerate devices behind this port properly (the port is
* powered down preventing all config space accesses to the
* subordinate devices). We can't be sure for native PCIe hotplug
* either so prevent that as well.
*/
if (!dev->is_hotplug_bridge) {
if (pci_bridge_d3_possible(dev)) {
/*
* Keep the port resumed 100ms to make sure things like
* config space accesses from userspace (lspci) will not
@ -175,7 +168,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
static void pcie_portdrv_remove(struct pci_dev *dev)
{
if (!dev->is_hotplug_bridge) {
if (pci_bridge_d3_possible(dev)) {
pm_runtime_forbid(&dev->dev);
pm_runtime_get_noresume(&dev->dev);
pm_runtime_dont_use_autosuspend(&dev->dev);

View File

@ -40,7 +40,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
list_del(&dev->bus_list);
up_write(&pci_bus_sem);
pci_bridge_d3_device_removed(dev);
pci_bridge_d3_update(dev);
pci_free_resources(dev);
put_device(&dev->dev);
}

View File

@ -129,6 +129,10 @@ static int uhci_pci_init(struct usb_hcd *hcd)
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
/* Intel controllers use non-PME wakeup signalling */
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
device_set_run_wake(uhci_dev(uhci), 1);
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;

View File

@ -176,6 +176,7 @@ struct hotplug_params {
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp);
bool pciehp_is_native(struct pci_dev *pdev);
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle);
int acpi_pci_detect_ejectable(acpi_handle handle);
@ -185,5 +186,6 @@ static inline int pci_get_hp_params(struct pci_dev *dev,
{
return -ENODEV;
}
static inline bool pciehp_is_native(struct pci_dev *pdev) { return true; }
#endif
#endif