Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 102280
b: refs/heads/master
c: 44e4e66
h: refs/heads/master
v: v3
  • Loading branch information
Rafael J. Wysocki authored and Jesse Barnes committed Jul 7, 2008
1 parent cda9ac4 commit 4a316bc
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 53 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 961d9120fa6f078492a1c762dd91f2c097e56c83
refs/heads/master: 44e4e66eeae5338b3ca0b28f8352e60bf18d5ba8
16 changes: 10 additions & 6 deletions trunk/drivers/pci/pci-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,22 +277,26 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
[PCI_D3hot] = ACPI_STATE_D3,
[PCI_D3cold] = ACPI_STATE_D3
};
int error = -EINVAL;

if (!handle)
return -ENODEV;
/* If the ACPI device has _EJ0, ignore the device */
if (ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
return 0;
if (!handle || ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
return -ENODEV;

switch (state) {
case PCI_D0:
case PCI_D1:
case PCI_D2:
case PCI_D3hot:
case PCI_D3cold:
return acpi_bus_set_power(handle, state_conv[state]);
error = acpi_bus_set_power(handle, state_conv[state]);
}
return -EINVAL;

if (!error)
dev_printk(KERN_INFO, &dev->dev,
"power state changed by ACPI to D%d\n", state);

return error;
}

static struct pci_platform_pm_ops acpi_pci_platform_pm = {
Expand Down
150 changes: 104 additions & 46 deletions trunk/drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,67 +404,56 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
}

/**
* pci_set_power_state - Set the power state of a PCI device
* @dev: PCI device to be suspended
* @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
* 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.
*
* Transition a device to a new power state, using the Power Management
* Capabilities in the device's config space.
*
* RETURN VALUE:
* -EINVAL if trying to enter a lower state than we're already in.
* 0 if we're already in the requested state.
* -EIO if device does not support PCI PM.
* 0 if we can successfully change the power state.
* RETURN VALUE:
* -EINVAL if the requested state is invalid.
* -EIO if device does not support PCI PM or its PM capabilities register has a
* wrong version, or device doesn't support the requested state.
* 0 if device already is in the requested state.
* 0 if device's power state has been successfully changed.
*/
int
pci_set_power_state(struct pci_dev *dev, pci_power_t state)
static int
pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
{
int pm, need_restore = 0;
u16 pmcsr, pmc;
bool need_restore = false;

/* bound the state we're entering */
if (state > PCI_D3hot)
state = PCI_D3hot;

/*
* If the device or the parent bridge can't support PCI PM, ignore
* the request if we're doing anything besides putting it into D0
* (which would only happen on boot).
*/
if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
return 0;

/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);

/* abort if the device doesn't support PM capabilities */
if (!pm)
return -EIO;

if (state < PCI_D0 || state > PCI_D3hot)
return -EINVAL;

/* Validate current state:
* Can enter D0 from any state, but if we can only go deeper
* to sleep if we're already in a low power state
*/
if (state != PCI_D0 && dev->current_state > state) {
if (dev->current_state == state) {
/* we're already there */
return 0;
} else if (state != PCI_D0 && dev->current_state <= PCI_D3cold
&& dev->current_state > state) {
dev_err(&dev->dev, "invalid power transition "
"(from state %d to %d)\n", dev->current_state, state);
return -EINVAL;
} else if (dev->current_state == state)
return 0; /* we're already there */
}

pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);

pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
dev_printk(KERN_DEBUG, &dev->dev, "unsupported PM cap regs "
"version (%u)\n", pmc & PCI_PM_CAP_VER_MASK);
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 */
if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
return -EIO;
else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
if ((state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
|| (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2)))
return -EIO;

pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
Expand All @@ -483,7 +472,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
case PCI_UNKNOWN: /* Boot-up */
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
&& !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
need_restore = 1;
need_restore = true;
/* Fall-through: force to D0 */
default:
pmcsr = 0;
Expand All @@ -500,12 +489,6 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200);

/*
* Give firmware a chance to be called, such as ACPI _PRx, _PSx
* Firmware method after native method ?
*/
platform_pci_set_power_state(dev, state);

dev->current_state = state;

/* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
Expand All @@ -529,6 +512,81 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
return 0;
}

/**
* 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)
{
if (pm) {
u16 pmcsr;

pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
}
}

/**
* pci_set_power_state - Set the power state of a PCI device
* @dev: PCI device to handle.
* @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
*
* Transition a device to a new power state, using the platform formware and/or
* the device's PCI PM registers.
*
* RETURN VALUE:
* -EINVAL if the requested state is invalid.
* -EIO if device does not support PCI PM or its PM capabilities register has a
* wrong version, or device doesn't support the requested state.
* 0 if device already is in the requested state.
* 0 if device's power state has been successfully changed.
*/
int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
int pm, error;

/* bound the state we're entering */
if (state > PCI_D3hot)
state = PCI_D3hot;
else if (state < PCI_D0)
state = PCI_D0;
else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
/*
* If the device or the parent bridge do not support PCI PM,
* ignore the request if we're doing anything other than putting
* it into D0 (which would only happen on boot).
*/
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)) {
/*
* Allow the platform to change the state, for example via ACPI
* _PR0, _PS0 and some such, but do not trust it.
*/
int ret = platform_pci_set_power_state(dev, PCI_D0);
if (!ret)
pci_update_current_state(dev, pm);
}

error = pci_raw_set_power_state(dev, pm, 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) {
pci_update_current_state(dev, pm);
error = 0;
}
}

return error;
}

/**
* pci_choose_state - Choose the power state of a PCI device
* @dev: PCI device to be suspended
Expand Down

0 comments on commit 4a316bc

Please sign in to comment.