Skip to content

Commit 9d26d3a

Browse files
westeribjorn-helgaas
authored andcommitted
PCI: Put PCIe ports into D3 during suspend
Currently the Linux PCI core does not touch power state of PCI bridges and PCIe ports when system suspend is entered. Leaving them in D0 consumes power unnecessarily and may prevent the CPU from entering deeper C-states. With recent PCIe hardware we can power down the ports to save power given that we take into account few restrictions: - The PCIe port hardware is recent enough, starting from 2015. - Devices connected to PCIe ports are effectively in D3cold once the port is transitioned to D3 (the config space is not accessible anymore and the link may be powered down). - Devices behind the PCIe port need to be allowed to transition to D3cold and back. There is a way both drivers and userspace can forbid this. - If the device behind the PCIe port is capable of waking the system it needs to be able to do so from D3cold. This patch adds a new flag to struct pci_device called 'bridge_d3'. This flag is set and cleared by the PCI core whenever there is a change in power management state of any of the devices behind the PCIe port. When system later on is suspended we only need to check this flag and if it is true transition the port to D3 otherwise we leave it in D0. Also provide override mechanism via command line parameter "pcie_port_pm=[off|force]" that can be used to disable or enable the feature regardless of the BIOS manufacturing date. Tested-by: Lukas Wunner <[email protected]> Signed-off-by: Mika Westerberg <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Acked-by: Rafael J. Wysocki <[email protected]>
1 parent 43f7f88 commit 9d26d3a

File tree

9 files changed

+203
-5
lines changed

9 files changed

+203
-5
lines changed

Documentation/kernel-parameters.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,6 +3047,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
30473047
compat Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
30483048
ports driver.
30493049

3050+
pcie_port_pm= [PCIE] PCIe port power management handling:
3051+
off Disable power management of all PCIe ports
3052+
force Forcibly enable power management of all PCIe ports
3053+
30503054
pcie_pme= [PCIE,PM] Native PCIe PME signaling options:
30513055
nomsi Do not use MSI for native PCIe PME signaling (this makes
30523056
all PCIe root ports use INTx for all services).

drivers/pci/bus.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ void pci_bus_add_device(struct pci_dev *dev)
291291
pci_fixup_device(pci_fixup_final, dev);
292292
pci_create_sysfs_dev_files(dev);
293293
pci_proc_attach_device(dev);
294+
pci_bridge_d3_device_changed(dev);
294295

295296
dev->match_driver = true;
296297
retval = device_attach(&dev->dev);

drivers/pci/pci-driver.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
777777

778778
if (!pci_dev->state_saved) {
779779
pci_save_state(pci_dev);
780-
if (!pci_has_subordinate(pci_dev))
780+
if (pci_power_manageable(pci_dev))
781781
pci_prepare_to_sleep(pci_dev);
782782
}
783783

@@ -1144,7 +1144,6 @@ static int pci_pm_runtime_suspend(struct device *dev)
11441144
return -ENOSYS;
11451145

11461146
pci_dev->state_saved = false;
1147-
pci_dev->no_d3cold = false;
11481147
error = pm->runtime_suspend(dev);
11491148
if (error) {
11501149
/*
@@ -1161,8 +1160,6 @@ static int pci_pm_runtime_suspend(struct device *dev)
11611160

11621161
return error;
11631162
}
1164-
if (!pci_dev->d3cold_allowed)
1165-
pci_dev->no_d3cold = true;
11661163

11671164
pci_fixup_device(pci_fixup_suspend, pci_dev);
11681165

drivers/pci/pci-sysfs.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,11 @@ static ssize_t d3cold_allowed_store(struct device *dev,
406406
return -EINVAL;
407407

408408
pdev->d3cold_allowed = !!val;
409+
if (pdev->d3cold_allowed)
410+
pci_d3cold_enable(pdev);
411+
else
412+
pci_d3cold_disable(pdev);
413+
409414
pm_runtime_resume(dev);
410415

411416
return count;

drivers/pci/pci.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <linux/kernel.h>
1111
#include <linux/delay.h>
12+
#include <linux/dmi.h>
1213
#include <linux/init.h>
1314
#include <linux/of.h>
1415
#include <linux/of_pci.h>
@@ -101,6 +102,21 @@ unsigned int pcibios_max_latency = 255;
101102
/* If set, the PCIe ARI capability will not be used. */
102103
static bool pcie_ari_disabled;
103104

105+
/* Disable bridge_d3 for all PCIe ports */
106+
static bool pci_bridge_d3_disable;
107+
/* Force bridge_d3 for all PCIe ports */
108+
static bool pci_bridge_d3_force;
109+
110+
static int __init pcie_port_pm_setup(char *str)
111+
{
112+
if (!strcmp(str, "off"))
113+
pci_bridge_d3_disable = true;
114+
else if (!strcmp(str, "force"))
115+
pci_bridge_d3_force = true;
116+
return 1;
117+
}
118+
__setup("pcie_port_pm=", pcie_port_pm_setup);
119+
104120
/**
105121
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
106122
* @bus: pointer to PCI bus structure to search
@@ -2155,6 +2171,164 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
21552171
pm_runtime_put_sync(parent);
21562172
}
21572173

2174+
/**
2175+
* pci_bridge_d3_possible - Is it possible to put the bridge into D3
2176+
* @bridge: Bridge to check
2177+
*
2178+
* This function checks if it is possible to move the bridge to D3.
2179+
* Currently we only allow D3 for recent enough PCIe ports.
2180+
*/
2181+
static bool pci_bridge_d3_possible(struct pci_dev *bridge)
2182+
{
2183+
unsigned int year;
2184+
2185+
if (!pci_is_pcie(bridge))
2186+
return false;
2187+
2188+
switch (pci_pcie_type(bridge)) {
2189+
case PCI_EXP_TYPE_ROOT_PORT:
2190+
case PCI_EXP_TYPE_UPSTREAM:
2191+
case PCI_EXP_TYPE_DOWNSTREAM:
2192+
if (pci_bridge_d3_disable)
2193+
return false;
2194+
if (pci_bridge_d3_force)
2195+
return true;
2196+
2197+
/*
2198+
* It should be safe to put PCIe ports from 2015 or newer
2199+
* to D3.
2200+
*/
2201+
if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
2202+
year >= 2015) {
2203+
return true;
2204+
}
2205+
break;
2206+
}
2207+
2208+
return false;
2209+
}
2210+
2211+
static int pci_dev_check_d3cold(struct pci_dev *dev, void *data)
2212+
{
2213+
bool *d3cold_ok = data;
2214+
bool no_d3cold;
2215+
2216+
/*
2217+
* The device needs to be allowed to go D3cold and if it is wake
2218+
* capable to do so from D3cold.
2219+
*/
2220+
no_d3cold = dev->no_d3cold || !dev->d3cold_allowed ||
2221+
(device_may_wakeup(&dev->dev) && !pci_pme_capable(dev, PCI_D3cold)) ||
2222+
!pci_power_manageable(dev);
2223+
2224+
*d3cold_ok = !no_d3cold;
2225+
2226+
return no_d3cold;
2227+
}
2228+
2229+
/*
2230+
* pci_bridge_d3_update - Update bridge D3 capabilities
2231+
* @dev: PCI device which is changed
2232+
* @remove: Is the device being removed
2233+
*
2234+
* Update upstream bridge PM capabilities accordingly depending on if the
2235+
* device PM configuration was changed or the device is being removed. The
2236+
* change is also propagated upstream.
2237+
*/
2238+
static void pci_bridge_d3_update(struct pci_dev *dev, bool remove)
2239+
{
2240+
struct pci_dev *bridge;
2241+
bool d3cold_ok = true;
2242+
2243+
bridge = pci_upstream_bridge(dev);
2244+
if (!bridge || !pci_bridge_d3_possible(bridge))
2245+
return;
2246+
2247+
pci_dev_get(bridge);
2248+
/*
2249+
* If the device is removed we do not care about its D3cold
2250+
* capabilities.
2251+
*/
2252+
if (!remove)
2253+
pci_dev_check_d3cold(dev, &d3cold_ok);
2254+
2255+
if (d3cold_ok) {
2256+
/*
2257+
* We need to go through all children to find out if all of
2258+
* them can still go to D3cold.
2259+
*/
2260+
pci_walk_bus(bridge->subordinate, pci_dev_check_d3cold,
2261+
&d3cold_ok);
2262+
}
2263+
2264+
if (bridge->bridge_d3 != d3cold_ok) {
2265+
bridge->bridge_d3 = d3cold_ok;
2266+
/* Propagate change to upstream bridges */
2267+
pci_bridge_d3_update(bridge, false);
2268+
}
2269+
2270+
pci_dev_put(bridge);
2271+
}
2272+
2273+
/**
2274+
* pci_bridge_d3_device_changed - Update bridge D3 capabilities on change
2275+
* @dev: PCI device that was changed
2276+
*
2277+
* If a device is added or its PM configuration, such as is it allowed to
2278+
* enter D3cold, is changed this function updates upstream bridge PM
2279+
* capabilities accordingly.
2280+
*/
2281+
void pci_bridge_d3_device_changed(struct pci_dev *dev)
2282+
{
2283+
pci_bridge_d3_update(dev, false);
2284+
}
2285+
2286+
/**
2287+
* pci_bridge_d3_device_removed - Update bridge D3 capabilities on remove
2288+
* @dev: PCI device being removed
2289+
*
2290+
* Function updates upstream bridge PM capabilities based on other devices
2291+
* still left on the bus.
2292+
*/
2293+
void pci_bridge_d3_device_removed(struct pci_dev *dev)
2294+
{
2295+
pci_bridge_d3_update(dev, true);
2296+
}
2297+
2298+
/**
2299+
* pci_d3cold_enable - Enable D3cold for device
2300+
* @dev: PCI device to handle
2301+
*
2302+
* This function can be used in drivers to enable D3cold from the device
2303+
* they handle. It also updates upstream PCI bridge PM capabilities
2304+
* accordingly.
2305+
*/
2306+
void pci_d3cold_enable(struct pci_dev *dev)
2307+
{
2308+
if (dev->no_d3cold) {
2309+
dev->no_d3cold = false;
2310+
pci_bridge_d3_device_changed(dev);
2311+
}
2312+
}
2313+
EXPORT_SYMBOL_GPL(pci_d3cold_enable);
2314+
2315+
/**
2316+
* pci_d3cold_disable - Disable D3cold for device
2317+
* @dev: PCI device to handle
2318+
*
2319+
* This function can be used in drivers to disable D3cold from the device
2320+
* they handle. It also updates upstream PCI bridge PM capabilities
2321+
* accordingly.
2322+
*/
2323+
void pci_d3cold_disable(struct pci_dev *dev)
2324+
{
2325+
if (!dev->no_d3cold) {
2326+
dev->no_d3cold = true;
2327+
pci_bridge_d3_device_changed(dev);
2328+
}
2329+
}
2330+
EXPORT_SYMBOL_GPL(pci_d3cold_disable);
2331+
21582332
/**
21592333
* pci_pm_init - Initialize PM functions of given PCI device
21602334
* @dev: PCI device to handle.
@@ -2189,6 +2363,7 @@ void pci_pm_init(struct pci_dev *dev)
21892363
dev->pm_cap = pm;
21902364
dev->d3_delay = PCI_PM_D3_WAIT;
21912365
dev->d3cold_delay = PCI_PM_D3COLD_WAIT;
2366+
dev->bridge_d3 = pci_bridge_d3_possible(dev);
21922367
dev->d3cold_allowed = true;
21932368

21942369
dev->d1_support = false;

drivers/pci/pci.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ void pci_pm_init(struct pci_dev *dev);
8282
void pci_ea_init(struct pci_dev *dev);
8383
void pci_allocate_cap_save_buffers(struct pci_dev *dev);
8484
void pci_free_cap_save_buffers(struct pci_dev *dev);
85+
void pci_bridge_d3_device_changed(struct pci_dev *dev);
86+
void pci_bridge_d3_device_removed(struct pci_dev *dev);
8587

8688
static inline void pci_wakeup_event(struct pci_dev *dev)
8789
{
@@ -94,6 +96,15 @@ static inline bool pci_has_subordinate(struct pci_dev *pci_dev)
9496
return !!(pci_dev->subordinate);
9597
}
9698

99+
static inline bool pci_power_manageable(struct pci_dev *pci_dev)
100+
{
101+
/*
102+
* Currently we allow normal PCI devices and PCI bridges transition
103+
* into D3 if their bridge_d3 is set.
104+
*/
105+
return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3;
106+
}
107+
97108
struct pci_vpd_ops {
98109
ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
99110
ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);

drivers/pci/remove.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ static void pci_remove_bus_device(struct pci_dev *dev)
9696
dev->subordinate = NULL;
9797
}
9898

99+
pci_bridge_d3_device_removed(dev);
100+
99101
pci_destroy_dev(dev);
100102
}
101103

drivers/usb/host/xhci-pci.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
382382
* need to have the registers polled during D3, so avoid D3cold.
383383
*/
384384
if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
385-
pdev->no_d3cold = true;
385+
pci_d3cold_disable(pdev);
386386

387387
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
388388
xhci_pme_quirk(hcd);

include/linux/pci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ struct pci_dev {
294294
unsigned int d2_support:1; /* Low power state D2 is supported */
295295
unsigned int no_d1d2:1; /* D1 and D2 are forbidden */
296296
unsigned int no_d3cold:1; /* D3cold is forbidden */
297+
unsigned int bridge_d3:1; /* Allow D3 for bridge */
297298
unsigned int d3cold_allowed:1; /* D3cold is allowed by user */
298299
unsigned int mmio_always_on:1; /* disallow turning off io/mem
299300
decoding during bar sizing */
@@ -1083,6 +1084,8 @@ int pci_back_from_sleep(struct pci_dev *dev);
10831084
bool pci_dev_run_wake(struct pci_dev *dev);
10841085
bool pci_check_pme_status(struct pci_dev *dev);
10851086
void pci_pme_wakeup_bus(struct pci_bus *bus);
1087+
void pci_d3cold_enable(struct pci_dev *dev);
1088+
void pci_d3cold_disable(struct pci_dev *dev);
10861089

10871090
static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state,
10881091
bool enable)

0 commit comments

Comments
 (0)