Skip to content

Commit

Permalink
PCI PM: Add suspend counterpart of pci_reenable_device
Browse files Browse the repository at this point in the history
PCI devices without drivers are not disabled during suspend and
hibernation, but they are enabled during resume, with the help of
pci_reenable_device(), so there is an unbalanced execution of
pcibios_enable_device() in the resume code path.

To correct this introduce function pci_disable_enabled_device()
that will disable the argument device, if it is enabled when the
function is being run, without updating the device's pci_dev
structure and use it in the suspend code path to balance the
pci_reenable_device() executed during resume.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@suse.cz>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Rafael J. Wysocki authored and Jesse Barnes committed Jan 7, 2009
1 parent c9b9972 commit fa58d30
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 13 deletions.
35 changes: 30 additions & 5 deletions drivers/pci/pci-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,19 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)

/*
* Default "suspend" method for devices that have no driver provided suspend,
* or not even a driver at all.
* or not even a driver at all (first part).
*/
static void pci_default_pm_suspend_early(struct pci_dev *pci_dev)
{
/* If device is enabled at this point, disable it */
pci_disable_enabled_device(pci_dev);
}

/*
* Default "suspend" method for devices that have no driver provided suspend,
* or not even a driver at all (second part).
*/
static void pci_default_pm_suspend(struct pci_dev *pci_dev)
static void pci_default_pm_suspend_late(struct pci_dev *pci_dev)
{
pci_save_state(pci_dev);
/*
Expand Down Expand Up @@ -377,7 +387,11 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
i = drv->suspend(pci_dev, state);
suspend_report_result(drv->suspend, i);
} else {
pci_default_pm_suspend(pci_dev);
/*
* For compatibility with existing code with legacy PM support
* don't call pci_default_pm_suspend_early() here.
*/
pci_default_pm_suspend_late(pci_dev);
}
return i;
}
Expand Down Expand Up @@ -455,7 +469,10 @@ static int pci_pm_suspend(struct device *dev)
}
} else if (pci_has_legacy_pm_support(pci_dev)) {
error = pci_legacy_suspend(dev, PMSG_SUSPEND);
} else {
pci_default_pm_suspend_early(pci_dev);
}

pci_fixup_device(pci_fixup_suspend, pci_dev);

return error;
Expand All @@ -475,7 +492,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
} else if (pci_has_legacy_pm_support(pci_dev)) {
error = pci_legacy_suspend_late(dev, PMSG_SUSPEND);
} else {
pci_default_pm_suspend(pci_dev);
pci_default_pm_suspend_late(pci_dev);
}

return error;
Expand Down Expand Up @@ -546,6 +563,8 @@ static int pci_pm_freeze(struct device *dev)
} else if (pci_has_legacy_pm_support(pci_dev)) {
error = pci_legacy_suspend(dev, PMSG_FREEZE);
pci_fixup_device(pci_fixup_suspend, pci_dev);
} else {
pci_default_pm_suspend_early(pci_dev);
}

return error;
Expand All @@ -565,7 +584,7 @@ static int pci_pm_freeze_noirq(struct device *dev)
} else if (pci_has_legacy_pm_support(pci_dev)) {
error = pci_legacy_suspend_late(dev, PMSG_FREEZE);
} else {
pci_default_pm_suspend(pci_dev);
pci_default_pm_suspend_late(pci_dev);
}

return error;
Expand All @@ -583,6 +602,8 @@ static int pci_pm_thaw(struct device *dev)
} else if (pci_has_legacy_pm_support(pci_dev)) {
pci_fixup_device(pci_fixup_resume, pci_dev);
error = pci_legacy_resume(dev);
} else {
pci_default_pm_resume_late(pci_dev);
}

return error;
Expand All @@ -600,6 +621,8 @@ static int pci_pm_thaw_noirq(struct device *dev)
} else if (pci_has_legacy_pm_support(pci_dev)) {
pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev));
error = pci_legacy_resume_early(dev);
} else {
pci_default_pm_resume_early(pci_dev);
}

return error;
Expand All @@ -618,6 +641,8 @@ static int pci_pm_poweroff(struct device *dev)
}
} else if (pci_has_legacy_pm_support(pci_dev)) {
error = pci_legacy_suspend(dev, PMSG_HIBERNATE);
} else {
pci_default_pm_suspend_early(pci_dev);
}

pci_fixup_device(pci_fixup_suspend, pci_dev);
Expand Down
36 changes: 28 additions & 8 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,32 @@ void pcim_pin_device(struct pci_dev *pdev)
*/
void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {}

static void do_pci_disable_device(struct pci_dev *dev)
{
u16 pci_command;

pci_read_config_word(dev, PCI_COMMAND, &pci_command);
if (pci_command & PCI_COMMAND_MASTER) {
pci_command &= ~PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, pci_command);
}

pcibios_disable_device(dev);
}

/**
* pci_disable_enabled_device - Disable device without updating enable_cnt
* @dev: PCI device to disable
*
* NOTE: This function is a backend of PCI power management routines and is
* not supposed to be called drivers.
*/
void pci_disable_enabled_device(struct pci_dev *dev)
{
if (atomic_read(&dev->enable_cnt))
do_pci_disable_device(dev);
}

/**
* pci_disable_device - Disable PCI device after use
* @dev: PCI device to be disabled
Expand All @@ -984,7 +1010,6 @@ void
pci_disable_device(struct pci_dev *dev)
{
struct pci_devres *dr;
u16 pci_command;

dr = find_pci_dr(dev);
if (dr)
Expand All @@ -993,14 +1018,9 @@ pci_disable_device(struct pci_dev *dev)
if (atomic_sub_return(1, &dev->enable_cnt) != 0)
return;

pci_read_config_word(dev, PCI_COMMAND, &pci_command);
if (pci_command & PCI_COMMAND_MASTER) {
pci_command &= ~PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, pci_command);
}
dev->is_busmaster = 0;
do_pci_disable_device(dev);

pcibios_disable_device(dev);
dev->is_busmaster = 0;
}

/**
Expand Down
1 change: 1 addition & 0 deletions drivers/pci/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct pci_platform_pm_ops {
};

extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
extern void pci_disable_enabled_device(struct pci_dev *dev);
extern void pci_pm_init(struct pci_dev *dev);
extern void platform_pci_wakeup_init(struct pci_dev *dev);
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);
Expand Down

0 comments on commit fa58d30

Please sign in to comment.