]> git.hungrycats.org Git - linux/commitdiff
[PATCH] PCI: restore pci config space on resume
authorArjan van de Ven <arjanv@redhat.com>
Wed, 26 May 2004 08:37:25 +0000 (01:37 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Wed, 26 May 2004 08:37:25 +0000 (01:37 -0700)
The patch below enhances the PCI layer with 2 things
1) enable and busmaster state are stored in the pci device struct
2) pci config space is stored to the pci device struct

with that, it is possible to make a generic pci resume method that restores
config space and reenables the device, including busmaster when appropriate.

One can rightfully argue that the driver resume method should do this, and
yes that is right. So the patch only does it for devices that don't have a
resume method. Like the main PCI bridge on my testbox of which the bios so
nicely forgets to restore the bus master bit during resume.. With this patch
my testbox resumes just fine while it, well, wasn't all too happy as you can
imagine without a busmaster pci bridge.

drivers/pci/pci-driver.c
drivers/pci/pci.c
include/linux/pci.h

index 4c7e1695d2111032f5cfee8740b74fac7e1168f8..0dea6b1ddd52d4bc2e9619944ed720dd254729d4 100644 (file)
@@ -299,10 +299,30 @@ static int pci_device_suspend(struct device * dev, u32 state)
 {
        struct pci_dev * pci_dev = to_pci_dev(dev);
        struct pci_driver * drv = pci_dev->driver;
+       int i = 0;
 
        if (drv && drv->suspend)
-               return drv->suspend(pci_dev,state);
-       return 0;
+               i = drv->suspend(pci_dev,state);
+               
+       pci_save_state(pci_dev, pci_dev->saved_config_space);
+       return i;
+}
+
+
+/* 
+ * Default resume method for devices that have no driver provided resume,
+ * or not even a driver at all.
+ */
+static void pci_default_resume(struct pci_dev *pci_dev)
+{
+       /* restore the PCI config space */
+       pci_restore_state(pci_dev, pci_dev->saved_config_space);
+       /* if the device was enabled before suspend, reenable */
+       if (pci_dev->is_enabled)
+               pci_enable_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);
 }
 
 static int pci_device_resume(struct device * dev)
@@ -312,6 +332,8 @@ static int pci_device_resume(struct device * dev)
 
        if (drv && drv->resume)
                drv->resume(pci_dev);
+       else
+               pci_default_resume(pci_dev);
        return 0;
 }
 
index f4b28f9f53f23547a4f5031d15f77411f30ef1df..e3c97d940bab67462d521fc7d67a8178c689cb8e 100644 (file)
@@ -385,6 +385,7 @@ pci_enable_device_bars(struct pci_dev *dev, int bars)
 int
 pci_enable_device(struct pci_dev *dev)
 {
+       dev->is_enabled = 1;
        return pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1);
 }
 
@@ -399,6 +400,9 @@ void
 pci_disable_device(struct pci_dev *dev)
 {
        u16 pci_command;
+       
+       dev->is_enabled = 0;
+       dev->is_busmaster = 0;
 
        pci_read_config_word(dev, PCI_COMMAND, &pci_command);
        if (pci_command & PCI_COMMAND_MASTER) {
@@ -601,6 +605,7 @@ pci_set_master(struct pci_dev *dev)
                cmd |= PCI_COMMAND_MASTER;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
+       dev->is_busmaster = 1;
        pcibios_set_master(dev);
 }
 
index e06685a1f4f6da853331f52be9e421a0a98dc022..2662f462b7e65e499137dd00076dcb3670a86ce8 100644 (file)
@@ -488,6 +488,11 @@ struct pci_dev {
        /* These fields are used by common fixups */
        unsigned int    transparent:1;  /* Transparent PCI bridge */
        unsigned int    multifunction:1;/* Part of multi-function device */
+       /* keep track of device state */
+       unsigned int    is_enabled:1;   /* pci_enable_device has been called */
+       unsigned int    is_busmaster:1; /* device is busmaster */
+       
+       unsigned int    saved_config_space[16]; /* config space saved at suspend time */
 #ifdef CONFIG_PCI_NAMES
 #define PCI_NAME_SIZE  96
 #define PCI_NAME_HALF  __stringify(43) /* less than half to handle slop */