diff -urNp linux-1391/arch/i386/kernel/dmi_scan.c linux-1420/arch/i386/kernel/dmi_scan.c --- linux-1391/arch/i386/kernel/dmi_scan.c +++ linux-1420/arch/i386/kernel/dmi_scan.c @@ -546,6 +546,22 @@ static __init int print_if_true(struct d } /* + * Dell Inspiron 8000 APM BIOS fails to correctly save and restore the + * config space of some PCI devices. + */ + +static __init int broken_apm_pci_restore(struct dmi_blacklist *d) +{ +#ifdef CONFIG_PCI + extern int pci_bridge_force_restore; + + printk(KERN_WARNING "%s detected. Forcing restore of PCI configuration space on APM resume.\n", d->ident); + pci_bridge_force_restore = 1; +#endif + return 0; +} + +/* * Process the DMI blacklists */ @@ -871,6 +887,16 @@ static __initdata struct dmi_blacklist d MATCH(DMI_SYS_VENDOR, "IBM"), NO_MATCH, NO_MATCH, NO_MATCH } }, + { broken_apm_pci_restore, "Dell Inspiron 8000", { /* Work around broken Dell BIOS */ + MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + MATCH(DMI_PRODUCT_NAME, "Inspiron 8000"), + NO_MATCH, NO_MATCH + } }, + { broken_apm_pci_restore, "Dell Inspiron 8100", { /* Work around broken Dell BIOS */ + MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + MATCH(DMI_PRODUCT_NAME, "Inspiron 8100"), + NO_MATCH, NO_MATCH + } }, { NULL, } }; diff -urNp linux-1391/drivers/pci/bridge.c linux-1420/drivers/pci/bridge.c --- linux-1391/drivers/pci/bridge.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-1420/drivers/pci/bridge.c @@ -0,0 +1,149 @@ + +/* + * Copyright (c) 2001 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms + * of the GNU public license. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Arjan van de Ven + * + */ + + +/* + * Generic PCI driver for PCI bridges for powermanagement purposes + * + */ + +#include +#include +#include +#include +#include + +static struct pci_device_id bridge_pci_table[] __devinitdata = { + {/* handle all PCI bridges */ + class: ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), + class_mask: ~0, + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + }, + {0,}, +}; + +static int bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static int pci_bridge_save_state_bus(struct pci_bus *bus, int force); +int pci_generic_resume_compare(struct pci_dev *pdev); + +int pci_bridge_force_restore = 0; + + + + +static int __init bridge_setup(char *str) +{ + if (!strcmp(str,"force")) + pci_bridge_force_restore = 1; + else if (!strcmp(str,"noforce")) + pci_bridge_force_restore = 0; + return 0; +} + +__setup("resume=",bridge_setup); + + +static int pci_bridge_save_state_bus(struct pci_bus *bus, int force) +{ + struct list_head *list; + int error = 0; + + list_for_each(list, &bus->children) { + error = pci_bridge_save_state_bus(pci_bus_b(list),force); + if (error) return error; + } + list_for_each(list, &bus->devices) { + pci_generic_suspend_save(pci_dev_b(list),0); + } + return 0; +} + + +static int pci_bridge_restore_state_bus(struct pci_bus *bus, int force) +{ + struct list_head *list; + int error = 0; + static int printed_warning=0; + + list_for_each(list, &bus->children) { + error = pci_bridge_restore_state_bus(pci_bus_b(list),force); + if (error) return error; + } + list_for_each(list, &bus->devices) { + if (force) + pci_generic_resume_restore(pci_dev_b(list)); + else { + error = pci_generic_resume_compare(pci_dev_b(list)); + if (error && !printed_warning++) { + printk(KERN_WARNING "resume warning: bios doesn't restore PCI state properly\n"); + printk(KERN_WARNING "resume warning: if resume failed, try booting with resume=force\n"); + } + if (error) + return error; + } + } + return 0; +} + +static int bridge_suspend(struct pci_dev *dev, u32 force) +{ + pci_generic_suspend_save(dev,force); + if (dev->subordinate) + pci_bridge_save_state_bus(dev->subordinate,force); + return 0; +} + +static int bridge_resume(struct pci_dev *dev) +{ + + pci_generic_resume_restore(dev); + if (dev->subordinate) + pci_bridge_restore_state_bus(dev->subordinate,pci_bridge_force_restore); + return 0; +} + + +MODULE_DEVICE_TABLE(pci, bridge_pci_table); +static struct pci_driver bridge_ops = { + name: "PCI Bridge", + id_table: bridge_pci_table, + probe: bridge_probe, + suspend: bridge_suspend, + resume: bridge_resume +}; + +static int __devinit bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + return 0; +} + +static int __init bridge_init(void) +{ + pci_register_driver(&bridge_ops); + return 0; +} + +static void __exit bridge_exit(void) +{ + pci_unregister_driver(&bridge_ops); +} + + +module_init(bridge_init) +module_exit(bridge_exit) + diff -urNp linux-1391/drivers/pci/Makefile linux-1420/drivers/pci/Makefile --- linux-1391/drivers/pci/Makefile +++ linux-1420/drivers/pci/Makefile @@ -13,7 +13,7 @@ O_TARGET := driver.o export-objs := pci.o -obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o +obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o bridge.o obj-$(CONFIG_PROC_FS) += proc.o ifndef CONFIG_SPARC64 diff -urNp linux-1391/drivers/pci/pci.c linux-1420/drivers/pci/pci.c --- linux-1391/drivers/pci/pci.c +++ linux-1420/drivers/pci/pci.c @@ -380,6 +380,48 @@ pci_enable_device_bars(struct pci_dev *d return 0; } +int +pci_compare_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + unsigned int temp; + + if (buffer) { + for (i = 0; i < 16; i++) { + pci_read_config_dword(dev,i*4,&temp); + if (temp!=buffer[i]) + return 1; + } + } + return 0; +} + +int pci_generic_suspend_save(struct pci_dev *pdev, u32 state) +{ + if (pdev) + pci_save_state(pdev,pdev->saved_state); + return 0; +} + +int pci_generic_resume_restore(struct pci_dev *pdev) +{ + if (pdev) + pci_restore_state(pdev,pdev->saved_state); + return 0; +} + +int pci_generic_resume_compare(struct pci_dev *pdev) +{ + int retval=0; + if (pdev) + retval = pci_compare_state(pdev,pdev->saved_state); + return retval; +} + +EXPORT_SYMBOL_GPL(pci_generic_suspend_save); +EXPORT_SYMBOL_GPL(pci_generic_resume_restore); +EXPORT_SYMBOL_GPL(pci_generic_resume_compare); + /** * pci_enable_device - Initialize device before it's used by a driver. * @dev: PCI device to be initialized diff -urNp linux-1391/include/linux/pci.h linux-1420/include/linux/pci.h --- linux-1391/include/linux/pci.h +++ linux-1420/include/linux/pci.h @@ -410,6 +410,7 @@ struct pci_dev { char name[90]; /* device name */ char slot_name[8]; /* slot name */ + u32 saved_state[16]; /* for saving the config space before suspend */ int active; /* ISAPnP: device is active */ int ro; /* ISAPnP: read only */ unsigned short regs; /* ISAPnP: supported registers */ @@ -612,6 +613,8 @@ int pci_save_state(struct pci_dev *dev, int pci_restore_state(struct pci_dev *dev, u32 *buffer); int pci_set_power_state(struct pci_dev *dev, int state); int pci_enable_wake(struct pci_dev *dev, u32 state, int enable); +int pci_generic_suspend_save(struct pci_dev *pdev, u32 state); +int pci_generic_resume_restore(struct pci_dev *pdev); /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ @@ -707,6 +710,7 @@ static inline int pci_restore_state(stru static inline int pci_set_power_state(struct pci_dev *dev, int state) { return 0; } static inline int pci_enable_wake(struct pci_dev *dev, u32 state, int enable) { return 0; } + #define pci_for_each_dev(dev) \ for(dev = NULL; 0; )