summary refs log tree commit diff
path: root/drivers/pci/pcie
diff options
context:
space:
mode:
authorHidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>2010-04-15 13:21:27 +0900
committerJesse Barnes <jbarnes@virtuousgeek.org>2010-05-11 12:01:38 -0700
commit89713422a768458a0d375f0c2f3586cd5ccde6a1 (patch)
treec446440123602cdb5320617ac7a8c2dbf514ff41 /drivers/pci/pcie
parent517cae3829ae8cc3033c24f60e64eb251b2f0d14 (diff)
downloadlinux-89713422a768458a0d375f0c2f3586cd5ccde6a1.tar.gz
PCI: aerdrv: introduce default_downstream_reset_link
I noticed that when I inject a fatal error to an endpoint via
aer-inject, aer_root_reset() is called as reset_link for a
downstream port at upstream of the endpoint:

  pcieport 0000:00:06.0: AER: Uncorrected (Fatal) error received: id=5401
   :
  pcieport 0000:52:02.0: Root Port link has been reset

It externally appears to be working, but internally issues some
accesses to PCI_ERR_ROOT_COMMAND/STATUS registers that is for
root port so not available on downstream port.

This patch introduces default_downstream_reset_link that is
a version of aer_root_reset() with no accesses to root port's
register. It is used for downstream ports that has no reset_link
function its specific.

This patch also updates related description in pcieaer-howto.txt.
Some minor fixes are included.

Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c23
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h1
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c78
3 files changed, 63 insertions, 39 deletions
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index cbc7cc77b2c3..a225d58c1ac8 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -341,7 +341,6 @@ static int __devinit aer_probe(struct pcie_device *dev)
  **/
 static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
 {
-	u16 p2p_ctrl;
 	u32 reg32;
 	int pos;
 
@@ -352,27 +351,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
 	reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
 	pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-	/* Assert Secondary Bus Reset */
-	pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
-	p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
-	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
-	/*
-	 * we should send hot reset message for 2ms to allow it time to
-	 * propogate to all downstream ports
-	 */
-	msleep(2);
-
-	/* De-assert Secondary Bus Reset */
-	p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
-	/*
-	 * System software must wait for at least 100ms from the end
-	 * of a reset of one or more device before it is permitted
-	 * to issue Configuration Requests to those devices.
-	 */
-	msleep(200);
+	aer_do_secondary_bus_reset(dev);
 	dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
 
 	/* Clear Root Error Status */
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index d0f8291c5ca0..7aaae2d2bd67 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -114,6 +114,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
 }
 
 extern struct bus_type pcie_port_bus_type;
+extern void aer_do_secondary_bus_reset(struct pci_dev *dev);
 extern int aer_init(struct pcie_device *dev);
 extern void aer_isr(struct work_struct *work);
 extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 8fb14aeb74dd..ce42cac99dd3 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -373,6 +373,53 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
 	return result_data.result;
 }
 
+/**
+ * aer_do_secondary_bus_reset - perform secondary bus reset
+ * @dev: pointer to bridge's pci_dev data structure
+ *
+ * Invoked when performing link reset at Root Port or Downstream Port.
+ */
+void aer_do_secondary_bus_reset(struct pci_dev *dev)
+{
+	u16 p2p_ctrl;
+
+	/* Assert Secondary Bus Reset */
+	pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
+	p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+	/*
+	 * we should send hot reset message for 2ms to allow it time to
+	 * propagate to all downstream ports
+	 */
+	msleep(2);
+
+	/* De-assert Secondary Bus Reset */
+	p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+	/*
+	 * System software must wait for at least 100ms from the end
+	 * of a reset of one or more device before it is permitted
+	 * to issue Configuration Requests to those devices.
+	 */
+	msleep(200);
+}
+
+/**
+ * default_downstream_reset_link - default reset function for Downstream Port
+ * @dev: pointer to downstream port's pci_dev data structure
+ *
+ * Invoked when performing link reset at Downstream Port w/ no aer driver.
+ */
+static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev)
+{
+	aer_do_secondary_bus_reset(dev);
+	dev_printk(KERN_DEBUG, &dev->dev,
+		"Downstream Port link has been reset\n");
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
 static int find_aer_service_iter(struct device *device, void *data)
 {
 	struct pcie_port_service_driver *service_driver, **drv;
@@ -406,31 +453,28 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
 	pci_ers_result_t status;
 	struct pcie_port_service_driver *driver;
 
-	if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
+	if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+		/* Reset this port for all subordinates */
 		udev = dev;
-	else
+	} else {
+		/* Reset the upstream component (likely downstream port) */
 		udev = dev->bus->self;
+	}
 
 	/* Use the aer driver of the component firstly */
 	driver = find_aer_service(udev);
 
-	/*
-	 * If it hasn't the driver and is downstream port, use the root port's
-	 */
-	if (!driver || !driver->reset_link) {
-		if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM &&
-			aerdev->device.driver &&
-			to_service_driver(aerdev->device.driver)->reset_link) {
-			driver = to_service_driver(aerdev->device.driver);
-		} else {
-			dev_printk(KERN_DEBUG, &dev->dev,
-				"no link-reset support at upstream device %s\n",
-				pci_name(udev));
-			return PCI_ERS_RESULT_DISCONNECT;
-		}
+	if (driver && driver->reset_link) {
+		status = driver->reset_link(udev);
+	} else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+		status = default_downstream_reset_link(udev);
+	} else {
+		dev_printk(KERN_DEBUG, &dev->dev,
+			"no link-reset support at upstream device %s\n",
+			pci_name(udev));
+		return PCI_ERS_RESULT_DISCONNECT;
 	}
 
-	status = driver->reset_link(udev);
 	if (status != PCI_ERS_RESULT_RECOVERED) {
 		dev_printk(KERN_DEBUG, &dev->dev,
 			"link reset at upstream device %s failed\n",