From fa58d305d9925b01830e535896a7227a868a9e15 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:03:42 +0100 Subject: [PATCH] PCI PM: Add suspend counterpart of pci_reenable_device 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 Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 35 ++++++++++++++++++++++++++++++----- drivers/pci/pci.c | 36 ++++++++++++++++++++++++++++-------- drivers/pci/pci.h | 1 + 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 23bdf64411e..57cb0015a47 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -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); /* @@ -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; } @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f3fd55df67d..6e309c8b47d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -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 @@ -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) @@ -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; } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 211fd418f48..881dc15f8ef 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -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); -- 2.41.1