Commit 76d826c3 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/reset'

- Add pci_reset_bus_function() Secondary Bus Reset interface (Raphael
  Norwitz)

- Work around Huawei Intelligent NIC VF FLR erratum (Chiqijun)

* pci/reset:
  PCI: Work around Huawei Intelligent NIC VF FLR erratum
  PCI: Add pci_reset_bus_function() Secondary Bus Reset interface
parents e92605b0 411e2a43
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -5020,6 +5020,16 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
	return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
}

static int pci_reset_bus_function(struct pci_dev *dev, int probe)
{
	int rc;

	rc = pci_dev_reset_slot_function(dev, probe);
	if (rc != -ENOTTY)
		return rc;
	return pci_parent_bus_reset(dev, probe);
}

static void pci_dev_lock(struct pci_dev *dev)
{
	pci_cfg_access_lock(dev);
@@ -5140,10 +5150,7 @@ int __pci_reset_function_locked(struct pci_dev *dev)
	rc = pci_pm_reset(dev, 0);
	if (rc != -ENOTTY)
		return rc;
	rc = pci_dev_reset_slot_function(dev, 0);
	if (rc != -ENOTTY)
		return rc;
	return pci_parent_bus_reset(dev, 0);
	return pci_reset_bus_function(dev, 0);
}
EXPORT_SYMBOL_GPL(__pci_reset_function_locked);

@@ -5173,13 +5180,10 @@ int pci_probe_reset_function(struct pci_dev *dev)
	if (rc != -ENOTTY)
		return rc;
	rc = pci_pm_reset(dev, 1);
	if (rc != -ENOTTY)
		return rc;
	rc = pci_dev_reset_slot_function(dev, 1);
	if (rc != -ENOTTY)
		return rc;

	return pci_parent_bus_reset(dev, 1);
	return pci_reset_bus_function(dev, 1);
}

/**
+65 −0
Original line number Diff line number Diff line
@@ -3912,6 +3912,69 @@ static int delay_250ms_after_flr(struct pci_dev *dev, int probe)
	return 0;
}

#define PCI_DEVICE_ID_HINIC_VF      0x375E
#define HINIC_VF_FLR_TYPE           0x1000
#define HINIC_VF_FLR_CAP_BIT        (1UL << 30)
#define HINIC_VF_OP                 0xE80
#define HINIC_VF_FLR_PROC_BIT       (1UL << 18)
#define HINIC_OPERATION_TIMEOUT     15000	/* 15 seconds */

/* Device-specific reset method for Huawei Intelligent NIC virtual functions */
static int reset_hinic_vf_dev(struct pci_dev *pdev, int probe)
{
	unsigned long timeout;
	void __iomem *bar;
	u32 val;

	if (probe)
		return 0;

	bar = pci_iomap(pdev, 0, 0);
	if (!bar)
		return -ENOTTY;

	/* Get and check firmware capabilities */
	val = ioread32be(bar + HINIC_VF_FLR_TYPE);
	if (!(val & HINIC_VF_FLR_CAP_BIT)) {
		pci_iounmap(pdev, bar);
		return -ENOTTY;
	}

	/* Set HINIC_VF_FLR_PROC_BIT for the start of FLR */
	val = ioread32be(bar + HINIC_VF_OP);
	val = val | HINIC_VF_FLR_PROC_BIT;
	iowrite32be(val, bar + HINIC_VF_OP);

	pcie_flr(pdev);

	/*
	 * The device must recapture its Bus and Device Numbers after FLR
	 * in order generate Completions.  Issue a config write to let the
	 * device capture this information.
	 */
	pci_write_config_word(pdev, PCI_VENDOR_ID, 0);

	/* Firmware clears HINIC_VF_FLR_PROC_BIT when reset is complete */
	timeout = jiffies + msecs_to_jiffies(HINIC_OPERATION_TIMEOUT);
	do {
		val = ioread32be(bar + HINIC_VF_OP);
		if (!(val & HINIC_VF_FLR_PROC_BIT))
			goto reset_complete;
		msleep(20);
	} while (time_before(jiffies, timeout));

	val = ioread32be(bar + HINIC_VF_OP);
	if (!(val & HINIC_VF_FLR_PROC_BIT))
		goto reset_complete;

	pci_warn(pdev, "Reset dev timeout, FLR ack reg: %#010x\n", val);

reset_complete:
	pci_iounmap(pdev, bar);

	return 0;
}

static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF,
		 reset_intel_82599_sfp_virtfn },
@@ -3924,6 +3987,8 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
	{ PCI_VENDOR_ID_INTEL, 0x0a54, delay_250ms_after_flr },
	{ PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
		reset_chelsio_generic_dev },
	{ PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_HINIC_VF,
		reset_hinic_vf_dev },
	{ 0 }
};