* Suspend/Resume bug in PCI layer wrt quirks
@ 2008-05-15 14:05 Arjan van de Ven
2008-05-15 16:07 ` Jesse Barnes
0 siblings, 1 reply; 9+ messages in thread
From: Arjan van de Ven @ 2008-05-15 14:05 UTC (permalink / raw)
To: Jesse Barnes; +Cc: linux-kernel, akpm, len.brown
Hi,
the following is a bug in the PCI layer wrt suspend/resume and quirks:
pci_device_resume_early does the following:
static int pci_device_resume_early(struct device * dev)
{
int error = 0;
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
pci_fixup_device(pci_fixup_resume, pci_dev);
eg it calls the resume quirks.
However, one of these quirks is
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6);
and asus_hides_smbus_lpc_ich6 does
base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */
if (base == NULL) return;
val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */
writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */
iounmap(base);
internally. ioremap()/iounmap() does a global tlb flush of course, which includes doing an IPI to other cpus.
However, during early resume, interrupts are disabled, and it's not allowed to do a IPI/global tlbflush with interrupts
off...
This is hitting quite a few people now:
http://www.kerneloops.org/searchweek.php?search=smp_call_function
do we need a DECLARE_PCI_FIXUP_RESUME_LATE() and use that for this one?
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-15 14:05 Suspend/Resume bug in PCI layer wrt quirks Arjan van de Ven @ 2008-05-15 16:07 ` Jesse Barnes 2008-05-15 17:54 ` Arjan van de Ven 2008-05-15 19:51 ` Rafael J. Wysocki 0 siblings, 2 replies; 9+ messages in thread From: Jesse Barnes @ 2008-05-15 16:07 UTC (permalink / raw) To: Arjan van de Ven; +Cc: linux-kernel, akpm, len.brown, Rafael J. Wysocki On Thursday, May 15, 2008 7:05 am Arjan van de Ven wrote: > Hi, > > the following is a bug in the PCI layer wrt suspend/resume and quirks: > > pci_device_resume_early does the following: > > static int pci_device_resume_early(struct device * dev) > { > int error = 0; > struct pci_dev * pci_dev = to_pci_dev(dev); > struct pci_driver * drv = pci_dev->driver; > > pci_fixup_device(pci_fixup_resume, pci_dev); > > eg it calls the resume quirks. > > However, one of these quirks is > DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, > asus_hides_smbus_lpc_ich6); > > and asus_hides_smbus_lpc_ich6 does > > base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits > 31:14, 16 kB aligned */ if (base == NULL) return; > val=readl(base + 0x3418); /* read the Function Disable register, > dword mode only */ writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the > SMBus device */ iounmap(base); > > internally. ioremap()/iounmap() does a global tlb flush of course, which > includes doing an IPI to other cpus. However, during early resume, > interrupts are disabled, and it's not allowed to do a IPI/global tlbflush > with interrupts off... Yeah I just got a similar report for quirk_vialatency(). But why are we only seeing these issues now? Is our interrupt disabled check catching more stuff these days? Rafael said he's looking at the VIA issue, and iirc we had talked about changing this stuff around a few weeks ago anyway. So yeah we may need either a new fixup phase or maybe we can just move the resume fixups to occur a little later, when interrupts are enabled. Jesse ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-15 16:07 ` Jesse Barnes @ 2008-05-15 17:54 ` Arjan van de Ven 2008-05-15 19:51 ` Rafael J. Wysocki 1 sibling, 0 replies; 9+ messages in thread From: Arjan van de Ven @ 2008-05-15 17:54 UTC (permalink / raw) To: Jesse Barnes; +Cc: linux-kernel, akpm, len.brown, Rafael J. Wysocki On Thu, 15 May 2008 09:07:06 -0700 > > internally. ioremap()/iounmap() does a global tlb flush of course, > > which includes doing an IPI to other cpus. However, during early > > resume, interrupts are disabled, and it's not allowed to do a > > IPI/global tlbflush with interrupts off... > > Yeah I just got a similar report for quirk_vialatency(). But why are > we only seeing these issues now? Is our interrupt disabled check > catching more stuff these days? we're now more systematically collecting WARN_ON()'s..... ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-15 16:07 ` Jesse Barnes 2008-05-15 17:54 ` Arjan van de Ven @ 2008-05-15 19:51 ` Rafael J. Wysocki 2008-05-16 17:51 ` Jesse Barnes 1 sibling, 1 reply; 9+ messages in thread From: Rafael J. Wysocki @ 2008-05-15 19:51 UTC (permalink / raw) To: Jesse Barnes; +Cc: Arjan van de Ven, linux-kernel, akpm, len.brown, Shaohua Li On Thursday, 15 of May 2008, Jesse Barnes wrote: > On Thursday, May 15, 2008 7:05 am Arjan van de Ven wrote: > > Hi, > > > > the following is a bug in the PCI layer wrt suspend/resume and quirks: > > > > pci_device_resume_early does the following: > > > > static int pci_device_resume_early(struct device * dev) > > { > > int error = 0; > > struct pci_dev * pci_dev = to_pci_dev(dev); > > struct pci_driver * drv = pci_dev->driver; > > > > pci_fixup_device(pci_fixup_resume, pci_dev); > > > > eg it calls the resume quirks. > > > > However, one of these quirks is > > DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, > > asus_hides_smbus_lpc_ich6); > > > > and asus_hides_smbus_lpc_ich6 does > > > > base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits > > 31:14, 16 kB aligned */ if (base == NULL) return; > > val=readl(base + 0x3418); /* read the Function Disable register, > > dword mode only */ writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the > > SMBus device */ iounmap(base); > > > > internally. ioremap()/iounmap() does a global tlb flush of course, which > > includes doing an IPI to other cpus. However, during early resume, > > interrupts are disabled, and it's not allowed to do a IPI/global tlbflush > > with interrupts off... > > Yeah I just got a similar report for quirk_vialatency(). But why are we only > seeing these issues now? Is our interrupt disabled check catching more stuff > these days? > > Rafael said he's looking at the VIA issue, and iirc we had talked about > changing this stuff around a few weeks ago anyway. So yeah we may need > either a new fixup phase or maybe we can just move the resume fixups to occur > a little later, when interrupts are enabled. In fact I forgot about a Shaohua's patch that cleaned up that a bit and is appended (should apply on top of the current -git). Please have a look and tell me what you think. Thanks, Rafael --- Some quirks should be called with interrupt disabled, we can't directly call them in .resume_early. Also the patch introduces pci_fixup_resume_early and pci_fixup_suspend, which matches current device core callbacks (.suspend/.resume_early). TBD: Somebody knows why we need quirk resume should double check if a quirk should be called in resume or resume_early. I changed some per my understanding, but can't make sure I fixed all. Signed-off-by: Shaohua Li <shaohua.li@intel.com> --- drivers/pci/pci-driver.c | 6 + drivers/pci/quirks.c | 120 +++++++++++++++++++++++++------------- include/asm-generic/vmlinux.lds.h | 6 + include/linux/pci.h | 10 ++- 4 files changed, 102 insertions(+), 40 deletions(-) Index: linux-2.6/drivers/pci/pci-driver.c =================================================================== --- linux-2.6.orig/drivers/pci/pci-driver.c +++ linux-2.6/drivers/pci/pci-driver.c @@ -292,6 +292,9 @@ static int pci_device_suspend(struct dev if (pci_dev->current_state == PCI_D0) pci_dev->current_state = PCI_UNKNOWN; } + + pci_fixup_device(pci_fixup_suspend, pci_dev); + return i; } @@ -337,6 +340,7 @@ static int pci_device_resume(struct devi error = drv->resume(pci_dev); else error = pci_default_resume(pci_dev); + pci_fixup_device(pci_fixup_resume, pci_dev); return error; } @@ -346,7 +350,7 @@ static int pci_device_resume_early(struc struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume, pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); if (drv && drv->resume_early) error = drv->resume_early(pci_dev); Index: linux-2.6/drivers/pci/quirks.c =================================================================== --- linux-2.6.orig/drivers/pci/quirks.c +++ linux-2.6/drivers/pci/quirks.c @@ -556,7 +556,7 @@ static void quirk_via_ioapic(struct pci_ pci_write_config_byte (dev, 0x58, tmp); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); /* * VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit. @@ -576,7 +576,7 @@ static void quirk_via_vt8237_bypass_apic } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); /* * The AMD io apic can hang the box when an apic irq is masked. @@ -622,7 +622,7 @@ static void quirk_amd_8131_ioapic(struct } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); #endif /* CONFIG_X86_IO_APIC */ /* @@ -774,7 +774,7 @@ static void quirk_cardbus_legacy(struct pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0); } DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); -DECLARE_PCI_FIXUP_RESUME(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); /* * Following the PCI ordering rules is optional on the AMD762. I'm not @@ -797,7 +797,7 @@ static void quirk_amd_ordering(struct pc } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); /* * DreamWorks provided workaround for Dunord I-3000 problem @@ -865,7 +865,7 @@ static void quirk_disable_pxb(struct pci } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) { @@ -885,9 +885,9 @@ static void __devinit quirk_amd_ide_mode } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); /* * Serverworks CSB5 IDE does not fully support native mode @@ -1093,31 +1093,61 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); - -static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev) +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); + +/* It appears we just have one such device. If not, we have a warning */ +static void __iomem *asus_rcba_base; +static void asus_hides_smbus_lpc_ich6_suspend(struct pci_dev *dev) { - u32 val, rcba; - void __iomem *base; + u32 rcba; if (likely(!asus_hides_smbus)) return; + WARN_ON(asus_rcba_base); + pci_read_config_dword(dev, 0xF0, &rcba); - base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */ - if (base == NULL) return; - val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */ - writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */ - iounmap(base); + /* use bits 31:14, 16 kB aligned */ + asus_rcba_base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); + if (asus_rcba_base == NULL) + return; +} + +static void asus_hides_smbus_lpc_ich6_resume_early(struct pci_dev *dev) +{ + u32 val; + + if (likely(!asus_hides_smbus || !asus_rcba_base)) + return; + /* read the Function Disable register, dword mode only */ + val = readl(asus_rcba_base + 0x3418); + writel(val & 0xFFFFFFF7, asus_rcba_base + 0x3418); /* enable the SMBus device */ +} + +static void asus_hides_smbus_lpc_ich6_resume(struct pci_dev *dev) +{ + if (likely(!asus_hides_smbus || !asus_rcba_base)) + return; + iounmap(asus_rcba_base); + asus_rcba_base = NULL; dev_info(&dev->dev, "Enabled ICH6/i801 SMBus device\n"); } + +static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev) +{ + asus_hides_smbus_lpc_ich6_suspend(dev); + asus_hides_smbus_lpc_ich6_resume_early(dev); + asus_hides_smbus_lpc_ich6_resume(dev); +} DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6); +DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_suspend); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume_early); /* * SiS 96x south bridge: BIOS typically hides SMBus device... @@ -1135,10 +1165,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); /* * ... This is further complicated by the fact that some SiS96x south @@ -1172,7 +1202,7 @@ static void quirk_sis_503(struct pci_dev quirk_sis_96x_smbus(dev); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); /* @@ -1205,7 +1235,7 @@ static void asus_hides_ac97_lpc(struct p } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); #if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE) @@ -1270,12 +1300,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JM DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); #endif @@ -1522,6 +1552,10 @@ extern struct pci_fixup __start_pci_fixu extern struct pci_fixup __end_pci_fixups_enable[]; extern struct pci_fixup __start_pci_fixups_resume[]; extern struct pci_fixup __end_pci_fixups_resume[]; +extern struct pci_fixup __start_pci_fixups_resume_early[]; +extern struct pci_fixup __end_pci_fixups_resume_early[]; +extern struct pci_fixup __start_pci_fixups_suspend[]; +extern struct pci_fixup __end_pci_fixups_suspend[]; void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) @@ -1554,6 +1588,16 @@ void pci_fixup_device(enum pci_fixup_pas end = __end_pci_fixups_resume; break; + case pci_fixup_resume_early: + start = __start_pci_fixups_resume_early; + end = __end_pci_fixups_resume_early; + break; + + case pci_fixup_suspend: + start = __start_pci_fixups_suspend; + end = __end_pci_fixups_suspend; + break; + default: /* stupid compiler warning, you would think with an enum... */ return; @@ -1630,7 +1674,7 @@ static void quirk_nvidia_ck804_pcie_aer_ } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, quirk_nvidia_ck804_pcie_aer_ext_cap); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, quirk_nvidia_ck804_pcie_aer_ext_cap); static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) Index: linux-2.6/include/asm-generic/vmlinux.lds.h =================================================================== --- linux-2.6.orig/include/asm-generic/vmlinux.lds.h +++ linux-2.6/include/asm-generic/vmlinux.lds.h @@ -84,6 +84,12 @@ VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \ *(.pci_fixup_resume) \ VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .; \ + *(.pci_fixup_resume_early) \ + VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ + *(.pci_fixup_suspend) \ + VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \ } \ \ /* RapidIO route ops */ \ Index: linux-2.6/include/linux/pci.h =================================================================== --- linux-2.6.orig/include/linux/pci.h +++ linux-2.6/include/linux/pci.h @@ -1013,7 +1013,9 @@ enum pci_fixup_pass { pci_fixup_header, /* After reading configuration header */ pci_fixup_final, /* Final phase of device fixups */ pci_fixup_enable, /* pci_enable_device() time */ - pci_fixup_resume, /* pci_enable_device() time */ + pci_fixup_resume, /* pci_device_resume() */ + pci_fixup_suspend, /* pci_device_suspend */ + pci_fixup_resume_early, /* pci_device_resume_early() */ }; /* Anonymous variables would be nice... */ @@ -1035,6 +1037,12 @@ enum pci_fixup_pass { #define DECLARE_PCI_FIXUP_RESUME(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume, \ resume##vendor##device##hook, vendor, device, hook) +#define DECLARE_PCI_FIXUP_RESUME_EARLY(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early, \ + resume_early##vendor##device##hook, vendor, device, hook) +#define DECLARE_PCI_FIXUP_SUSPEND(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ + suspend##vendor##device##hook, vendor, device, hook) void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-15 19:51 ` Rafael J. Wysocki @ 2008-05-16 17:51 ` Jesse Barnes 2008-05-16 20:16 ` Rafael J. Wysocki 0 siblings, 1 reply; 9+ messages in thread From: Jesse Barnes @ 2008-05-16 17:51 UTC (permalink / raw) To: Rafael J. Wysocki Cc: Arjan van de Ven, linux-kernel, akpm, len.brown, Shaohua Li On Thursday, May 15, 2008 12:51 pm Rafael J. Wysocki wrote: > Some quirks should be called with interrupt disabled, we can't directly > call them in .resume_early. Also the patch introduces > pci_fixup_resume_early and pci_fixup_suspend, which matches current > device core callbacks (.suspend/.resume_early). > > TBD: Somebody knows why we need quirk resume should double check if a > quirk should be called in resume or resume_early. I changed some per my > understanding, but can't make sure I fixed all. > > Signed-off-by: Shaohua Li <shaohua.li@intel.com> I applied this one to linux-next, it should fix some of the problems people have been seeing at least... Jesse ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-16 17:51 ` Jesse Barnes @ 2008-05-16 20:16 ` Rafael J. Wysocki 2008-05-16 21:37 ` Greg KH 0 siblings, 1 reply; 9+ messages in thread From: Rafael J. Wysocki @ 2008-05-16 20:16 UTC (permalink / raw) To: Jesse Barnes, Greg KH Cc: Arjan van de Ven, linux-kernel, akpm, len.brown, Shaohua Li On Friday, 16 of May 2008, Jesse Barnes wrote: > On Thursday, May 15, 2008 12:51 pm Rafael J. Wysocki wrote: > > Some quirks should be called with interrupt disabled, we can't directly > > call them in .resume_early. Also the patch introduces > > pci_fixup_resume_early and pci_fixup_suspend, which matches current > > device core callbacks (.suspend/.resume_early). > > > > TBD: Somebody knows why we need quirk resume should double check if a > > quirk should be called in resume or resume_early. I changed some per my > > understanding, but can't make sure I fixed all. > > > > Signed-off-by: Shaohua Li <shaohua.li@intel.com> > > I applied this one to linux-next, it should fix some of the problems people > have been seeing at least... Well, that will collide with pm-new-suspend-and-hibernation-callbacks-for-pci-bus-type.patch in the Greg's tree. Greg, perhaps it's better if you drop it and I'll send an updated version to Jesse? That will make his tree dependent on yours, but that will be okay, won't it? Thanks, Rafael ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-16 20:16 ` Rafael J. Wysocki @ 2008-05-16 21:37 ` Greg KH 2008-05-19 19:09 ` Jesse Barnes 0 siblings, 1 reply; 9+ messages in thread From: Greg KH @ 2008-05-16 21:37 UTC (permalink / raw) To: Rafael J. Wysocki Cc: Jesse Barnes, Arjan van de Ven, linux-kernel, akpm, len.brown, Shaohua Li On Fri, May 16, 2008 at 10:16:30PM +0200, Rafael J. Wysocki wrote: > On Friday, 16 of May 2008, Jesse Barnes wrote: > > On Thursday, May 15, 2008 12:51 pm Rafael J. Wysocki wrote: > > > Some quirks should be called with interrupt disabled, we can't directly > > > call them in .resume_early. Also the patch introduces > > > pci_fixup_resume_early and pci_fixup_suspend, which matches current > > > device core callbacks (.suspend/.resume_early). > > > > > > TBD: Somebody knows why we need quirk resume should double check if a > > > quirk should be called in resume or resume_early. I changed some per my > > > understanding, but can't make sure I fixed all. > > > > > > Signed-off-by: Shaohua Li <shaohua.li@intel.com> > > > > I applied this one to linux-next, it should fix some of the problems people > > have been seeing at least... > > Well, that will collide with > pm-new-suspend-and-hibernation-callbacks-for-pci-bus-type.patch > in the Greg's tree. > > Greg, perhaps it's better if you drop it and I'll send an updated version to > Jesse? That will make his tree dependent on yours, but that will be okay, > won't it? That's up to Jesse. I don't know if he can handle a depancy on an external tree. Jesse? ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-16 21:37 ` Greg KH @ 2008-05-19 19:09 ` Jesse Barnes 2008-05-19 22:49 ` Rafael J. Wysocki 0 siblings, 1 reply; 9+ messages in thread From: Jesse Barnes @ 2008-05-19 19:09 UTC (permalink / raw) To: Greg KH Cc: Rafael J. Wysocki, Arjan van de Ven, linux-kernel, akpm, len.brown, Shaohua Li On Friday, May 16, 2008 2:37 pm Greg KH wrote: > On Fri, May 16, 2008 at 10:16:30PM +0200, Rafael J. Wysocki wrote: > > On Friday, 16 of May 2008, Jesse Barnes wrote: > > > On Thursday, May 15, 2008 12:51 pm Rafael J. Wysocki wrote: > > > > Some quirks should be called with interrupt disabled, we can't > > > > directly call them in .resume_early. Also the patch introduces > > > > pci_fixup_resume_early and pci_fixup_suspend, which matches current > > > > device core callbacks (.suspend/.resume_early). > > > > > > > > TBD: Somebody knows why we need quirk resume should double check if a > > > > quirk should be called in resume or resume_early. I changed some per > > > > my understanding, but can't make sure I fixed all. > > > > > > > > Signed-off-by: Shaohua Li <shaohua.li@intel.com> > > > > > > I applied this one to linux-next, it should fix some of the problems > > > people have been seeing at least... > > > > Well, that will collide with > > pm-new-suspend-and-hibernation-callbacks-for-pci-bus-type.patch > > in the Greg's tree. > > > > Greg, perhaps it's better if you drop it and I'll send an updated version > > to Jesse? That will make his tree dependent on yours, but that will be > > okay, won't it? > > That's up to Jesse. I don't know if he can handle a depancy on an > external tree. > > Jesse? It would be easiest for me to just carry Rafael's patch, but that assumes you're ok with the changes. Have you looked at it enough to feel good about it, Greg? Thanks, Jesse ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Suspend/Resume bug in PCI layer wrt quirks 2008-05-19 19:09 ` Jesse Barnes @ 2008-05-19 22:49 ` Rafael J. Wysocki 0 siblings, 0 replies; 9+ messages in thread From: Rafael J. Wysocki @ 2008-05-19 22:49 UTC (permalink / raw) To: Jesse Barnes Cc: Greg KH, Arjan van de Ven, linux-kernel, akpm, len.brown, Shaohua Li On Monday, 19 of May 2008, Jesse Barnes wrote: > On Friday, May 16, 2008 2:37 pm Greg KH wrote: > > On Fri, May 16, 2008 at 10:16:30PM +0200, Rafael J. Wysocki wrote: > > > On Friday, 16 of May 2008, Jesse Barnes wrote: > > > > On Thursday, May 15, 2008 12:51 pm Rafael J. Wysocki wrote: > > > > > Some quirks should be called with interrupt disabled, we can't > > > > > directly call them in .resume_early. Also the patch introduces > > > > > pci_fixup_resume_early and pci_fixup_suspend, which matches current > > > > > device core callbacks (.suspend/.resume_early). > > > > > > > > > > TBD: Somebody knows why we need quirk resume should double check if a > > > > > quirk should be called in resume or resume_early. I changed some per > > > > > my understanding, but can't make sure I fixed all. > > > > > > > > > > Signed-off-by: Shaohua Li <shaohua.li@intel.com> > > > > > > > > I applied this one to linux-next, it should fix some of the problems > > > > people have been seeing at least... > > > > > > Well, that will collide with > > > pm-new-suspend-and-hibernation-callbacks-for-pci-bus-type.patch > > > in the Greg's tree. > > > > > > Greg, perhaps it's better if you drop it and I'll send an updated version > > > to Jesse? That will make his tree dependent on yours, but that will be > > > okay, won't it? > > > > That's up to Jesse. I don't know if he can handle a depancy on an > > external tree. > > > > Jesse? > > It would be easiest for me to just carry Rafael's patch, but that assumes > you're ok with the changes. Have you looked at it enough to feel good about > it, Greg? Below is the updated patch, FWIW. It's on top of the Shaohua's patch cleaning up the quirks. Thanks, Rafael --- From: Rafael J. Wysocki <rjw@sisk.pl> Implement new suspend and hibernation callbacks for the PCI bus type. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz> Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org> --- drivers/pci/pci-driver.c | 392 +++++++++++++++++++++++++++++++++++++++++------ include/linux/pci.h | 2 2 files changed, 347 insertions(+), 47 deletions(-) Index: linux-2.6/drivers/pci/pci-driver.c =================================================================== --- linux-2.6.orig/drivers/pci/pci-driver.c +++ linux-2.6/drivers/pci/pci-driver.c @@ -274,7 +274,57 @@ static int pci_device_remove(struct devi return 0; } -static int pci_device_suspend(struct device * dev, pm_message_t state) +static void pci_device_shutdown(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + + if (drv && drv->shutdown) + drv->shutdown(pci_dev); + pci_msi_shutdown(pci_dev); + pci_msix_shutdown(pci_dev); +} + +#ifdef CONFIG_PM_SLEEP + +/* + * Default "suspend" method for devices that have no driver provided suspend, + * or not even a driver at all. + */ +static void pci_default_pm_suspend(struct pci_dev *pci_dev) +{ + pci_save_state(pci_dev); + /* + * mark its power state as "unknown", since we don't know if + * e.g. the BIOS will change its device state when we suspend. + */ + if (pci_dev->current_state == PCI_D0) + pci_dev->current_state = PCI_UNKNOWN; +} + +/* + * Default "resume" method for devices that have no driver provided resume, + * or not even a driver at all. + */ +static int pci_default_pm_resume(struct pci_dev *pci_dev) +{ + int retval = 0; + + /* restore the PCI config space */ + pci_restore_state(pci_dev); + /* if the device was enabled before suspend, reenable */ + retval = pci_reenable_device(pci_dev); + /* + * if the device was busmaster before the suspend, make it busmaster + * again + */ + if (pci_dev->is_busmaster) + pci_set_master(pci_dev); + + return retval; +} + +static int pci_legacy_suspend(struct device *dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; @@ -284,21 +334,12 @@ static int pci_device_suspend(struct dev i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); } else { - pci_save_state(pci_dev); - /* - * mark its power state as "unknown", since we don't know if - * e.g. the BIOS will change its device state when we suspend. - */ - if (pci_dev->current_state == PCI_D0) - pci_dev->current_state = PCI_UNKNOWN; + pci_default_pm_suspend(pci_dev); } - - pci_fixup_device(pci_fixup_suspend, pci_dev); - return i; } -static int pci_device_suspend_late(struct device * dev, pm_message_t state) +static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; @@ -311,26 +352,7 @@ static int pci_device_suspend_late(struc return i; } -/* - * Default resume method for devices that have no driver provided resume, - * or not even a driver at all. - */ -static int pci_default_resume(struct pci_dev *pci_dev) -{ - int retval = 0; - - /* restore the PCI config space */ - pci_restore_state(pci_dev); - /* if the device was enabled before suspend, reenable */ - retval = pci_reenable_device(pci_dev); - /* if the device was busmaster before the suspend, make it busmaster again */ - if (pci_dev->is_busmaster) - pci_set_master(pci_dev); - - return retval; -} - -static int pci_device_resume(struct device * dev) +static int pci_legacy_resume(struct device *dev) { int error; struct pci_dev * pci_dev = to_pci_dev(dev); @@ -339,35 +361,313 @@ static int pci_device_resume(struct devi if (drv && drv->resume) error = drv->resume(pci_dev); else - error = pci_default_resume(pci_dev); - pci_fixup_device(pci_fixup_resume, pci_dev); + error = pci_default_pm_resume(pci_dev); return error; } -static int pci_device_resume_early(struct device * dev) +static int pci_legacy_resume_early(struct device *dev) { int error = 0; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume_early, pci_dev); - if (drv && drv->resume_early) error = drv->resume_early(pci_dev); return error; } -static void pci_device_shutdown(struct device *dev) +static int pci_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm && drv->pm->prepare) + error = drv->pm->prepare(dev); + + return error; +} + +static void pci_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +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; + + if (drv && drv->pm) { + if (drv->pm->suspend) { + error = drv->pm->suspend(dev); + suspend_report_result(drv->pm->suspend, error); + } else { + pci_default_pm_suspend(pci_dev); + } + } else { + error = pci_legacy_suspend(dev, PMSG_SUSPEND); + } + pci_fixup_device(pci_fixup_suspend, pci_dev); + + return error; +} + +static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + int error = 0; - if (drv && drv->shutdown) - drv->shutdown(pci_dev); - pci_msi_shutdown(pci_dev); - pci_msix_shutdown(pci_dev); + if (drv && drv->pm) { + if (drv->pm->suspend_noirq) { + error = drv->pm->suspend_noirq(dev); + suspend_report_result(drv->pm->suspend_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); + } + + return error; +} + +static int pci_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error; + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (drv && drv->pm) { + error = drv->pm->resume ? drv->pm->resume(dev) : + pci_default_pm_resume(pci_dev); + } else { + error = pci_legacy_resume(dev); + } + + return error; } +static int pci_pm_resume_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + if (drv && drv->pm) { + if (drv->pm->resume_noirq) + error = drv->pm->resume_noirq(dev); + } else { + error = pci_legacy_resume_early(dev); + } + + return error; +} + +#else /* !CONFIG_SUSPEND */ + +#define pci_pm_suspend NULL +#define pci_pm_suspend_noirq NULL +#define pci_pm_resume NULL +#define pci_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +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; + + if (drv && drv->pm) { + if (drv->pm->freeze) { + error = drv->pm->freeze(dev); + suspend_report_result(drv->pm->freeze, error); + } else { + pci_default_pm_suspend(pci_dev); + } + } else { + error = pci_legacy_suspend(dev, PMSG_FREEZE); + pci_fixup_device(pci_fixup_suspend, pci_dev); + } + + return error; +} + +static int pci_pm_freeze_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->freeze_noirq) { + error = drv->pm->freeze_noirq(dev); + suspend_report_result(drv->pm->freeze_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_FREEZE); + } + + return error; +} + +static int pci_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw) + error = drv->pm->thaw(dev); + } else { + pci_fixup_device(pci_fixup_resume, to_pci_dev(dev)); + error = pci_legacy_resume(dev); + } + + return error; +} + +static int pci_pm_thaw_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw_noirq) + error = drv->pm->thaw_noirq(dev); + } else { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + error = pci_legacy_resume_early(dev); + } + + return error; +} + +static int pci_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); + + if (drv && drv->pm) { + if (drv->pm->poweroff) { + error = drv->pm->poweroff(dev); + suspend_report_result(drv->pm->poweroff, error); + } + } else { + error = pci_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return error; +} + +static int pci_pm_poweroff_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->poweroff_noirq) { + error = drv->pm->poweroff_noirq(dev); + suspend_report_result(drv->pm->poweroff_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE); + } + + return error; +} + +static int pci_pm_restore(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error; + + if (drv && drv->pm) { + error = drv->pm->restore ? drv->pm->restore(dev) : + pci_default_pm_resume(pci_dev); + } else { + error = pci_legacy_resume(dev); + } + pci_fixup_device(pci_fixup_resume, pci_dev); + + return error; +} + +static int pci_pm_restore_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (drv && drv->pm) { + if (drv->pm->restore_noirq) + error = drv->pm->restore_noirq(dev); + } else { + error = pci_legacy_resume_early(dev); + } + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + return error; +} + +#else /* !CONFIG_HIBERNATION */ + +#define pci_pm_freeze NULL +#define pci_pm_freeze_noirq NULL +#define pci_pm_thaw NULL +#define pci_pm_thaw_noirq NULL +#define pci_pm_poweroff NULL +#define pci_pm_poweroff_noirq NULL +#define pci_pm_restore NULL +#define pci_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +struct pm_ext_ops pci_pm_ops = { + .base = { + .prepare = pci_pm_prepare, + .complete = pci_pm_complete, + .suspend = pci_pm_suspend, + .resume = pci_pm_resume, + .freeze = pci_pm_freeze, + .thaw = pci_pm_thaw, + .poweroff = pci_pm_poweroff, + .restore = pci_pm_restore, + }, + .suspend_noirq = pci_pm_suspend_noirq, + .resume_noirq = pci_pm_resume_noirq, + .freeze_noirq = pci_pm_freeze_noirq, + .thaw_noirq = pci_pm_thaw_noirq, + .poweroff_noirq = pci_pm_poweroff_noirq, + .restore_noirq = pci_pm_restore_noirq, +}; + +#define PCI_PM_OPS_PTR &pci_pm_ops + +#else /* !CONFIG_PM_SLEEP */ + +#define PCI_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + /** * __pci_register_driver - register a new pci driver * @drv: the driver structure to register @@ -390,6 +690,9 @@ int __pci_register_driver(struct pci_dri drv->driver.owner = owner; drv->driver.mod_name = mod_name; + if (drv->pm) + drv->driver.pm = &drv->pm->base; + spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -515,12 +818,9 @@ struct bus_type pci_bus_type = { .uevent = pci_uevent, .probe = pci_device_probe, .remove = pci_device_remove, - .suspend = pci_device_suspend, - .suspend_late = pci_device_suspend_late, - .resume_early = pci_device_resume_early, - .resume = pci_device_resume, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, + .pm = PCI_PM_OPS_PTR, }; static int __init pci_driver_init(void) Index: linux-2.6/include/linux/pci.h =================================================================== --- linux-2.6.orig/include/linux/pci.h +++ linux-2.6/include/linux/pci.h @@ -389,7 +389,7 @@ struct pci_driver { int (*resume_early) (struct pci_dev *dev); int (*resume) (struct pci_dev *dev); /* Device woken up */ void (*shutdown) (struct pci_dev *dev); - + struct pm_ext_ops *pm; struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-05-19 22:48 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-05-15 14:05 Suspend/Resume bug in PCI layer wrt quirks Arjan van de Ven 2008-05-15 16:07 ` Jesse Barnes 2008-05-15 17:54 ` Arjan van de Ven 2008-05-15 19:51 ` Rafael J. Wysocki 2008-05-16 17:51 ` Jesse Barnes 2008-05-16 20:16 ` Rafael J. Wysocki 2008-05-16 21:37 ` Greg KH 2008-05-19 19:09 ` Jesse Barnes 2008-05-19 22:49 ` Rafael J. Wysocki
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®