PCI: Simplify PCI device PM code
If the offset of PCI device's PM capability in its configuration space, the mask of states that the device supports PME# from and the D1 and D2 support bits are cached in the corresponding struct pci_dev, the PCI device PM code can be simplified quite a bit. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
404cc2d8ce
commit
337001b6c4
|
@ -419,7 +419,6 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
|
||||||
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
|
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
|
||||||
* given PCI device
|
* given PCI device
|
||||||
* @dev: PCI device to handle.
|
* @dev: PCI device to handle.
|
||||||
* @pm: PCI PM capability offset of the device.
|
|
||||||
* @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
|
* @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
|
||||||
*
|
*
|
||||||
* RETURN VALUE:
|
* RETURN VALUE:
|
||||||
|
@ -430,12 +429,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
|
||||||
* 0 if device's power state has been successfully changed.
|
* 0 if device's power state has been successfully changed.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
|
pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||||
{
|
{
|
||||||
u16 pmcsr, pmc;
|
u16 pmcsr;
|
||||||
bool need_restore = false;
|
bool need_restore = false;
|
||||||
|
|
||||||
if (!pm)
|
if (!dev->pm_cap)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (state < PCI_D0 || state > PCI_D3hot)
|
if (state < PCI_D0 || state > PCI_D3hot)
|
||||||
|
@ -455,20 +454,12 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
|
|
||||||
|
|
||||||
if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
|
|
||||||
dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
|
|
||||||
pmc & PCI_PM_CAP_VER_MASK);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if this device supports the desired state */
|
/* check if this device supports the desired state */
|
||||||
if ((state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
|
if ((state == PCI_D1 && !dev->d1_support)
|
||||||
|| (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2)))
|
|| (state == PCI_D2 && !dev->d2_support))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
|
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||||
|
|
||||||
/* If we're (effectively) in D3, force entire word to 0.
|
/* If we're (effectively) in D3, force entire word to 0.
|
||||||
* This doesn't affect PME_Status, disables PME_En, and
|
* This doesn't affect PME_Status, disables PME_En, and
|
||||||
|
@ -492,7 +483,7 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enter specified state */
|
/* enter specified state */
|
||||||
pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
|
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
|
||||||
|
|
||||||
/* Mandatory power management transition delays */
|
/* Mandatory power management transition delays */
|
||||||
/* see PCI PM 1.1 5.6.1 table 18 */
|
/* see PCI PM 1.1 5.6.1 table 18 */
|
||||||
|
@ -528,14 +519,13 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
|
||||||
* pci_update_current_state - Read PCI power state of given device from its
|
* pci_update_current_state - Read PCI power state of given device from its
|
||||||
* PCI PM registers and cache it
|
* PCI PM registers and cache it
|
||||||
* @dev: PCI device to handle.
|
* @dev: PCI device to handle.
|
||||||
* @pm: PCI PM capability offset of the device.
|
|
||||||
*/
|
*/
|
||||||
static void pci_update_current_state(struct pci_dev *dev, int pm)
|
static void pci_update_current_state(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (pm) {
|
if (dev->pm_cap) {
|
||||||
u16 pmcsr;
|
u16 pmcsr;
|
||||||
|
|
||||||
pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
|
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||||
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -557,7 +547,7 @@ static void pci_update_current_state(struct pci_dev *dev, int pm)
|
||||||
*/
|
*/
|
||||||
int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||||
{
|
{
|
||||||
int pm, error;
|
int error;
|
||||||
|
|
||||||
/* bound the state we're entering */
|
/* bound the state we're entering */
|
||||||
if (state > PCI_D3hot)
|
if (state > PCI_D3hot)
|
||||||
|
@ -572,9 +562,6 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||||
*/
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Find PCI PM capability in the list */
|
|
||||||
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
|
||||||
|
|
||||||
if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
|
if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
|
||||||
/*
|
/*
|
||||||
* Allow the platform to change the state, for example via ACPI
|
* Allow the platform to change the state, for example via ACPI
|
||||||
|
@ -582,16 +569,16 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||||
*/
|
*/
|
||||||
int ret = platform_pci_set_power_state(dev, PCI_D0);
|
int ret = platform_pci_set_power_state(dev, PCI_D0);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
pci_update_current_state(dev, pm);
|
pci_update_current_state(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = pci_raw_set_power_state(dev, pm, state);
|
error = pci_raw_set_power_state(dev, state);
|
||||||
|
|
||||||
if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
|
if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
|
||||||
/* Allow the platform to finalize the transition */
|
/* Allow the platform to finalize the transition */
|
||||||
int ret = platform_pci_set_power_state(dev, state);
|
int ret = platform_pci_set_power_state(dev, state);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
pci_update_current_state(dev, pm);
|
pci_update_current_state(dev);
|
||||||
error = 0;
|
error = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1050,48 +1037,38 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
|
||||||
/**
|
/**
|
||||||
* pci_pme_capable - check the capability of PCI device to generate PME#
|
* pci_pme_capable - check the capability of PCI device to generate PME#
|
||||||
* @dev: PCI device to handle.
|
* @dev: PCI device to handle.
|
||||||
* @pm: PCI PM capability offset of the device.
|
|
||||||
* @state: PCI state from which device will issue PME#.
|
* @state: PCI state from which device will issue PME#.
|
||||||
*/
|
*/
|
||||||
static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state)
|
static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
|
||||||
{
|
{
|
||||||
u16 pmc;
|
if (!dev->pm_cap)
|
||||||
|
|
||||||
if (!pm)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Check device's ability to generate PME# from given state */
|
return !!(dev->pme_support & (1 << state));
|
||||||
pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
|
|
||||||
|
|
||||||
pmc &= PCI_PM_CAP_PME_MASK;
|
|
||||||
pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */
|
|
||||||
|
|
||||||
return !!(pmc & (1 << state));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_pme_active - enable or disable PCI device's PME# function
|
* pci_pme_active - enable or disable PCI device's PME# function
|
||||||
* @dev: PCI device to handle.
|
* @dev: PCI device to handle.
|
||||||
* @pm: PCI PM capability offset of the device.
|
|
||||||
* @enable: 'true' to enable PME# generation; 'false' to disable it.
|
* @enable: 'true' to enable PME# generation; 'false' to disable it.
|
||||||
*
|
*
|
||||||
* The caller must verify that the device is capable of generating PME# before
|
* The caller must verify that the device is capable of generating PME# before
|
||||||
* calling this function with @enable equal to 'true'.
|
* calling this function with @enable equal to 'true'.
|
||||||
*/
|
*/
|
||||||
static void pci_pme_active(struct pci_dev *dev, int pm, bool enable)
|
static void pci_pme_active(struct pci_dev *dev, bool enable)
|
||||||
{
|
{
|
||||||
u16 pmcsr;
|
u16 pmcsr;
|
||||||
|
|
||||||
if (!pm)
|
if (!dev->pm_cap)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
|
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||||
/* Clear PME_Status by writing 1 to it and enable PME# */
|
/* Clear PME_Status by writing 1 to it and enable PME# */
|
||||||
pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
|
pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
|
||||||
if (!enable)
|
if (!enable)
|
||||||
pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
|
pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
|
||||||
|
|
||||||
pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
|
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
|
||||||
|
|
||||||
dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
|
dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
|
||||||
enable ? "enabled" : "disabled");
|
enable ? "enabled" : "disabled");
|
||||||
|
@ -1118,7 +1095,6 @@ static void pci_pme_active(struct pci_dev *dev, int pm, bool enable)
|
||||||
*/
|
*/
|
||||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
|
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
|
||||||
{
|
{
|
||||||
int pm;
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
bool pme_done = false;
|
bool pme_done = false;
|
||||||
|
|
||||||
|
@ -1134,9 +1110,8 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
|
||||||
if (!enable && platform_pci_can_wakeup(dev))
|
if (!enable && platform_pci_can_wakeup(dev))
|
||||||
error = platform_pci_sleep_wake(dev, false);
|
error = platform_pci_sleep_wake(dev, false);
|
||||||
|
|
||||||
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
if (!enable || pci_pme_capable(dev, state)) {
|
||||||
if (!enable || pci_pme_capable(dev, pm, state)) {
|
pci_pme_active(dev, enable);
|
||||||
pci_pme_active(dev, pm, enable);
|
|
||||||
pme_done = true;
|
pme_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,7 +1133,6 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
|
||||||
int pci_prepare_to_sleep(struct pci_dev *dev)
|
int pci_prepare_to_sleep(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
pci_power_t target_state = PCI_D3hot;
|
pci_power_t target_state = PCI_D3hot;
|
||||||
int pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (platform_pci_power_manageable(dev)) {
|
if (platform_pci_power_manageable(dev)) {
|
||||||
|
@ -1186,23 +1160,14 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
|
||||||
* wake-up events, make it the target state and enable device
|
* wake-up events, make it the target state and enable device
|
||||||
* to generate PME#.
|
* to generate PME#.
|
||||||
*/
|
*/
|
||||||
u16 pmc;
|
if (!dev->pm_cap)
|
||||||
|
|
||||||
if (!pm)
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
|
if (dev->pme_support) {
|
||||||
if (pmc & PCI_PM_CAP_PME_MASK) {
|
while (target_state
|
||||||
if (!(pmc & PCI_PM_CAP_PME_D3)) {
|
&& !(dev->pme_support & (1 << target_state)))
|
||||||
/* Device cannot generate PME# from D3_hot */
|
target_state--;
|
||||||
if (pmc & PCI_PM_CAP_PME_D2)
|
pci_pme_active(dev, true);
|
||||||
target_state = PCI_D2;
|
|
||||||
else if (pmc & PCI_PM_CAP_PME_D1)
|
|
||||||
target_state = PCI_D1;
|
|
||||||
else
|
|
||||||
target_state = PCI_D0;
|
|
||||||
}
|
|
||||||
pci_pme_active(dev, pm, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1236,6 +1201,8 @@ void pci_pm_init(struct pci_dev *dev)
|
||||||
int pm;
|
int pm;
|
||||||
u16 pmc;
|
u16 pmc;
|
||||||
|
|
||||||
|
dev->pm_cap = 0;
|
||||||
|
|
||||||
/* find PCI PM capability in list */
|
/* find PCI PM capability in list */
|
||||||
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||||
if (!pm)
|
if (!pm)
|
||||||
|
@ -1249,7 +1216,23 @@ void pci_pm_init(struct pci_dev *dev)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pmc & PCI_PM_CAP_PME_MASK) {
|
dev->pm_cap = pm;
|
||||||
|
|
||||||
|
dev->d1_support = false;
|
||||||
|
dev->d2_support = false;
|
||||||
|
if (!pci_no_d1d2(dev)) {
|
||||||
|
if (pmc & PCI_PM_CAP_D1) {
|
||||||
|
dev_printk(KERN_DEBUG, &dev->dev, "supports D1\n");
|
||||||
|
dev->d1_support = true;
|
||||||
|
}
|
||||||
|
if (pmc & PCI_PM_CAP_D2) {
|
||||||
|
dev_printk(KERN_DEBUG, &dev->dev, "supports D2\n");
|
||||||
|
dev->d2_support = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pmc &= PCI_PM_CAP_PME_MASK;
|
||||||
|
if (pmc) {
|
||||||
dev_printk(KERN_INFO, &dev->dev,
|
dev_printk(KERN_INFO, &dev->dev,
|
||||||
"PME# supported from%s%s%s%s%s\n",
|
"PME# supported from%s%s%s%s%s\n",
|
||||||
(pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
|
(pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
|
||||||
|
@ -1257,6 +1240,7 @@ void pci_pm_init(struct pci_dev *dev)
|
||||||
(pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
|
(pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
|
||||||
(pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
|
(pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
|
||||||
(pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
|
(pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
|
||||||
|
dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
|
||||||
/*
|
/*
|
||||||
* Make device's PM flags reflect the wake-up capability, but
|
* Make device's PM flags reflect the wake-up capability, but
|
||||||
* let the user space enable it to wake up the system as needed.
|
* let the user space enable it to wake up the system as needed.
|
||||||
|
@ -1264,7 +1248,9 @@ void pci_pm_init(struct pci_dev *dev)
|
||||||
device_set_wakeup_capable(&dev->dev, true);
|
device_set_wakeup_capable(&dev->dev, true);
|
||||||
device_set_wakeup_enable(&dev->dev, false);
|
device_set_wakeup_enable(&dev->dev, false);
|
||||||
/* Disable the PME# generation functionality */
|
/* Disable the PME# generation functionality */
|
||||||
pci_pme_active(dev, pm, false);
|
pci_pme_active(dev, false);
|
||||||
|
} else {
|
||||||
|
dev->pme_support = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,13 @@ struct pci_dev {
|
||||||
pci_power_t current_state; /* Current operating state. In ACPI-speak,
|
pci_power_t current_state; /* Current operating state. In ACPI-speak,
|
||||||
this is D0-D3, D0 being fully functional,
|
this is D0-D3, D0 being fully functional,
|
||||||
and D3 being off. */
|
and D3 being off. */
|
||||||
|
int pm_cap; /* PM capability offset in the
|
||||||
|
configuration space */
|
||||||
|
unsigned int pme_support:5; /* Bitmask of states from which PME#
|
||||||
|
can be generated */
|
||||||
|
unsigned int d1_support:1; /* Low power state D1 is supported */
|
||||||
|
unsigned int d2_support:1; /* Low power state D2 is supported */
|
||||||
|
unsigned int no_d1d2:1; /* Only allow D0 and D3 */
|
||||||
|
|
||||||
#ifdef CONFIG_PCIEASPM
|
#ifdef CONFIG_PCIEASPM
|
||||||
struct pcie_link_state *link_state; /* ASPM link state. */
|
struct pcie_link_state *link_state; /* ASPM link state. */
|
||||||
|
@ -201,7 +208,6 @@ struct pci_dev {
|
||||||
unsigned int is_added:1;
|
unsigned int is_added:1;
|
||||||
unsigned int is_busmaster:1; /* device is busmaster */
|
unsigned int is_busmaster:1; /* device is busmaster */
|
||||||
unsigned int no_msi:1; /* device may not use msi */
|
unsigned int no_msi:1; /* device may not use msi */
|
||||||
unsigned int no_d1d2:1; /* only allow d0 or d3 */
|
|
||||||
unsigned int block_ucfg_access:1; /* userspace config space access is blocked */
|
unsigned int block_ucfg_access:1; /* userspace config space access is blocked */
|
||||||
unsigned int broken_parity_status:1; /* Device generates false positive parity */
|
unsigned int broken_parity_status:1; /* Device generates false positive parity */
|
||||||
unsigned int msi_enabled:1;
|
unsigned int msi_enabled:1;
|
||||||
|
|
|
@ -231,6 +231,7 @@
|
||||||
#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */
|
#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */
|
||||||
#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */
|
#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */
|
||||||
#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
|
#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
|
||||||
|
#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */
|
||||||
#define PCI_PM_CTRL 4 /* PM control and status register */
|
#define PCI_PM_CTRL 4 /* PM control and status register */
|
||||||
#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
|
#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
|
||||||
#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */
|
#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */
|
||||||
|
|
Loading…
Reference in a new issue