summary refs log tree commit diff
path: root/drivers/pci
diff options
context:
space:
mode:
authorSheng Yang <sheng@linux.intel.com>2008-11-11 17:17:48 +0800
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-01-07 11:12:25 -0800
commit1ca887970a3971a22e4875b7c6ad5ae3ce49f61a (patch)
tree8368a3f90da7ee894818658ff925478ef0b43947 /drivers/pci
parentf7b7baae6b30ff04124259ff8d7c0c0d281320e6 (diff)
downloadlinux-1ca887970a3971a22e4875b7c6ad5ae3ce49f61a.tar.gz
PCI: Extend pci_reset_function() to support PCI Advanced Features
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 62978f644a92..3c2fa2fdc9cd 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -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;
 }