Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/jbarnes/pci-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6:
  PCI PM: make the PM core more careful with drivers using the new PM framework
  PCI PM: Read power state from device after trying to change it on resume
  PCI PM: Do not disable and enable bridges during suspend-resume
  PCI: PCIe portdrv: Simplify suspend and resume
  PCI PM: Fix saving of device state in pci_legacy_suspend
  PCI PM: Check if the state has been saved before trying to restore it
  PCI PM: Fix handling of devices without drivers
  PCI: return error on failure to read PCI ROMs
  PCI: properly clean up ASPM link state on device remove
  • Loading branch information
Linus Torvalds committed Feb 7, 2009
2 parents 7f9a50a + 5294e25 commit e83102c
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 87 deletions.
13 changes: 12 additions & 1 deletion Documentation/filesystems/sysfs-pci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ that support it. For example, a given bus might look like this:
| |-- class
| |-- config
| |-- device
| |-- enable
| |-- irq
| |-- local_cpus
| |-- resource
Expand All @@ -32,6 +33,7 @@ files, each with their own function.
class PCI class (ascii, ro)
config PCI config space (binary, rw)
device PCI device (ascii, ro)
enable Whether the device is enabled (ascii, rw)
irq IRQ number (ascii, ro)
local_cpus nearby CPU mask (cpumask, ro)
resource PCI resource host addresses (ascii, ro)
Expand All @@ -57,10 +59,19 @@ used to do actual device programming from userspace. Note that some platforms
don't support mmapping of certain resources, so be sure to check the return
value from any attempted mmap.

The 'enable' file provides a counter that indicates how many times the device
has been enabled. If the 'enable' file currently returns '4', and a '1' is
echoed into it, it will then return '5'. Echoing a '0' into it will decrease
the count. Even when it returns to 0, though, some of the initialisation
may not be reversed.

The 'rom' file is special in that it provides read-only access to the device's
ROM file, if available. It's disabled by default, however, so applications
should write the string "1" to the file to enable it before attempting a read
call, and disable it following the access by writing "0" to the file.
call, and disable it following the access by writing "0" to the file. Note
that the device must be enabled for a rom read to return data succesfully.
In the event a driver is not bound to the device, it can be enabled using the
'enable' file, documented above.

Accessing legacy resources through sysfs
----------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion arch/ia64/sn/kernel/io_acpi_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ sn_acpi_slot_fixup(struct pci_dev *dev)
size = pci_resource_len(dev, PCI_ROM_RESOURCE);
addr = ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE],
size);
image_size = pci_get_rom_size(addr, size);
image_size = pci_get_rom_size(dev, addr, size);
dev->resource[PCI_ROM_RESOURCE].start = (unsigned long) addr;
dev->resource[PCI_ROM_RESOURCE].end =
(unsigned long) addr + image_size - 1;
Expand Down
2 changes: 1 addition & 1 deletion arch/ia64/sn/kernel/io_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ sn_io_slot_fixup(struct pci_dev *dev)

rom = ioremap(pci_resource_start(dev, PCI_ROM_RESOURCE),
size + 1);
image_size = pci_get_rom_size(rom, size + 1);
image_size = pci_get_rom_size(dev, rom, size + 1);
dev->resource[PCI_ROM_RESOURCE].end =
dev->resource[PCI_ROM_RESOURCE].start +
image_size - 1;
Expand Down
164 changes: 105 additions & 59 deletions drivers/pci/pci-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
int i = 0;

if (drv && drv->suspend) {
pci_power_t prev = pci_dev->current_state;

pci_dev->state_saved = false;

i = drv->suspend(pci_dev, state);
Expand All @@ -365,12 +367,16 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
if (pci_dev->state_saved)
goto Fixup;

if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0))
if (pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
WARN_ONCE(pci_dev->current_state != prev,
"PCI PM: Device state not saved by %pF\n",
drv->suspend);
goto Fixup;
}
}

pci_save_state(pci_dev);
pci_dev->state_saved = true;
/*
* This is for compatibility with existing code with legacy PM support.
*/
Expand Down Expand Up @@ -424,35 +430,20 @@ static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
pci_fixup_device(pci_fixup_resume_early, pci_dev);
}

static int pci_pm_default_resume(struct pci_dev *pci_dev)
static void pci_pm_default_resume(struct pci_dev *pci_dev)
{
pci_fixup_device(pci_fixup_resume, pci_dev);

if (!pci_is_bridge(pci_dev))
pci_enable_wake(pci_dev, PCI_D0, false);

return pci_pm_reenable_device(pci_dev);
}

static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev)
{
/* If device is enabled at this point, disable it */
pci_disable_enabled_device(pci_dev);
/*
* Save state with interrupts enabled, because in principle the bus the
* device is on may be put into a low power state after this code runs.
*/
pci_save_state(pci_dev);
}

static void pci_pm_default_suspend(struct pci_dev *pci_dev)
{
pci_pm_default_suspend_generic(pci_dev);

/* Disable non-bridge devices without PM support */
if (!pci_is_bridge(pci_dev))
pci_prepare_to_sleep(pci_dev);

pci_fixup_device(pci_fixup_suspend, pci_dev);
pci_disable_enabled_device(pci_dev);
pci_save_state(pci_dev);
}

static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
Expand Down Expand Up @@ -497,21 +488,49 @@ static void pci_pm_complete(struct device *dev)
static int pci_pm_suspend(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
int error = 0;
struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_SUSPEND);

if (drv && drv->pm && drv->pm->suspend) {
error = drv->pm->suspend(dev);
suspend_report_result(drv->pm->suspend, error);
if (!pm) {
pci_pm_default_suspend(pci_dev);
goto Fixup;
}

if (!error)
pci_pm_default_suspend(pci_dev);
pci_dev->state_saved = false;

return error;
if (pm->suspend) {
pci_power_t prev = pci_dev->current_state;
int error;

error = pm->suspend(dev);
suspend_report_result(pm->suspend, error);
if (error)
return error;

if (pci_dev->state_saved)
goto Fixup;

if (pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
WARN_ONCE(pci_dev->current_state != prev,
"PCI PM: State of device not saved by %pF\n",
pm->suspend);
goto Fixup;
}
}

if (!pci_dev->state_saved) {
pci_save_state(pci_dev);
if (!pci_is_bridge(pci_dev))
pci_prepare_to_sleep(pci_dev);
}

Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);

return 0;
}

static int pci_pm_suspend_noirq(struct device *dev)
Expand Down Expand Up @@ -554,7 +573,7 @@ static int pci_pm_resume_noirq(struct device *dev)
static int pci_pm_resume(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;

/*
Expand All @@ -567,12 +586,16 @@ static int pci_pm_resume(struct device *dev)
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev);

error = pci_pm_default_resume(pci_dev);
pci_pm_default_resume(pci_dev);

if (!error && drv && drv->pm && drv->pm->resume)
error = drv->pm->resume(dev);
if (pm) {
if (pm->resume)
error = pm->resume(dev);
} else {
pci_pm_reenable_device(pci_dev);
}

return error;
return 0;
}

#else /* !CONFIG_SUSPEND */
Expand All @@ -589,21 +612,31 @@ static int pci_pm_resume(struct device *dev)
static int pci_pm_freeze(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
int error = 0;
struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_FREEZE);

if (drv && drv->pm && drv->pm->freeze) {
error = drv->pm->freeze(dev);
suspend_report_result(drv->pm->freeze, error);
if (!pm) {
pci_pm_default_suspend(pci_dev);
return 0;
}

if (!error)
pci_pm_default_suspend_generic(pci_dev);
pci_dev->state_saved = false;

return error;
if (pm->freeze) {
int error;

error = pm->freeze(dev);
suspend_report_result(pm->freeze, error);
if (error)
return error;
}

if (!pci_dev->state_saved)
pci_save_state(pci_dev);

return 0;
}

static int pci_pm_freeze_noirq(struct device *dev)
Expand Down Expand Up @@ -646,39 +679,48 @@ static int pci_pm_thaw_noirq(struct device *dev)
static int pci_pm_thaw(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;

if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev);

pci_pm_reenable_device(pci_dev);

if (drv && drv->pm && drv->pm->thaw)
error = drv->pm->thaw(dev);
if (pm) {
if (pm->thaw)
error = pm->thaw(dev);
} else {
pci_pm_reenable_device(pci_dev);
}

return error;
}

static int pci_pm_poweroff(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;

if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_HIBERNATE);

if (!drv || !drv->pm)
return 0;
if (!pm) {
pci_pm_default_suspend(pci_dev);
goto Fixup;
}

pci_dev->state_saved = false;

if (drv->pm->poweroff) {
error = drv->pm->poweroff(dev);
suspend_report_result(drv->pm->poweroff, error);
if (pm->poweroff) {
error = pm->poweroff(dev);
suspend_report_result(pm->poweroff, error);
}

if (!error)
pci_pm_default_suspend(pci_dev);
if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
pci_prepare_to_sleep(pci_dev);

Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);

return error;
}
Expand Down Expand Up @@ -719,7 +761,7 @@ static int pci_pm_restore_noirq(struct device *dev)
static int pci_pm_restore(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;

/*
Expand All @@ -732,10 +774,14 @@ static int pci_pm_restore(struct device *dev)
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev);

error = pci_pm_default_resume(pci_dev);
pci_pm_default_resume(pci_dev);

if (!error && drv && drv->pm && drv->pm->restore)
error = drv->pm->restore(dev);
if (pm) {
if (pm->restore)
error = pm->restore(dev);
} else {
pci_pm_reenable_device(pci_dev);
}

return error;
}
Expand Down
4 changes: 2 additions & 2 deletions drivers/pci/pci-sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -768,8 +768,8 @@ pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
return -EINVAL;

rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
if (!rom)
return 0;
if (!rom || !size)
return -EIO;

if (off >= size)
count = 0;
Expand Down
4 changes: 2 additions & 2 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1418,10 +1418,10 @@ int pci_restore_standard_config(struct pci_dev *dev)
break;
}

dev->current_state = PCI_D0;
pci_update_current_state(dev, PCI_D0);

Restore:
return pci_restore_state(dev);
return dev->state_saved ? pci_restore_state(dev) : 0;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions drivers/pci/pcie/aspm.c
Original file line number Diff line number Diff line change
Expand Up @@ -718,9 +718,9 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)

/*
* All PCIe functions are in one slot, remove one function will remove
* the the whole slot, so just wait
* the whole slot, so just wait until we are the last function left.
*/
if (!list_empty(&parent->subordinate->devices))
if (!list_is_last(&pdev->bus_list, &parent->subordinate->devices))
goto out;

/* All functions are removed, so just disable ASPM for the link */
Expand Down
Loading

0 comments on commit e83102c

Please sign in to comment.