PCI: Extend pci_reset_function() to support PCI Advanced Features
authorSheng Yang <sheng@linux.intel.com>
Tue, 11 Nov 2008 09:17:48 +0000 (17:17 +0800)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 7 Jan 2009 19:12:25 +0000 (11:12 -0800)
Some PCI devices implement PCI Advanced Features, which means they
support Function Level Reset(FLR).  Implement support for that in
pci_reset_function.

Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/pci.c

index 62978f644a9203eb18681bf596ecfd499a72f51d..3c2fa2fdc9cd965d09af0d6338d17df2004c673e 100644 (file)
@@ -1789,6 +1789,43 @@ static int __pcie_flr(struct pci_dev *dev, int probe)
        return 0;
 }
 
+static int __pci_af_flr(struct pci_dev *dev, int probe)
+{
+       int cappos = pci_find_capability(dev, PCI_CAP_ID_AF);
+       u8 status;
+       u8 cap;
+
+       if (!cappos)
+               return -ENOTTY;
+       pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap);
+       if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR))
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       pci_block_user_cfg_access(dev);
+
+       /* Wait for Transaction Pending bit clean */
+       msleep(100);
+       pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status);
+       if (status & PCI_AF_STATUS_TP) {
+               dev_info(&dev->dev, "Busy after 100ms while trying to"
+                               " reset; sleeping for 1 second\n");
+               ssleep(1);
+               pci_read_config_byte(dev,
+                               cappos + PCI_AF_STATUS, &status);
+               if (status & PCI_AF_STATUS_TP)
+                       dev_info(&dev->dev, "Still busy after 1s; "
+                                       "proceeding with reset anyway\n");
+       }
+       pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
+       mdelay(100);
+
+       pci_unblock_user_cfg_access(dev);
+       return 0;
+}
+
 static int __pci_reset_function(struct pci_dev *pdev, int probe)
 {
        int res;
@@ -1797,6 +1834,10 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe)
        if (res != -ENOTTY)
                return res;
 
+       res = __pci_af_flr(pdev, probe);
+       if (res != -ENOTTY)
+               return res;
+
        return res;
 }