[RFC][PATCH] PCI: Enable Function Level Reset(FLR) for PCI-E

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Sheng Yang <sheng.yang@xxxxxxxxx>
Date: Wed, 27 Aug 2008 17:29:04 +0800
Subject: [PATCH] PCI: Enable Function Level Reset(FLR) for PCI-E

The FLR mechanism enables software to quiesce and reset Endpoint 
hardware with Function-level granularity.

Current the usage model for this is VT-d support in KVM. We'd better 
to do FLR before assign device to the guest.

This can also be used with other purpose. Please refer to PCI Express 
spec chapter 6.6.2.

Signed-off-by: Sheng Yang <sheng.yang@xxxxxxxxx>
---
 drivers/pci/pci.c        |   71 
++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h      |    1 +
 include/linux/pci_regs.h |    2 +
 3 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c9884bb..5d67751 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -18,6 +18,7 @@
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
+#include <linux/jiffies.h>
 #include <asm/dma.h>	/* isa_dma_bridge_buggy */
 #include "pci.h"

@@ -1849,6 +1850,76 @@ out:
 EXPORT_SYMBOL(pcie_set_readrq);

 /**
+ * pcie_do_flr - execute Function Level Reset on device if capability 
exist
+ * @dev: PCI device to execute FLR
+ *
+ * Returns 0 on success, or -EOPNOTSUPP if device don't support the 
feature.
+ *
+ * Notice that on rare case, the Transaction Pending bit does not 
clear before
+ * FLR, we forced to continue the process, bless everything is going 
well...
+ */
+int pcie_do_flr(struct pci_dev *dev)
+{
+#define FLR_CONF_SPACE_BACKUP_SIZE	    16
+	u16 status;
+	u32 cap, cs_backup[FLR_CONF_SPACE_BACKUP_SIZE];
+	int i;
+	int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	unsigned long start;
+
+	if (exppos) {
+		pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap);
+		if (!(cap & PCI_EXP_DEVCAP_FLR))
+			return -EOPNOTSUPP;
+	}
+	/* Backup PCI configuration space */
+	for (i = 0; i < FLR_CONF_SPACE_BACKUP_SIZE; i++)
+		pci_read_config_dword(dev, i * 0x04, &cs_backup[i]);
+
+	/* Clear entire Command register */
+	pci_write_config_word(dev, PCI_COMMAND, 0);
+	pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL, 0);
+
+	start = jiffies;
+	/* Wait 100ms for Transaction Pending bit clean */
+	while (jiffies - start < HZ / 10) {
+		pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
+		if (!(status & PCI_EXP_DEVSTA_TRPND))
+			break;
+		cpu_relax();
+	}
+	if (status & PCI_EXP_DEVSTA_TRPND) {
+		printk(KERN_INFO
+			"Transacation Pending bit haven't been cleaned. "
+			"Longer wait time involved, 5 seconds at most.\n");
+		start = jiffies;
+		while (jiffies - start <= HZ * 5) {
+			pci_read_config_word(dev,
+					     exppos + PCI_EXP_DEVSTA, &status);
+			if (!(status & PCI_EXP_DEVSTA_TRPND))
+				break;
+			cpu_relax();
+		}
+		if (jiffies - start <= HZ * 5)
+			printk(KERN_INFO
+			"Transacation Pending bit have been cleaned.\n");
+		else
+			printk(KERN_INFO
+			"Fail to wait Transacation Pending bit clean,"
+			" force to FLR\n");
+	}
+	pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL,
+				PCI_EXP_DEVCTL_BCR_FLR);
+	mdelay(100);
+
+	/* Restore PCI configuration space */
+	for (i = 0; i < FLR_CONF_SPACE_BACKUP_SIZE; i++)
+		pci_write_config_dword(dev, i * 0x04, cs_backup[i]);
+	return 0;
+}
+EXPORT_SYMBOL(pcie_do_flr);
+
+/**
  * pci_select_bars - Make BAR mask from the type of resource
  * @dev: the PCI device for which BAR mask is made
  * @flags: resource type mask to be selected
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c0e1400..ac804c6 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -626,6 +626,7 @@ int pcix_get_mmrbc(struct pci_dev *dev);
 int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
 int pcie_get_readrq(struct pci_dev *dev);
 int pcie_set_readrq(struct pci_dev *dev, int rq);
+int pcie_do_flr(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, struct resource *res, 
int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int pci_select_bars(struct pci_dev *dev, unsigned long flags);
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index 450684f..ee55f27 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -377,6 +377,7 @@
 #define  PCI_EXP_DEVCAP_RBER	0x8000	/* Role-Based Error Reporting */
 #define  PCI_EXP_DEVCAP_PWR_VAL	0x3fc0000 /* Slot Power Limit Value 
*/
 #define  PCI_EXP_DEVCAP_PWR_SCL	0xc000000 /* Slot Power Limit Scale 
*/
+#define  PCI_EXP_DEVCAP_FLR     0x10000000 /* Function Level Reset */
 #define PCI_EXP_DEVCTL		8	/* Device Control */
 #define  PCI_EXP_DEVCTL_CERE	0x0001	/* Correctable Error Reporting 
En. */
 #define  PCI_EXP_DEVCTL_NFERE	0x0002	/* Non-Fatal Error Reporting 
Enable */
@@ -389,6 +390,7 @@
 #define  PCI_EXP_DEVCTL_AUX_PME	0x0400	/* Auxiliary Power PM Enable 
*/
 #define  PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800  /* Enable No Snoop */
 #define  PCI_EXP_DEVCTL_READRQ	0x7000	/* Max_Read_Request_Size */
+#define  PCI_EXP_DEVCTL_BCR_FLR 0x8000  /* Bridge Configuration 
Retry / FLR */
 #define PCI_EXP_DEVSTA		10	/* Device Status */
 #define  PCI_EXP_DEVSTA_CED	0x01	/* Correctable Error Detected */
 #define  PCI_EXP_DEVSTA_NFED	0x02	/* Non-Fatal Error Detected */
--
1.5.6

From 4f359ccba363ad50747ee70b0a63e2aae14c4c60 Mon Sep 17 00:00:00 2001
From: Sheng Yang <sheng.yang@xxxxxxxxx>
Date: Wed, 27 Aug 2008 17:29:04 +0800
Subject: [PATCH] PCI: Enable Function Level Reset(FLR) for PCI-E

The FLR mechanism enables software to quiesce and reset Endpoint hardware with
Function-level granularity.

Current the usage model for this is VT-d support in KVM. We'd better to do FLR
before assign device to the guest.

This can also be used with other purpose. Please refer to PCI Express spec
chapter 6.6.2.

Signed-off-by: Sheng Yang <sheng.yang@xxxxxxxxx>
---
 drivers/pci/pci.c        |   71 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h      |    1 +
 include/linux/pci_regs.h |    2 +
 3 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c9884bb..5d67751 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -18,6 +18,7 @@
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
+#include <linux/jiffies.h>
 #include <asm/dma.h>	/* isa_dma_bridge_buggy */
 #include "pci.h"
 
@@ -1849,6 +1850,76 @@ out:
 EXPORT_SYMBOL(pcie_set_readrq);
 
 /**
+ * pcie_do_flr - execute Function Level Reset on device if capability exist
+ * @dev: PCI device to execute FLR
+ *
+ * Returns 0 on success, or -EOPNOTSUPP if device don't support the feature.
+ *
+ * Notice that on rare case, the Transaction Pending bit does not clear before
+ * FLR, we forced to continue the process, bless everything is going well...
+ */
+int pcie_do_flr(struct pci_dev *dev)
+{
+#define FLR_CONF_SPACE_BACKUP_SIZE	    16
+	u16 status;
+	u32 cap, cs_backup[FLR_CONF_SPACE_BACKUP_SIZE];
+	int i;
+	int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	unsigned long start;
+
+	if (exppos) {
+		pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap);
+		if (!(cap & PCI_EXP_DEVCAP_FLR))
+			return -EOPNOTSUPP;
+	}
+	/* Backup PCI configuration space */
+	for (i = 0; i < FLR_CONF_SPACE_BACKUP_SIZE; i++)
+		pci_read_config_dword(dev, i * 0x04, &cs_backup[i]);
+
+	/* Clear entire Command register */
+	pci_write_config_word(dev, PCI_COMMAND, 0);
+	pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL, 0);
+
+	start = jiffies;
+	/* Wait 100ms for Transaction Pending bit clean */
+	while (jiffies - start < HZ / 10) {
+		pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
+		if (!(status & PCI_EXP_DEVSTA_TRPND))
+			break;
+		cpu_relax();
+	}
+	if (status & PCI_EXP_DEVSTA_TRPND) {
+		printk(KERN_INFO
+			"Transacation Pending bit haven't been cleaned. "
+			"Longer wait time involved, 5 seconds at most.\n");
+		start = jiffies;
+		while (jiffies - start <= HZ * 5) {
+			pci_read_config_word(dev,
+					     exppos + PCI_EXP_DEVSTA, &status);
+			if (!(status & PCI_EXP_DEVSTA_TRPND))
+				break;
+			cpu_relax();
+		}
+		if (jiffies - start <= HZ * 5)
+			printk(KERN_INFO
+			"Transacation Pending bit have been cleaned.\n");
+		else
+			printk(KERN_INFO
+			"Fail to wait Transacation Pending bit clean,"
+			" force to FLR\n");
+	}
+	pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL,
+				PCI_EXP_DEVCTL_BCR_FLR);
+	mdelay(100);
+
+	/* Restore PCI configuration space */
+	for (i = 0; i < FLR_CONF_SPACE_BACKUP_SIZE; i++)
+		pci_write_config_dword(dev, i * 0x04, cs_backup[i]);
+	return 0;
+}
+EXPORT_SYMBOL(pcie_do_flr);
+
+/**
  * pci_select_bars - Make BAR mask from the type of resource
  * @dev: the PCI device for which BAR mask is made
  * @flags: resource type mask to be selected
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c0e1400..ac804c6 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -626,6 +626,7 @@ int pcix_get_mmrbc(struct pci_dev *dev);
 int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
 int pcie_get_readrq(struct pci_dev *dev);
 int pcie_set_readrq(struct pci_dev *dev, int rq);
+int pcie_do_flr(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int pci_select_bars(struct pci_dev *dev, unsigned long flags);
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index 450684f..ee55f27 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -377,6 +377,7 @@
 #define  PCI_EXP_DEVCAP_RBER	0x8000	/* Role-Based Error Reporting */
 #define  PCI_EXP_DEVCAP_PWR_VAL	0x3fc0000 /* Slot Power Limit Value */
 #define  PCI_EXP_DEVCAP_PWR_SCL	0xc000000 /* Slot Power Limit Scale */
+#define  PCI_EXP_DEVCAP_FLR     0x10000000 /* Function Level Reset */
 #define PCI_EXP_DEVCTL		8	/* Device Control */
 #define  PCI_EXP_DEVCTL_CERE	0x0001	/* Correctable Error Reporting En. */
 #define  PCI_EXP_DEVCTL_NFERE	0x0002	/* Non-Fatal Error Reporting Enable */
@@ -389,6 +390,7 @@
 #define  PCI_EXP_DEVCTL_AUX_PME	0x0400	/* Auxiliary Power PM Enable */
 #define  PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800  /* Enable No Snoop */
 #define  PCI_EXP_DEVCTL_READRQ	0x7000	/* Max_Read_Request_Size */
+#define  PCI_EXP_DEVCTL_BCR_FLR 0x8000  /* Bridge Configuration Retry / FLR */
 #define PCI_EXP_DEVSTA		10	/* Device Status */
 #define  PCI_EXP_DEVSTA_CED	0x01	/* Correctable Error Detected */
 #define  PCI_EXP_DEVSTA_NFED	0x02	/* Non-Fatal Error Detected */
-- 
1.5.6


[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux