aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-04-15 15:40:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-04-15 16:06:29 -0400
commitebfc5b802fa76baeb4371311ff9fc27a2258d90d (patch)
tree03da364fee4f182d3e0e2f98e8e20519c0b040b7 /drivers
parent6c23b8e9330c77557fa9658db751029675dd195a (diff)
PCI: Fix regression in pci_restore_state(), v3
Commit 26f41062f28d ("PCI: check for pci bar restore completion and retry") attempted to address problems with PCI BAR restoration on systems where FLR had not been completed before pci_restore_state() was called, but it did that in an utterly wrong way. First off, instead of retrying the writes for the BAR registers only, it did that for all of the PCI config space of the device, including the status register (whose value after the write quite obviously need not be the same as the written one). Second, it added arbitrary delay to pci_restore_state() even for systems where the PCI config space restoration was successful at first attempt. Finally, the mdelay(10) it added to every iteration of the writing loop was way too much of a delay for any reasonable device. All of this actually caused resume failures for some devices on Mikko's system. To fix the regression, make pci_restore_state() only retry the writes for BAR registers and only wait if the first read from the register doesn't return the written value. Additionaly, make it wait for 1 ms, instead of 10 ms, after every failing attempt to write into config space. Reported-by: Mikko Vinni <mmvinni@yahoo.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/pci.c57
1 files changed, 39 insertions, 18 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 815674415267..d20f1334792b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -967,16 +967,47 @@ pci_save_state(struct pci_dev *dev)
967 return 0; 967 return 0;
968} 968}
969 969
970static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
971 u32 saved_val, int retry)
972{
973 u32 val;
974
975 pci_read_config_dword(pdev, offset, &val);
976 if (val == saved_val)
977 return;
978
979 for (;;) {
980 dev_dbg(&pdev->dev, "restoring config space at offset "
981 "%#x (was %#x, writing %#x)\n", offset, val, saved_val);
982 pci_write_config_dword(pdev, offset, saved_val);
983 if (retry-- <= 0)
984 return;
985
986 pci_read_config_dword(pdev, offset, &val);
987 if (val == saved_val)
988 return;
989
990 mdelay(1);
991 }
992}
993
994static void pci_restore_config_space(struct pci_dev *pdev, int start, int end,
995 int retry)
996{
997 int index;
998
999 for (index = end; index >= start; index--)
1000 pci_restore_config_dword(pdev, 4 * index,
1001 pdev->saved_config_space[index],
1002 retry);
1003}
1004
970/** 1005/**
971 * pci_restore_state - Restore the saved state of a PCI device 1006 * pci_restore_state - Restore the saved state of a PCI device
972 * @dev: - PCI device that we're dealing with 1007 * @dev: - PCI device that we're dealing with
973 */ 1008 */
974void pci_restore_state(struct pci_dev *dev) 1009void pci_restore_state(struct pci_dev *dev)
975{ 1010{
976 int i;
977 u32 val;
978 int tries;
979
980 if (!dev->state_saved) 1011 if (!dev->state_saved)
981 return; 1012 return;
982 1013
@@ -984,24 +1015,14 @@ void pci_restore_state(struct pci_dev *dev)
984 pci_restore_pcie_state(dev); 1015 pci_restore_pcie_state(dev);
985 pci_restore_ats_state(dev); 1016 pci_restore_ats_state(dev);
986 1017
1018 pci_restore_config_space(dev, 10, 15, 0);
987 /* 1019 /*
988 * The Base Address register should be programmed before the command 1020 * The Base Address register should be programmed before the command
989 * register(s) 1021 * register(s)
990 */ 1022 */
991 for (i = 15; i >= 0; i--) { 1023 pci_restore_config_space(dev, 4, 9, 10);
992 pci_read_config_dword(dev, i * 4, &val); 1024 pci_restore_config_space(dev, 0, 3, 0);
993 tries = 10; 1025
994 while (tries && val != dev->saved_config_space[i]) {
995 dev_dbg(&dev->dev, "restoring config "
996 "space at offset %#x (was %#x, writing %#x)\n",
997 i, val, (int)dev->saved_config_space[i]);
998 pci_write_config_dword(dev,i * 4,
999 dev->saved_config_space[i]);
1000 pci_read_config_dword(dev, i * 4, &val);
1001 mdelay(10);
1002 tries--;
1003 }
1004 }
1005 pci_restore_pcix_state(dev); 1026 pci_restore_pcix_state(dev);
1006 pci_restore_msi_state(dev); 1027 pci_restore_msi_state(dev);
1007 pci_restore_iov_state(dev); 1028 pci_restore_iov_state(dev);