Commit e83102ca authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/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
parents 7f9a50a5 5294e256
...@@ -9,6 +9,7 @@ that support it. For example, a given bus might look like this: ...@@ -9,6 +9,7 @@ that support it. For example, a given bus might look like this:
| |-- class | |-- class
| |-- config | |-- config
| |-- device | |-- device
| |-- enable
| |-- irq | |-- irq
| |-- local_cpus | |-- local_cpus
| |-- resource | |-- resource
...@@ -32,6 +33,7 @@ files, each with their own function. ...@@ -32,6 +33,7 @@ files, each with their own function.
class PCI class (ascii, ro) class PCI class (ascii, ro)
config PCI config space (binary, rw) config PCI config space (binary, rw)
device PCI device (ascii, ro) device PCI device (ascii, ro)
enable Whether the device is enabled (ascii, rw)
irq IRQ number (ascii, ro) irq IRQ number (ascii, ro)
local_cpus nearby CPU mask (cpumask, ro) local_cpus nearby CPU mask (cpumask, ro)
resource PCI resource host addresses (ascii, ro) resource PCI resource host addresses (ascii, ro)
...@@ -57,10 +59,19 @@ used to do actual device programming from userspace. Note that some platforms ...@@ -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 don't support mmapping of certain resources, so be sure to check the return
value from any attempted mmap. 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 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 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 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 Accessing legacy resources through sysfs
---------------------------------------- ----------------------------------------
......
...@@ -443,7 +443,7 @@ sn_acpi_slot_fixup(struct pci_dev *dev) ...@@ -443,7 +443,7 @@ sn_acpi_slot_fixup(struct pci_dev *dev)
size = pci_resource_len(dev, PCI_ROM_RESOURCE); size = pci_resource_len(dev, PCI_ROM_RESOURCE);
addr = ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE], addr = ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE],
size); 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].start = (unsigned long) addr;
dev->resource[PCI_ROM_RESOURCE].end = dev->resource[PCI_ROM_RESOURCE].end =
(unsigned long) addr + image_size - 1; (unsigned long) addr + image_size - 1;
......
...@@ -269,7 +269,7 @@ sn_io_slot_fixup(struct pci_dev *dev) ...@@ -269,7 +269,7 @@ sn_io_slot_fixup(struct pci_dev *dev)
rom = ioremap(pci_resource_start(dev, PCI_ROM_RESOURCE), rom = ioremap(pci_resource_start(dev, PCI_ROM_RESOURCE),
size + 1); 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].end =
dev->resource[PCI_ROM_RESOURCE].start + dev->resource[PCI_ROM_RESOURCE].start +
image_size - 1; image_size - 1;
......
...@@ -355,6 +355,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) ...@@ -355,6 +355,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
int i = 0; int i = 0;
if (drv && drv->suspend) { if (drv && drv->suspend) {
pci_power_t prev = pci_dev->current_state;
pci_dev->state_saved = false; pci_dev->state_saved = false;
i = drv->suspend(pci_dev, state); i = drv->suspend(pci_dev, state);
...@@ -365,12 +367,16 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) ...@@ -365,12 +367,16 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
if (pci_dev->state_saved) if (pci_dev->state_saved)
goto Fixup; 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; goto Fixup;
} }
}
pci_save_state(pci_dev); pci_save_state(pci_dev);
pci_dev->state_saved = true;
/* /*
* This is for compatibility with existing code with legacy PM support. * This is for compatibility with existing code with legacy PM support.
*/ */
...@@ -424,35 +430,20 @@ static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) ...@@ -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); 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); pci_fixup_device(pci_fixup_resume, pci_dev);
if (!pci_is_bridge(pci_dev)) if (!pci_is_bridge(pci_dev))
pci_enable_wake(pci_dev, PCI_D0, false); 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) 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)) if (!pci_is_bridge(pci_dev))
pci_prepare_to_sleep(pci_dev); pci_disable_enabled_device(pci_dev);
pci_save_state(pci_dev);
pci_fixup_device(pci_fixup_suspend, pci_dev);
} }
static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
...@@ -497,21 +488,49 @@ static void pci_pm_complete(struct device *dev) ...@@ -497,21 +488,49 @@ static void pci_pm_complete(struct device *dev)
static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(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)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_SUSPEND); return pci_legacy_suspend(dev, PMSG_SUSPEND);
if (drv && drv->pm && drv->pm->suspend) { if (!pm) {
error = drv->pm->suspend(dev); pci_pm_default_suspend(pci_dev);
suspend_report_result(drv->pm->suspend, error); goto Fixup;
} }
if (!error) pci_dev->state_saved = false;
pci_pm_default_suspend(pci_dev);
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; 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) static int pci_pm_suspend_noirq(struct device *dev)
...@@ -554,7 +573,7 @@ static int pci_pm_resume_noirq(struct device *dev) ...@@ -554,7 +573,7 @@ static int pci_pm_resume_noirq(struct device *dev)
static int pci_pm_resume(struct device *dev) static int pci_pm_resume(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(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; int error = 0;
/* /*
...@@ -567,12 +586,16 @@ static int pci_pm_resume(struct device *dev) ...@@ -567,12 +586,16 @@ static int pci_pm_resume(struct device *dev)
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(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) if (pm) {
error = drv->pm->resume(dev); if (pm->resume)
error = pm->resume(dev);
} else {
pci_pm_reenable_device(pci_dev);
}
return error; return 0;
} }
#else /* !CONFIG_SUSPEND */ #else /* !CONFIG_SUSPEND */
...@@ -589,21 +612,31 @@ static int pci_pm_resume(struct device *dev) ...@@ -589,21 +612,31 @@ static int pci_pm_resume(struct device *dev)
static int pci_pm_freeze(struct device *dev) static int pci_pm_freeze(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(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)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_FREEZE); return pci_legacy_suspend(dev, PMSG_FREEZE);
if (drv && drv->pm && drv->pm->freeze) { if (!pm) {
error = drv->pm->freeze(dev); pci_pm_default_suspend(pci_dev);
suspend_report_result(drv->pm->freeze, error); return 0;
} }
if (!error) pci_dev->state_saved = false;
pci_pm_default_suspend_generic(pci_dev);
if (pm->freeze) {
int error;
error = pm->freeze(dev);
suspend_report_result(pm->freeze, error);
if (error)
return error; return error;
}
if (!pci_dev->state_saved)
pci_save_state(pci_dev);
return 0;
} }
static int pci_pm_freeze_noirq(struct device *dev) static int pci_pm_freeze_noirq(struct device *dev)
...@@ -646,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev) ...@@ -646,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev)
static int pci_pm_thaw(struct device *dev) static int pci_pm_thaw(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(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; int error = 0;
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev); return pci_legacy_resume(dev);
if (pm) {
if (pm->thaw)
error = pm->thaw(dev);
} else {
pci_pm_reenable_device(pci_dev); pci_pm_reenable_device(pci_dev);
}
if (drv && drv->pm && drv->pm->thaw)
error = drv->pm->thaw(dev);
return error; return error;
} }
...@@ -663,22 +698,29 @@ static int pci_pm_thaw(struct device *dev) ...@@ -663,22 +698,29 @@ static int pci_pm_thaw(struct device *dev)
static int pci_pm_poweroff(struct device *dev) static int pci_pm_poweroff(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(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; int error = 0;
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_HIBERNATE); return pci_legacy_suspend(dev, PMSG_HIBERNATE);
if (!drv || !drv->pm) if (!pm) {
return 0; pci_pm_default_suspend(pci_dev);
goto Fixup;
}
pci_dev->state_saved = false;
if (drv->pm->poweroff) { if (pm->poweroff) {
error = drv->pm->poweroff(dev); error = pm->poweroff(dev);
suspend_report_result(drv->pm->poweroff, error); suspend_report_result(pm->poweroff, error);
} }
if (!error) if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
pci_pm_default_suspend(pci_dev); pci_prepare_to_sleep(pci_dev);
Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);
return error; return error;
} }
...@@ -719,7 +761,7 @@ static int pci_pm_restore_noirq(struct device *dev) ...@@ -719,7 +761,7 @@ static int pci_pm_restore_noirq(struct device *dev)
static int pci_pm_restore(struct device *dev) static int pci_pm_restore(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(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; int error = 0;
/* /*
...@@ -732,10 +774,14 @@ static int pci_pm_restore(struct device *dev) ...@@ -732,10 +774,14 @@ static int pci_pm_restore(struct device *dev)
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(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) if (pm) {
error = drv->pm->restore(dev); if (pm->restore)
error = pm->restore(dev);
} else {
pci_pm_reenable_device(pci_dev);
}
return error; return error;
} }
......
...@@ -768,8 +768,8 @@ pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr, ...@@ -768,8 +768,8 @@ pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
return -EINVAL; return -EINVAL;
rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */ rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
if (!rom) if (!rom || !size)
return 0; return -EIO;
if (off >= size) if (off >= size)
count = 0; count = 0;
......
...@@ -1418,10 +1418,10 @@ int pci_restore_standard_config(struct pci_dev *dev) ...@@ -1418,10 +1418,10 @@ int pci_restore_standard_config(struct pci_dev *dev)
break; break;
} }
dev->current_state = PCI_D0; pci_update_current_state(dev, PCI_D0);
Restore: Restore:
return pci_restore_state(dev); return dev->state_saved ? pci_restore_state(dev) : 0;
} }
/** /**
......
...@@ -718,9 +718,9 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) ...@@ -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 * 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; goto out;
/* All functions are removed, so just disable ASPM for the link */ /* All functions are removed, so just disable ASPM for the link */
......
...@@ -55,25 +55,13 @@ static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) ...@@ -55,25 +55,13 @@ static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state)
} }
static int pcie_portdrv_suspend_late(struct pci_dev *dev, pm_message_t state)
{
return pci_save_state(dev);
}
static int pcie_portdrv_resume_early(struct pci_dev *dev)
{
return pci_restore_state(dev);
}
static int pcie_portdrv_resume(struct pci_dev *dev) static int pcie_portdrv_resume(struct pci_dev *dev)
{ {
pcie_portdrv_restore_config(dev); pci_set_master(dev);
return pcie_port_device_resume(dev); return pcie_port_device_resume(dev);
} }
#else #else
#define pcie_portdrv_suspend NULL #define pcie_portdrv_suspend NULL
#define pcie_portdrv_suspend_late NULL
#define pcie_portdrv_resume_early NULL
#define pcie_portdrv_resume NULL #define pcie_portdrv_resume NULL
#endif #endif
...@@ -292,8 +280,6 @@ static struct pci_driver pcie_portdriver = { ...@@ -292,8 +280,6 @@ static struct pci_driver pcie_portdriver = {
.remove = pcie_portdrv_remove, .remove = pcie_portdrv_remove,
.suspend = pcie_portdrv_suspend, .suspend = pcie_portdrv_suspend,
.suspend_late = pcie_portdrv_suspend_late,
.resume_early = pcie_portdrv_resume_early,
.resume = pcie_portdrv_resume, .resume = pcie_portdrv_resume,
.err_handler = &pcie_portdrv_err_handler, .err_handler = &pcie_portdrv_err_handler,
......
...@@ -63,7 +63,7 @@ void pci_disable_rom(struct pci_dev *pdev) ...@@ -63,7 +63,7 @@ void pci_disable_rom(struct pci_dev *pdev)
* The PCI window size could be much larger than the * The PCI window size could be much larger than the
* actual image size. * actual image size.
*/ */
size_t pci_get_rom_size(void __iomem *rom, size_t size) size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
{ {
void __iomem *image; void __iomem *image;
int last_image; int last_image;
...@@ -72,8 +72,10 @@ size_t pci_get_rom_size(void __iomem *rom, size_t size) ...@@ -72,8 +72,10 @@ size_t pci_get_rom_size(void __iomem *rom, size_t size)
do { do {
void __iomem *pds; void __iomem *pds;
/* Standard PCI ROMs start out with these bytes 55 AA */ /* Standard PCI ROMs start out with these bytes 55 AA */
if (readb(image) != 0x55) if (readb(image) != 0x55) {
dev_err(&pdev->dev, "Invalid ROM contents\n");
break; break;
}
if (readb(image + 1) != 0xAA) if (readb(image + 1) != 0xAA)
break; break;
/* get the PCI data structure and check its signature */ /* get the PCI data structure and check its signature */
...@@ -159,7 +161,7 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) ...@@ -159,7 +161,7 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
* size is much larger than the actual size of the ROM. * size is much larger than the actual size of the ROM.
* True size is important if the ROM is going to be copied. * True size is important if the ROM is going to be copied.
*/ */
*size = pci_get_rom_size(rom, *size); *size = pci_get_rom_size(pdev, rom, *size);
return rom; return rom;
} }
......
...@@ -684,7 +684,7 @@ int pci_enable_rom(struct pci_dev *pdev); ...@@ -684,7 +684,7 @@ int pci_enable_rom(struct pci_dev *pdev);
void pci_disable_rom(struct pci_dev *pdev); void pci_disable_rom(struct pci_dev *pdev);
void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size); void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size);
void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom); void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom);
size_t pci_get_rom_size(void __iomem *rom, size_t size); size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size);
/* Power management related routines */ /* Power management related routines */
int pci_save_state(struct pci_dev *dev); int pci_save_state(struct pci_dev *dev);
......
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