Commit 93c398be authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/controller/dwc'

- Extend PCI_FIND_NEXT_CAP() and PCI_FIND_NEXT_EXT_CAP() to return a
  pointer to the preceding Capability (Qiang Yu)

- Add dw_pcie_remove_capability() and dw_pcie_remove_ext_capability() to
  remove Capabilities that are advertised but not fully implemented (Qiang
  Yu)

- Remove MSI and MSI-X Capabilities for DWC controllers in platforms that
  can't support them, so we automatically fall back to INTx (Qiang Yu)

- Remove MSI-X and DPC Capabilities for Qualcomm platforms that advertise
  but don't support them (Qiang Yu)

- Remove duplicate dw_pcie_ep_hide_ext_capability() function and replace
  with dw_pcie_remove_ext_capability() (Qiang Yu)

- Add ASPM L1.1 and L1.2 Substates context to debugfs ltssm_status for
  drivers that support this (Shawn Lin)

- Skip PME_Turn_Off broadcast and L2/L3 transition during suspend if link
  is not up to avoid an unnecessary timeout (Manivannan Sadhasivam)

- Revert dw-rockchip, qcom, and DWC core changes that used link-up IRQs to
  trigger enumeration instead of waiting for link to be up because the PCI
  core doesn't allocate bus number space for hierarchies that might be
  attached (Niklas Cassel)

- Make endpoint iATU entry for MSI permanent instead of programming it
  dynamically, which is slow and racy with respect to other concurrent
  traffic, e.g., eDMA (Koichiro Den)

- Use iMSI-RX MSI target address when possible to fix endpoints using
  32-bit MSI (Shawn Lin)

- Make dw_pcie_ltssm_status_string() available and use it for logging
  errors in dw_pcie_wait_for_link() (Manivannan Sadhasivam)

- Return -ENODEV when dw_pcie_wait_for_link() finds no devices, -EIO for
  device present but inactive, -ETIMEDOUT for other failures, so callers
  can handle these cases differently (Manivannan Sadhasivam)

- Allow DWC host controller driver probe to continue if device is not found
  or found but inactive; only fail when there's an error with the link
  (Manivannan Sadhasivam)

- For controllers like NXP i.MX6QP and i.MX7D, where LTSSM registers are
  not accessible after PME_Turn_Off, simply wait 10ms instead of polling
  for L2/L3 Ready (Richard Zhu)

- Use multiple iATU entries to map large bridge windows and DMA ranges when
  necessary instead of failing (Samuel Holland)

- Rename struct dw_pcie_rp.has_msi_ctrl to .use_imsi_rx for clarity (Qiang
  Yu)

- Add EPC dynamic_inbound_mapping feature bit for Endpoint Controllers that
  can update BAR inbound address translation without requiring EPF driver
  to clear/reset the BAR first, and advertise it for DWC-based Endpoints
  (Koichiro Den)

- Add EPC subrange_mapping feature bit for Endpoint Controllers that can
  map multiple independent inbound regions in a single BAR, implement
  subrange mapping, advertise it for DWC-based Endpoints, and add Endpoint
  selftests for it (Koichiro Den)

- Allow overriding default BAR sizes for pci-epf-test (Niklas Cassel)

- Make resizable BARs work for Endpoint multi-PF configurations; previously
  it only worked for PF 0 (Aksh Garg)

- Fix Endpoint non-PF 0 support for BAR configuration, ATU mappings, and
  Address Match Mode (Aksh Garg)

- Fix issues with outbound iATU index assignment that caused iATU index to
  be out of bounds (Niklas Cassel)

- Clean up iATU index tracking to be consistent (Niklas Cassel)

- Set up iATU when ECAM is enabled; previously IO and MEM outbound windows
  weren't programmed, and ECAM-related iATU entries weren't restored after
  suspend/resume, so config accesses failed (Krishna Chaitanya Chundru)

* pci/controller/dwc:
  PCI: dwc: Fix missing iATU setup when ECAM is enabled
  PCI: dwc: Clean up iATU index usage in dw_pcie_iatu_setup()
  PCI: dwc: Fix msg_atu_index assignment
  PCI: dwc: ep: Add comment explaining controller level PTM access in multi PF setup
  PCI: dwc: ep: Add per-PF BAR and inbound ATU mapping support
  PCI: dwc: ep: Fix resizable BAR support for multi-PF configurations
  PCI: endpoint: pci-epf-test: Allow overriding default BAR sizes
  selftests: pci_endpoint: Add BAR subrange mapping test case
  misc: pci_endpoint_test: Add BAR subrange mapping test case
  PCI: endpoint: pci-epf-test: Add BAR subrange mapping test support
  Documentation: PCI: endpoint: Clarify pci_epc_set_bar() usage
  PCI: dwc: ep: Support BAR subrange inbound mapping via Address Match Mode iATU
  PCI: dwc: Advertise dynamic inbound mapping support
  PCI: endpoint: Add BAR subrange mapping support
  PCI: endpoint: Add dynamic_inbound_mapping EPC feature
  PCI: dwc: Rename dw_pcie_rp::has_msi_ctrl to dw_pcie_rp::use_imsi_rx for clarity
  PCI: dwc: Fix grammar and formatting for comment in dw_pcie_remove_ext_capability()
  PCI: dwc: Use multiple iATU windows for mapping large bridge windows and DMA ranges
  PCI: dwc: Remove duplicate dw_pcie_ep_hide_ext_capability() function
  PCI: dwc: Skip waiting for L2/L3 Ready if dw_pcie_rp::skip_l23_wait is true
  PCI: dwc: Fail dw_pcie_host_init() if dw_pcie_wait_for_link() returns -ETIMEDOUT
  PCI: dwc: Rework the error print of dw_pcie_wait_for_link()
  PCI: dwc: Rename and move ltssm_status_string() to pcie-designware.c
  PCI: dwc: Return -EIO from dw_pcie_wait_for_link() if device is not active
  PCI: dwc: Return -ENODEV from dw_pcie_wait_for_link() if device is not found
  PCI: dwc: Use cfg0_base as iMSI-RX target address to support 32-bit MSI devices
  PCI: dwc: ep: Cache MSI outbound iATU mapping
  Revert "PCI: dwc: Don't wait for link up if driver can detect Link Up event"
  Revert "PCI: qcom: Enumerate endpoints based on Link up event in 'global_irq' interrupt"
  Revert "PCI: qcom: Enable MSI interrupts together with Link up if 'Global IRQ' is supported"
  Revert "PCI: qcom: Don't wait for link if we can detect Link Up"
  Revert "PCI: dw-rockchip: Enumerate endpoints based on dll_link_up IRQ"
  Revert "PCI: dw-rockchip: Don't wait for link since we can detect Link Up"
  PCI: dwc: Skip PME_Turn_Off broadcast and L2/L3 transition during suspend if link is not up
  PCI: dw-rockchip: Change get_ltssm() to provide L1 Substates info
  PCI: dwc: Add L1 Substates context to ltssm_status of debugfs
  PCI: qcom: Remove DPC Extended Capability
  PCI: qcom: Remove MSI-X Capability for Root Ports
  PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller
  PCI: dwc: Add new APIs to remove standard and extended Capability
  PCI: Add preceding capability position support in PCI_FIND_NEXT_*_CAP macros
parents cb3ca564 43d324ee
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -95,6 +95,30 @@ by the PCI endpoint function driver.
   Register space of the function driver is usually configured
   using this API.

   Some endpoint controllers also support calling pci_epc_set_bar() again
   for the same BAR (without calling pci_epc_clear_bar()) to update inbound
   address translations after the host has programmed the BAR base address.
   Endpoint function drivers can check this capability via the
   dynamic_inbound_mapping EPC feature bit.

   When pci_epf_bar.num_submap is non-zero, the endpoint function driver is
   requesting BAR subrange mapping using pci_epf_bar.submap. This requires
   the EPC to advertise support via the subrange_mapping EPC feature bit.

   When an EPF driver wants to make use of the inbound subrange mapping
   feature, it requires that the BAR base address has been programmed by
   the host during enumeration. Thus, it needs to call pci_epc_set_bar()
   twice for the same BAR (requires dynamic_inbound_mapping): first with
   num_submap set to zero and configuring the BAR size, then after the PCIe
   link is up and the host enumerates the endpoint and programs the BAR
   base address, again with num_submap set to non-zero value.

   Note that when making use of the inbound subrange mapping feature, the
   EPF driver must not call pci_epc_clear_bar() between the two
   pci_epc_set_bar() calls, because clearing the BAR can clear/disable the
   BAR register or BAR decode on the endpoint while the host still expects
   the assigned BAR address to remain valid.

* pci_epc_clear_bar()

   The PCI endpoint function driver should use pci_epc_clear_bar() to reset
+19 −0
Original line number Diff line number Diff line
@@ -84,6 +84,25 @@ device, the following commands can be used::
	# echo 32 > functions/pci_epf_test/func1/msi_interrupts
	# echo 2048 > functions/pci_epf_test/func1/msix_interrupts

By default, pci-epf-test uses the following BAR sizes::

	# grep . functions/pci_epf_test/func1/pci_epf_test.0/bar?_size
	  functions/pci_epf_test/func1/pci_epf_test.0/bar0_size:131072
	  functions/pci_epf_test/func1/pci_epf_test.0/bar1_size:131072
	  functions/pci_epf_test/func1/pci_epf_test.0/bar2_size:131072
	  functions/pci_epf_test/func1/pci_epf_test.0/bar3_size:131072
	  functions/pci_epf_test/func1/pci_epf_test.0/bar4_size:131072
	  functions/pci_epf_test/func1/pci_epf_test.0/bar5_size:1048576

The user can override a default value using e.g.::
	# echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar1_size

Overriding the default BAR sizes can only be done before binding the
pci-epf-test device to a PCI endpoint controller driver.

Note: Some endpoint controllers might have fixed-size BARs or reserved BARs;
for such controllers, the corresponding BAR size in configfs will be ignored.


Binding pci-epf-test Device to EP Controller
--------------------------------------------
+202 −1
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@
#define COMMAND_COPY				BIT(5)
#define COMMAND_ENABLE_DOORBELL			BIT(6)
#define COMMAND_DISABLE_DOORBELL		BIT(7)
#define COMMAND_BAR_SUBRANGE_SETUP		BIT(8)
#define COMMAND_BAR_SUBRANGE_CLEAR		BIT(9)

#define PCI_ENDPOINT_TEST_STATUS		0x8
#define STATUS_READ_SUCCESS			BIT(0)
@@ -55,6 +57,10 @@
#define STATUS_DOORBELL_ENABLE_FAIL		BIT(11)
#define STATUS_DOORBELL_DISABLE_SUCCESS		BIT(12)
#define STATUS_DOORBELL_DISABLE_FAIL		BIT(13)
#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS	BIT(14)
#define STATUS_BAR_SUBRANGE_SETUP_FAIL		BIT(15)
#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS	BIT(16)
#define STATUS_BAR_SUBRANGE_CLEAR_FAIL		BIT(17)

#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR	0x0c
#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR	0x10
@@ -77,6 +83,7 @@
#define CAP_MSI					BIT(1)
#define CAP_MSIX				BIT(2)
#define CAP_INTX				BIT(3)
#define CAP_SUBRANGE_MAPPING			BIT(4)

#define PCI_ENDPOINT_TEST_DB_BAR		0x34
#define PCI_ENDPOINT_TEST_DB_OFFSET		0x38
@@ -100,6 +107,8 @@

#define PCI_DEVICE_ID_ROCKCHIP_RK3588		0x3588

#define PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB	2

static DEFINE_IDA(pci_endpoint_test_ida);

#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
@@ -414,6 +423,193 @@ static int pci_endpoint_test_bars(struct pci_endpoint_test *test)
	return 0;
}

static u8 pci_endpoint_test_subrange_sig_byte(enum pci_barno barno,
					      unsigned int subno)
{
	return 0x50 + (barno * 8) + subno;
}

static u8 pci_endpoint_test_subrange_test_byte(enum pci_barno barno,
					       unsigned int subno)
{
	return 0xa0 + (barno * 8) + subno;
}

static int pci_endpoint_test_bar_subrange_cmd(struct pci_endpoint_test *test,
					      enum pci_barno barno, u32 command,
					      u32 ok_bit, u32 fail_bit)
{
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;
	int irq_type = test->irq_type;
	u32 status;

	if (irq_type < PCITEST_IRQ_TYPE_INTX ||
	    irq_type > PCITEST_IRQ_TYPE_MSIX) {
		dev_err(dev, "Invalid IRQ type\n");
		return -EINVAL;
	}

	reinit_completion(&test->irq_raised);

	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
	/* Reuse SIZE as a command parameter: bar number. */
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, barno);
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, command);

	if (!wait_for_completion_timeout(&test->irq_raised,
					 msecs_to_jiffies(1000)))
		return -ETIMEDOUT;

	status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
	if (status & fail_bit)
		return -EIO;

	if (!(status & ok_bit))
		return -EIO;

	return 0;
}

static int pci_endpoint_test_bar_subrange_setup(struct pci_endpoint_test *test,
						enum pci_barno barno)
{
	return pci_endpoint_test_bar_subrange_cmd(test, barno,
			COMMAND_BAR_SUBRANGE_SETUP,
			STATUS_BAR_SUBRANGE_SETUP_SUCCESS,
			STATUS_BAR_SUBRANGE_SETUP_FAIL);
}

static int pci_endpoint_test_bar_subrange_clear(struct pci_endpoint_test *test,
						enum pci_barno barno)
{
	return pci_endpoint_test_bar_subrange_cmd(test, barno,
			COMMAND_BAR_SUBRANGE_CLEAR,
			STATUS_BAR_SUBRANGE_CLEAR_SUCCESS,
			STATUS_BAR_SUBRANGE_CLEAR_FAIL);
}

static int pci_endpoint_test_bar_subrange(struct pci_endpoint_test *test,
					  enum pci_barno barno)
{
	u32 nsub = PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB;
	struct device *dev = &test->pdev->dev;
	size_t sub_size, buf_size;
	resource_size_t bar_size;
	void __iomem *bar_addr;
	void *read_buf = NULL;
	int ret, clear_ret;
	size_t off, chunk;
	u32 i, exp, val;
	u8 pattern;

	if (!(test->ep_caps & CAP_SUBRANGE_MAPPING))
		return -EOPNOTSUPP;

	/*
	 * The test register BAR is not safe to reprogram and write/read
	 * over its full size. BAR_TEST already special-cases it to a tiny
	 * range. For subrange mapping tests, let's simply skip it.
	 */
	if (barno == test->test_reg_bar)
		return -EBUSY;

	bar_size = pci_resource_len(test->pdev, barno);
	if (!bar_size)
		return -ENODATA;

	bar_addr = test->bar[barno];
	if (!bar_addr)
		return -ENOMEM;

	ret = pci_endpoint_test_bar_subrange_setup(test, barno);
	if (ret)
		return ret;

	if (bar_size % nsub || bar_size / nsub > SIZE_MAX) {
		ret = -EINVAL;
		goto out_clear;
	}

	sub_size = bar_size / nsub;
	if (sub_size < sizeof(u32)) {
		ret = -ENOSPC;
		goto out_clear;
	}

	/* Limit the temporary buffer size */
	buf_size = min_t(size_t, sub_size, SZ_1M);

	read_buf = kmalloc(buf_size, GFP_KERNEL);
	if (!read_buf) {
		ret = -ENOMEM;
		goto out_clear;
	}

	/*
	 * Step 1: verify EP-provided signature per subrange. This detects
	 * whether the EP actually applied the submap order.
	 */
	for (i = 0; i < nsub; i++) {
		exp = (u32)pci_endpoint_test_subrange_sig_byte(barno, i) *
			0x01010101U;
		val = ioread32(bar_addr + (i * sub_size));
		if (val != exp) {
			dev_err(dev,
				"BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n",
				barno, i, (size_t)i * sub_size, exp, val);
			ret = -EIO;
			goto out_clear;
		}
		val = ioread32(bar_addr + (i * sub_size) + sub_size - sizeof(u32));
		if (val != exp) {
			dev_err(dev,
				"BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n",
				barno, i,
				((size_t)i * sub_size) + sub_size - sizeof(u32),
				exp, val);
			ret = -EIO;
			goto out_clear;
		}
	}

	/* Step 2: write unique pattern per subrange (write all first). */
	for (i = 0; i < nsub; i++) {
		pattern = pci_endpoint_test_subrange_test_byte(barno, i);
		memset_io(bar_addr + (i * sub_size), pattern, sub_size);
	}

	/* Step 3: read back and verify (read all after all writes). */
	for (i = 0; i < nsub; i++) {
		pattern = pci_endpoint_test_subrange_test_byte(barno, i);
		for (off = 0; off < sub_size; off += chunk) {
			void *bad;

			chunk = min_t(size_t, buf_size, sub_size - off);
			memcpy_fromio(read_buf, bar_addr + (i * sub_size) + off,
				      chunk);
			bad = memchr_inv(read_buf, pattern, chunk);
			if (bad) {
				size_t bad_off = (u8 *)bad - (u8 *)read_buf;

				dev_err(dev,
					"BAR%d subrange%u data mismatch @%#zx (pattern %#02x)\n",
					barno, i, (size_t)i * sub_size + off + bad_off,
					pattern);
				ret = -EIO;
				goto out_clear;
			}
		}
	}

out_clear:
	kfree(read_buf);
	clear_ret = pci_endpoint_test_bar_subrange_clear(test, barno);
	return ret ?: clear_ret;
}

static int pci_endpoint_test_intx_irq(struct pci_endpoint_test *test)
{
	u32 val;
@@ -936,12 +1132,17 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,

	switch (cmd) {
	case PCITEST_BAR:
	case PCITEST_BAR_SUBRANGE:
		bar = arg;
		if (bar <= NO_BAR || bar > BAR_5)
			goto ret;
		if (is_am654_pci_dev(pdev) && bar == BAR_0)
			goto ret;

		if (cmd == PCITEST_BAR)
			ret = pci_endpoint_test_bar(test, bar);
		else
			ret = pci_endpoint_test_bar_subrange(test, bar);
		break;
	case PCITEST_BARS:
		ret = pci_endpoint_test_bars(test);
+2 −2
Original line number Diff line number Diff line
@@ -13,13 +13,13 @@
u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap)
{
	return PCI_FIND_NEXT_CAP(cdns_pcie_read_cfg, PCI_CAPABILITY_LIST,
				 cap, pcie);
				 cap, NULL, pcie);
}
EXPORT_SYMBOL_GPL(cdns_pcie_find_capability);

u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap)
{
	return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, pcie);
	return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, NULL, pcie);
}
EXPORT_SYMBOL_GPL(cdns_pcie_find_ext_capability);

+1 −0
Original line number Diff line number Diff line
@@ -424,6 +424,7 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}

static const struct pci_epc_features dra7xx_pcie_epc_features = {
	DWC_EPC_COMMON_FEATURES,
	.linkup_notifier = true,
	.msi_capable = true,
};
Loading