diff mbox

[14/22] aerdrv: introduce default_downstream_reset_link

Message ID 4BBC34AD.1040100@jp.fujitsu.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Hidetoshi Seto April 7, 2010, 7:30 a.m. UTC
None
diff mbox

Patch

diff --git a/Documentation/PCI/pcieaer-howto.txt b/Documentation/PCI/pcieaer-howto.txt
index 75cbe53..1576163 100644
--- a/Documentation/PCI/pcieaer-howto.txt
+++ b/Documentation/PCI/pcieaer-howto.txt
@@ -13,7 +13,7 @@  Reporting (AER) driver and provides information on how to use it, as
 well as how to enable the drivers of endpoint devices to conform with
 PCI Express AER driver.
 
-1.2 Copyright ã‚¥ Intel Corporation 2006.
+1.2 Copyright (C) Intel Corporation 2006.
 
 1.3 What is the PCI Express AER Driver?
 
@@ -109,7 +109,7 @@  but the PCI Express link itself is fully functional. Fatal errors, on
 the other hand, cause the link to be unreliable.
 
 When AER is enabled, a PCI Express device will automatically send an
-error message to the PCIE root port above it when the device captures
+error message to the PCIe root port above it when the device captures
 an error. The Root Port, upon receiving an error reporting message,
 internally processes and logs the error message in its PCI Express
 capability structure. Error information being logged includes storing
@@ -195,8 +195,9 @@  to reset link, AER port service driver is required to provide the
 function to reset link. Firstly, kernel looks for if the upstream
 component has an aer driver. If it has, kernel uses the reset_link
 callback of the aer driver. If the upstream component has no aer driver
-and the port is downstream port, we will use the aer driver of the
-root port who reports the AER error. As for upstream ports,
+and the port is downstream port, we will perform a hot reset as the
+default by setting the Secondary Bus Reset bit of the Bridge Control
+register associated with the downstream port. As for upstream ports,
 they should provide their own aer service drivers with reset_link
 function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
 reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes
@@ -250,11 +251,11 @@  cleanup uncorrectable status register. Pls. refer to section 3.3.
 
 4. Software error injection
 
-Debugging PCIE AER error recovery code is quite difficult because it
+Debugging PCIe AER error recovery code is quite difficult because it
 is hard to trigger real hardware errors. Software based error
-injection can be used to fake various kinds of PCIE errors.
+injection can be used to fake various kinds of PCIe errors.
 
-First you should enable PCIE AER software error injection in kernel
+First you should enable PCIe AER software error injection in kernel
 configuration, that is, following item should be in your .config.
 
 CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index a76b4d4..8344127 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -234,7 +234,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;
 
@@ -245,27 +244,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 f0029a0..f9874f3 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 void aer_enable_rootport(struct aer_rpc *rpc);
 extern void aer_disable_rootport(struct aer_rpc *rpc);
 extern int aer_init(struct pcie_device *dev);
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index d2ccda9..df735cd 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -384,6 +384,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
+	 * 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);
+}
+
+/**
+ * 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;
@@ -418,31 +465,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",