summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
patch |
inline | side by side (from parent 1:
404cc2d)
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>
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
* given PCI device
* @dev: PCI device to handle.
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
* given PCI device
* @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.
*
* RETURN VALUE:
* @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
*
* RETURN VALUE:
* 0 if device's power state has been successfully changed.
*/
static int
* 0 if device's power state has been successfully changed.
*/
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)
bool need_restore = false;
bool need_restore = false;
return -EIO;
if (state < PCI_D0 || state > PCI_D3hot)
return -EIO;
if (state < PCI_D0 || state > PCI_D3hot)
- 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))
- || (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2)))
+ if ((state == PCI_D1 && !dev->d1_support)
+ || (state == PCI_D2 && !dev->d2_support))
- 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.
* This doesn't affect PME_Status, disables PME_En, and
/* If we're (effectively) in D3, force entire word to 0.
* This doesn't affect PME_Status, disables PME_En, and
}
/* 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 */
/* see PCI PM 1.1 5.6.1 table 18 */
/* Mandatory power management transition delays */
/* see PCI PM 1.1 5.6.1 table 18 */
* pci_update_current_state - Read PCI power state of given device from its
* PCI PM registers and cache it
* @dev: PCI device to handle.
* pci_update_current_state - Read PCI power state of given device from its
* PCI PM registers and cache it
* @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)
- 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);
}
}
*/
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)
{
/* bound the state we're entering */
if (state > PCI_D3hot)
/* bound the state we're entering */
if (state > PCI_D3hot)
- /* 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)) {
/*
* Allow the platform to change the state, for example via ACPI
if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
/*
* Allow the platform to change the state, for example via ACPI
*/
int ret = platform_pci_set_power_state(dev, PCI_D0);
if (!ret)
*/
int ret = platform_pci_set_power_state(dev, PCI_D0);
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)) {
/* Allow the platform to finalize the transition */
int ret = platform_pci_set_power_state(dev, state);
if (!ret) {
if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
/* Allow the platform to finalize the transition */
int ret = platform_pci_set_power_state(dev, state);
if (!ret) {
- pci_update_current_state(dev, pm);
+ pci_update_current_state(dev);
/**
* pci_pme_capable - check the capability of PCI device to generate PME#
* @dev: PCI device to handle.
/**
* pci_pme_capable - check the capability of PCI device to generate PME#
* @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)
- /* Check device's ability to generate PME# from given 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));
+ return !!(dev->pme_support & (1 << state));
}
/**
* pci_pme_active - enable or disable PCI device's PME# function
* @dev: PCI device to handle.
}
/**
* pci_pme_active - enable or disable PCI device's PME# function
* @dev: PCI device to handle.
- * @pm: PCI PM capability offset of the device.
* @enable: 'true' to enable PME# generation; 'false' to disable it.
*
* The caller must verify that the device is capable of generating PME# before
* calling this function with @enable equal to 'true'.
*/
* @enable: 'true' to enable PME# generation; 'false' to disable it.
*
* The caller must verify that the device is capable of generating PME# before
* 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)
- 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# */
pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
if (!enable)
pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
/* Clear PME_Status by writing 1 to it and enable PME# */
pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
if (!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",
enable ? "enabled" : "disabled");
dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
enable ? "enabled" : "disabled");
*/
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 error = 0;
bool pme_done = false;
int error = 0;
bool pme_done = false;
if (!enable && platform_pci_can_wakeup(dev))
error = platform_pci_sleep_wake(dev, false);
if (!enable && platform_pci_can_wakeup(dev))
error = platform_pci_sleep_wake(dev, false);
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- if (!enable || pci_pme_capable(dev, pm, state)) {
- pci_pme_active(dev, pm, enable);
+ if (!enable || pci_pme_capable(dev, state)) {
+ pci_pme_active(dev, enable);
int pci_prepare_to_sleep(struct pci_dev *dev)
{
pci_power_t target_state = PCI_D3hot;
int pci_prepare_to_sleep(struct pci_dev *dev)
{
pci_power_t target_state = PCI_D3hot;
- int pm = pci_find_capability(dev, PCI_CAP_ID_PM);
int error;
if (platform_pci_power_manageable(dev)) {
int error;
if (platform_pci_power_manageable(dev)) {
* wake-up events, make it the target state and enable device
* to generate PME#.
*/
* wake-up events, make it the target state and enable device
* to generate PME#.
*/
- pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
- if (pmc & PCI_PM_CAP_PME_MASK) {
- if (!(pmc & PCI_PM_CAP_PME_D3)) {
- /* Device cannot generate PME# from D3_hot */
- if (pmc & PCI_PM_CAP_PME_D2)
- 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);
+ if (dev->pme_support) {
+ while (target_state
+ && !(dev->pme_support & (1 << target_state)))
+ target_state--;
+ pci_pme_active(dev, true);
/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (!pm)
/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (!pm)
- 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,
"PME# supported from%s%s%s%s%s\n",
(pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
dev_printk(KERN_INFO, &dev->dev,
"PME# supported from%s%s%s%s%s\n",
(pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
(pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
(pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
(pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
(pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
(pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
(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
* let the user space enable it to wake up the system as needed.
/*
* Make device's PM flags reflect the wake-up capability, but
* let the user space enable it to wake up the system as needed.
device_set_wakeup_capable(&dev->dev, true);
device_set_wakeup_enable(&dev->dev, false);
/* Disable the PME# generation functionality */
device_set_wakeup_capable(&dev->dev, true);
device_set_wakeup_enable(&dev->dev, false);
/* Disable the PME# generation functionality */
- pci_pme_active(dev, pm, false);
+ pci_pme_active(dev, false);
+ } else {
+ dev->pme_support = 0;
pci_power_t current_state; /* Current operating state. In ACPI-speak,
this is D0-D3, D0 being fully functional,
and D3 being off. */
pci_power_t current_state; /* Current operating state. In ACPI-speak,
this is D0-D3, D0 being fully functional,
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
struct pcie_link_state *link_state; /* ASPM link state. */
#ifdef CONFIG_PCIEASPM
struct pcie_link_state *link_state; /* ASPM link state. */
unsigned int is_added:1;
unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */
unsigned int is_added:1;
unsigned int is_busmaster:1; /* device is busmaster */
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 broken_parity_status:1; /* Device generates false positive parity */
unsigned int msi_enabled:1;
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 msi_enabled:1;
#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_D3cold 0x8000 /* PME# from D3 (cold) */
#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_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_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 4 /* PM control and status register */
#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 */