Commit 1abb4739 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/enumeration'

- Add PCI_EXT_CAP_ID_PL_32GT define (Ben Dooks)

- Propagate firmware node by calling device_set_node() for better
  modularity (Andy Shevchenko)

- Discover Data Link Layer Link Active Reporting earlier so quirks can take
  advantage of it (Maciej W. Rozycki)

- Use cached Data Link Layer Link Active Reporting capability in pciehp,
  powerpc/eeh, and mlx5 (Maciej W. Rozycki)

- Run quirk for devices that require OS to clear Retrain Link earlier, so
  later quirks can rely on it (Maciej W. Rozycki)

- Export pcie_retrain_link() for use outside ASPM (Maciej W. Rozycki)

- Add Data Link Layer Link Active Reporting as another way for
  pcie_retrain_link() to determine the link is up (Maciej W. Rozycki)

- Work around link training failures (especially on the ASMedia ASM2824
  switch) by training first at 2.5GT/s and then attempting higher rates
  (Maciej W. Rozycki)

* pci/enumeration:
  PCI: Add failed link recovery for device reset events
  PCI: Work around PCIe link training failures
  PCI: Use pcie_wait_for_link_status() in pcie_wait_for_link_delay()
  PCI: Add support for polling DLLLA to pcie_retrain_link()
  PCI: Export pcie_retrain_link() for use outside ASPM
  PCI: Export PCIe link retrain timeout
  PCI: Execute quirk_enable_clear_retrain_link() earlier
  PCI/ASPM: Factor out waiting for link training to complete
  PCI/ASPM: Avoid unnecessary pcie_link_state use
  PCI/ASPM: Use distinct local vars in pcie_retrain_link()
  net/mlx5: Rely on dev->link_active_reporting
  powerpc/eeh: Rely on dev->link_active_reporting
  PCI: pciehp: Rely on dev->link_active_reporting
  PCI: Initialize dev->link_active_reporting earlier
  PCI: of: Propagate firmware node by calling device_set_node()
  PCI: Add PCI_EXT_CAP_ID_PL_32GT define

# Conflicts:
#	drivers/pci/pcie/aspm.c
parents 0f32114e 08e3ed12
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -671,9 +671,8 @@ static void eeh_bridge_check_link(struct eeh_dev *edev)
	eeh_ops->write_config(edev, cap + PCI_EXP_LNKCTL, 2, val);

	/* Check link */
	eeh_ops->read_config(edev, cap + PCI_EXP_LNKCAP, 4, &val);
	if (!(val & PCI_EXP_LNKCAP_DLLLARC)) {
		eeh_edev_dbg(edev, "No link reporting capability (0x%08x) \n", val);
	if (!edev->pdev->link_active_reporting) {
		eeh_edev_dbg(edev, "No link reporting capability\n");
		msleep(1000);
		return;
	}
+2 −6
Original line number Diff line number Diff line
@@ -307,7 +307,6 @@ static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev)
	unsigned long timeout;
	struct pci_dev *sdev;
	int cap, err;
	u32 reg32;

	/* Check that all functions under the pci bridge are PFs of
	 * this device otherwise fail this function.
@@ -346,11 +345,8 @@ static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev)
		return err;

	/* Check link */
	err = pci_read_config_dword(bridge, cap + PCI_EXP_LNKCAP, &reg32);
	if (err)
		return err;
	if (!(reg32 & PCI_EXP_LNKCAP_DLLLARC)) {
		mlx5_core_warn(dev, "No PCI link reporting capability (0x%08x)\n", reg32);
	if (!bridge->link_active_reporting) {
		mlx5_core_warn(dev, "No PCI link reporting capability\n");
		msleep(1000);
		goto restore;
	}
+2 −5
Original line number Diff line number Diff line
@@ -984,7 +984,7 @@ static inline int pcie_hotplug_depth(struct pci_dev *dev)
struct controller *pcie_init(struct pcie_device *dev)
{
	struct controller *ctrl;
	u32 slot_cap, slot_cap2, link_cap;
	u32 slot_cap, slot_cap2;
	u8 poweron;
	struct pci_dev *pdev = dev->port;
	struct pci_bus *subordinate = pdev->subordinate;
@@ -1030,9 +1030,6 @@ struct controller *pcie_init(struct pcie_device *dev)
	if (dmi_first_match(inband_presence_disabled_dmi_table))
		ctrl->inband_presence_disabled = 1;

	/* Check if Data Link Layer Link Active Reporting is implemented */
	pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);

	/* Clear all remaining event bits in Slot Status register. */
	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
		PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
@@ -1051,7 +1048,7 @@ struct controller *pcie_init(struct pcie_device *dev)
		FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
		FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
		FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD),
		FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC),
		FLAG(pdev->link_active_reporting, true),
		pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");

	/*
+4 −10
Original line number Diff line number Diff line
@@ -39,16 +39,14 @@ int pci_set_of_node(struct pci_dev *dev)
		return -ENODEV;
	}

	dev->dev.of_node = node;
	dev->dev.fwnode = &node->fwnode;
	device_set_node(&dev->dev, of_fwnode_handle(node));
	return 0;
}

void pci_release_of_node(struct pci_dev *dev)
{
	of_node_put(dev->dev.of_node);
	dev->dev.of_node = NULL;
	dev->dev.fwnode = NULL;
	device_set_node(&dev->dev, NULL);
}

void pci_set_bus_of_node(struct pci_bus *bus)
@@ -63,17 +61,13 @@ void pci_set_bus_of_node(struct pci_bus *bus)
			bus->self->external_facing = true;
	}

	bus->dev.of_node = node;

	if (bus->dev.of_node)
		bus->dev.fwnode = &bus->dev.of_node->fwnode;
	device_set_node(&bus->dev, of_fwnode_handle(node));
}

void pci_release_bus_of_node(struct pci_bus *bus)
{
	of_node_put(bus->dev.of_node);
	bus->dev.of_node = NULL;
	bus->dev.fwnode = NULL;
	device_set_node(&bus->dev, NULL);
}

struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
+112 −21
Original line number Diff line number Diff line
@@ -1156,7 +1156,14 @@ void pci_resume_bus(struct pci_bus *bus)
static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
{
	int delay = 1;
	u32 id;
	bool retrain = false;
	struct pci_dev *bridge;

	if (pci_is_pcie(dev)) {
		bridge = pci_upstream_bridge(dev);
		if (bridge)
			retrain = true;
	}

	/*
	 * After reset, the device should not silently discard config
@@ -1170,21 +1177,33 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
	 * Command register instead of Vendor ID so we don't have to
	 * contend with the CRS SV value.
	 */
	for (;;) {
		u32 id;

		pci_read_config_dword(dev, PCI_COMMAND, &id);
	while (PCI_POSSIBLE_ERROR(id)) {
		if (!PCI_POSSIBLE_ERROR(id))
			break;

		if (delay > timeout) {
			pci_warn(dev, "not ready %dms after %s; giving up\n",
				 delay - 1, reset_type);
			return -ENOTTY;
		}

		if (delay > PCI_RESET_WAIT)
		if (delay > PCI_RESET_WAIT) {
			if (retrain) {
				retrain = false;
				if (pcie_failed_link_retrain(bridge)) {
					delay = 1;
					continue;
				}
			}
			pci_info(dev, "not ready %dms after %s; waiting\n",
				 delay - 1, reset_type);
		}

		msleep(delay);
		delay *= 2;
		pci_read_config_dword(dev, PCI_COMMAND, &id);
	}

	if (delay > PCI_RESET_WAIT)
@@ -4856,6 +4875,79 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
	return pci_dev_wait(dev, "PM D3hot->D0", PCIE_RESET_READY_POLL_MS);
}

/**
 * pcie_wait_for_link_status - Wait for link status change
 * @pdev: Device whose link to wait for.
 * @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE.
 * @active: Waiting for active or inactive?
 *
 * Return 0 if successful, or -ETIMEDOUT if status has not changed within
 * PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds.
 */
static int pcie_wait_for_link_status(struct pci_dev *pdev,
				     bool use_lt, bool active)
{
	u16 lnksta_mask, lnksta_match;
	unsigned long end_jiffies;
	u16 lnksta;

	lnksta_mask = use_lt ? PCI_EXP_LNKSTA_LT : PCI_EXP_LNKSTA_DLLLA;
	lnksta_match = active ? lnksta_mask : 0;

	end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS);
	do {
		pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
		if ((lnksta & lnksta_mask) == lnksta_match)
			return 0;
		msleep(1);
	} while (time_before(jiffies, end_jiffies));

	return -ETIMEDOUT;
}

/**
 * pcie_retrain_link - Request a link retrain and wait for it to complete
 * @pdev: Device whose link to retrain.
 * @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE, for status.
 *
 * Retrain completion status is retrieved from the Link Status Register
 * according to @use_lt.  It is not verified whether the use of the DLLLA
 * bit is valid.
 *
 * Return 0 if successful, or -ETIMEDOUT if training has not completed
 * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds.
 */
int pcie_retrain_link(struct pci_dev *pdev, bool use_lt)
{
	int rc;
	u16 lnkctl;

	/*
	 * Ensure the updated LNKCTL parameters are used during link
	 * training by checking that there is no ongoing link training to
	 * avoid LTSSM race as recommended in Implementation Note at the
	 * end of PCIe r6.0.1 sec 7.5.3.7.
	 */
	rc = pcie_wait_for_link_status(pdev, use_lt, !use_lt);
	if (rc)
		return rc;

	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl);
	lnkctl |= PCI_EXP_LNKCTL_RL;
	pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl);
	if (pdev->clear_retrain_link) {
		/*
		 * Due to an erratum in some devices the Retrain Link bit
		 * needs to be cleared again manually to allow the link
		 * training to succeed.
		 */
		lnkctl &= ~PCI_EXP_LNKCTL_RL;
		pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl);
	}

	return pcie_wait_for_link_status(pdev, use_lt, !use_lt);
}

/**
 * pcie_wait_for_link_delay - Wait until link is active or inactive
 * @pdev: Bridge device
@@ -4867,16 +4959,14 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active,
				     int delay)
{
	int timeout = 1000;
	bool ret;
	u16 lnk_status;
	int rc;

	/*
	 * Some controllers might not implement link active reporting. In this
	 * case, we wait for 1000 ms + any delay requested by the caller.
	 */
	if (!pdev->link_active_reporting) {
		msleep(timeout + delay);
		msleep(PCIE_LINK_RETRAIN_TIMEOUT_MS + delay);
		return true;
	}

@@ -4891,20 +4981,21 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active,
	 */
	if (active)
		msleep(20);
	for (;;) {
		pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
		ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
		if (ret == active)
			break;
		if (timeout <= 0)
			break;
		msleep(10);
		timeout -= 10;
	}
	if (active && ret)
	rc = pcie_wait_for_link_status(pdev, false, active);
	if (active) {
		if (rc)
			rc = pcie_failed_link_retrain(pdev);
		if (rc)
			return false;

		msleep(delay);
		return true;
	}

	return ret == active;
	if (rc)
		return false;

	return true;
}

/**
Loading