Commit 7d715a6c authored by Shaohua Li's avatar Shaohua Li Committed by Greg Kroah-Hartman

PCI: add PCI Express ASPM support

PCI Express ASPM defines a protocol for PCI Express components in the D0
state to reduce Link power by placing their Links into a low power state
and instructing the other end of the Link to do likewise. This
capability allows hardware-autonomous, dynamic Link power reduction
beyond what is achievable by software-only controlled power management.
However, The device should be configured by software appropriately.
Enabling ASPM will save power, but will introduce device latency.

This patch adds ASPM support in Linux. It introduces a global policy for
ASPM, a sysfs file /sys/module/pcie_aspm/parameters/policy can control
it. The interface can be used as a boot option too. Currently we have
below setting:
        -default, BIOS default setting
        -powersave, highest power saving mode, enable all available ASPM
state and clock power management
        -performance, highest performance, disable ASPM and clock power
management
By default, the 'default' policy is used currently.

In my test, power difference between powersave mode and performance mode
is about 1.3w in a system with 3 PCIE links.

Note: some devices might not work well with aspm, either because chipset
issue or device issue. The patch provide API (pci_disable_link_state),
driver can disable ASPM for specific device.
Signed-off-by: default avatarShaohua Li <shaohua.li@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 657472e9
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/topology.h> #include <linux/topology.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/pci-aspm.h>
#include "pci.h" #include "pci.h"
static int sysfs_initialized; /* = 0 */ static int sysfs_initialized; /* = 0 */
...@@ -650,6 +651,8 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) ...@@ -650,6 +651,8 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
if (pcibios_add_platform_entries(pdev)) if (pcibios_add_platform_entries(pdev))
goto err_rom_file; goto err_rom_file;
pcie_aspm_create_sysfs_dev_files(pdev);
return 0; return 0;
err_rom_file: err_rom_file:
...@@ -679,6 +682,8 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) ...@@ -679,6 +682,8 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
if (!sysfs_initialized) if (!sysfs_initialized)
return; return;
pcie_aspm_remove_sysfs_dev_files(pdev);
if (pdev->cfg_size < 4096) if (pdev->cfg_size < 4096)
sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
else else
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/pci-aspm.h>
#include <asm/dma.h> /* isa_dma_bridge_buggy */ #include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h" #include "pci.h"
...@@ -501,6 +502,9 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state) ...@@ -501,6 +502,9 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
if (need_restore) if (need_restore)
pci_restore_bars(dev); pci_restore_bars(dev);
if (dev->bus->self)
pcie_aspm_pm_state_change(dev->bus->self);
return 0; return 0;
} }
......
...@@ -26,3 +26,23 @@ config HOTPLUG_PCI_PCIE ...@@ -26,3 +26,23 @@ config HOTPLUG_PCI_PCIE
When in doubt, say N. When in doubt, say N.
source "drivers/pci/pcie/aer/Kconfig" source "drivers/pci/pcie/aer/Kconfig"
#
# PCI Express ASPM
#
config PCIEASPM
bool "PCI Express ASPM support(Experimental)"
depends on PCI && EXPERIMENTAL && PCIEPORTBUS
default y
help
This enables PCI Express ASPM (Active State Power Management) and
Clock Power Management. ASPM supports state L0/L0s/L1.
When in doubt, say N.
config PCIEASPM_DEBUG
bool "Debug PCI Express ASPM"
depends on PCIEASPM
default n
help
This enables PCI Express ASPM debug support. It will add per-device
interface to control ASPM.
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
# Makefile for PCI-Express PORT Driver # Makefile for PCI-Express PORT Driver
# #
# Build PCI Express ASPM if needed
obj-$(CONFIG_PCIEASPM) += aspm.o
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
......
This diff is collapsed.
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/pci-aspm.h>
#include "pci.h" #include "pci.h"
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
...@@ -1014,6 +1015,10 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) ...@@ -1014,6 +1015,10 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
break; break;
} }
} }
if (bus->self)
pcie_aspm_init_link_state(bus->self);
return nr; return nr;
} }
......
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci-aspm.h>
#include "pci.h" #include "pci.h"
static void pci_free_resources(struct pci_dev *dev) static void pci_free_resources(struct pci_dev *dev)
...@@ -24,6 +25,9 @@ static void pci_stop_dev(struct pci_dev *dev) ...@@ -24,6 +25,9 @@ static void pci_stop_dev(struct pci_dev *dev)
device_unregister(&dev->dev); device_unregister(&dev->dev);
dev->is_added = 0; dev->is_added = 0;
} }
if (dev->bus->self)
pcie_aspm_exit_link_state(dev);
} }
static void pci_destroy_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev)
......
/*
* aspm.h
*
* PCI Express ASPM defines and function prototypes
*
* Copyright (C) 2007 Intel Corp.
* Zhang Yanmin (yanmin.zhang@intel.com)
* Shaohua Li (shaohua.li@intel.com)
*
* For more information, please consult the following manuals (look at
* http://www.pcisig.com/ for how to get them):
*
* PCI Express Specification
*/
#ifndef LINUX_ASPM_H
#define LINUX_ASPM_H
#include <linux/pci.h>
#define PCIE_LINK_STATE_L0S 1
#define PCIE_LINK_STATE_L1 2
#define PCIE_LINK_STATE_CLKPM 4
#ifdef CONFIG_PCIEASPM
extern void pcie_aspm_init_link_state(struct pci_dev *pdev);
extern void pcie_aspm_exit_link_state(struct pci_dev *pdev);
extern void pcie_aspm_pm_state_change(struct pci_dev *pdev);
extern void pci_disable_link_state(struct pci_dev *pdev, int state);
#else
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev)
{
}
static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev)
{
}
static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev)
{
}
static inline void pci_disable_link_state(struct pci_dev *pdev, int state)
{
}
#endif
#ifdef CONFIG_PCIEASPM_DEBUG /* this depends on CONFIG_PCIEASPM */
extern void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev);
extern void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev);
#else
static inline void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
{
}
static inline void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
{
}
#endif
#endif /* LINUX_ASPM_H */
...@@ -128,6 +128,7 @@ struct pci_cap_saved_state { ...@@ -128,6 +128,7 @@ struct pci_cap_saved_state {
u32 data[0]; u32 data[0];
}; };
struct pcie_link_state;
/* /*
* The pci_dev structure is used to describe PCI devices. * The pci_dev structure is used to describe PCI devices.
*/ */
...@@ -164,6 +165,10 @@ struct pci_dev { ...@@ -164,6 +165,10 @@ struct pci_dev {
this is D0-D3, D0 being fully functional, this is D0-D3, D0 being fully functional,
and D3 being off. */ and D3 being off. */
#ifdef CONFIG_PCIEASPM
struct pcie_link_state *link_state; /* ASPM link state. */
#endif
pci_channel_state_t error_state; /* current connectivity state */ pci_channel_state_t error_state; /* current connectivity state */
struct device dev; /* Generic device interface */ struct device dev; /* Generic device interface */
......
...@@ -395,9 +395,17 @@ ...@@ -395,9 +395,17 @@
#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ #define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */
#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ #define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */
#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ #define PCI_EXP_LNKCAP 12 /* Link Capabilities */
#define PCI_EXP_LNKCAP_ASPMS 0xc00 /* ASPM Support */
#define PCI_EXP_LNKCAP_L0SEL 0x7000 /* L0s Exit Latency */
#define PCI_EXP_LNKCAP_L1EL 0x38000 /* L1 Exit Latency */
#define PCI_EXP_LNKCAP_CLKPM 0x40000 /* L1 Clock Power Management */
#define PCI_EXP_LNKCTL 16 /* Link Control */ #define PCI_EXP_LNKCTL 16 /* Link Control */
#define PCI_EXP_LNKCTL_RL 0x20 /* Retrain Link */
#define PCI_EXP_LNKCTL_CCC 0x40 /* Common Clock COnfiguration */
#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ #define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */
#define PCI_EXP_LNKSTA 18 /* Link Status */ #define PCI_EXP_LNKSTA 18 /* Link Status */
#define PCI_EXP_LNKSTA_LT 0x800 /* Link Training */
#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */
#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */
#define PCI_EXP_SLTCTL 24 /* Slot Control */ #define PCI_EXP_SLTCTL 24 /* Slot Control */
#define PCI_EXP_SLTSTA 26 /* Slot Status */ #define PCI_EXP_SLTSTA 26 /* Slot Status */
......
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