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:
commit
daaed10443
|
@ -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);
|
||||
|
|
|
@ -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, ®32))
|
||||
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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue