mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 16:41:58 +00:00
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
This commit is contained in:
commit
e83102cab0
@ -9,6 +9,7 @@ that support it. For example, a given bus might look like this:
|
||||
| |-- class
|
||||
| |-- config
|
||||
| |-- device
|
||||
| |-- enable
|
||||
| |-- irq
|
||||
| |-- local_cpus
|
||||
| |-- resource
|
||||
@ -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)
|
||||
@ -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
|
||||
----------------------------------------
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
@ -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.
|
||||
*/
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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;
|
||||
|
||||
/*
|
||||
@ -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 */
|
||||
@ -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)
|
||||
@ -646,16 +679,18 @@ 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;
|
||||
}
|
||||
@ -663,22 +698,29 @@ static int pci_pm_thaw(struct device *dev)
|
||||
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 (drv->pm->poweroff) {
|
||||
error = drv->pm->poweroff(dev);
|
||||
suspend_report_result(drv->pm->poweroff, error);
|
||||
if (!pm) {
|
||||
pci_pm_default_suspend(pci_dev);
|
||||
goto Fixup;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
pci_pm_default_suspend(pci_dev);
|
||||
pci_dev->state_saved = false;
|
||||
|
||||
if (pm->poweroff) {
|
||||
error = pm->poweroff(dev);
|
||||
suspend_report_result(pm->poweroff, error);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/*
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
pcie_portdrv_restore_config(dev);
|
||||
pci_set_master(dev);
|
||||
return pcie_port_device_resume(dev);
|
||||
}
|
||||
#else
|
||||
#define pcie_portdrv_suspend NULL
|
||||
#define pcie_portdrv_suspend_late NULL
|
||||
#define pcie_portdrv_resume_early NULL
|
||||
#define pcie_portdrv_resume NULL
|
||||
#endif
|
||||
|
||||
@ -292,8 +280,6 @@ static struct pci_driver pcie_portdriver = {
|
||||
.remove = pcie_portdrv_remove,
|
||||
|
||||
.suspend = pcie_portdrv_suspend,
|
||||
.suspend_late = pcie_portdrv_suspend_late,
|
||||
.resume_early = pcie_portdrv_resume_early,
|
||||
.resume = pcie_portdrv_resume,
|
||||
|
||||
.err_handler = &pcie_portdrv_err_handler,
|
||||
|
@ -63,7 +63,7 @@ void pci_disable_rom(struct pci_dev *pdev)
|
||||
* The PCI window size could be much larger than the
|
||||
* 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;
|
||||
int last_image;
|
||||
@ -72,8 +72,10 @@ size_t pci_get_rom_size(void __iomem *rom, size_t size)
|
||||
do {
|
||||
void __iomem *pds;
|
||||
/* 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;
|
||||
}
|
||||
if (readb(image + 1) != 0xAA)
|
||||
break;
|
||||
/* 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)
|
||||
* size is much larger than the actual size of the ROM.
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -684,7 +684,7 @@ int pci_enable_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 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 */
|
||||
int pci_save_state(struct pci_dev *dev);
|
||||
|
Loading…
Reference in New Issue
Block a user