Commit 74855f66 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/endpoint'

- Destroy the EPC device in devm_pci_epc_destroy(), which previously didn't
  call devres_release() (Zijun Hu)

- Simplify pci_epc_get() with class_find_device_by_name() (Zijun Hu)

- Finish virtual EP removal in pci_epf_remove_vepf(), which previously
  caused a subsequent pci_epf_add_vepf() to fail with -EBUSY (Zijun Hu)

- Write BAR_MASK before iATU registers in pci_epc_set_bar() so we don't
  depend on the BAR_MASK reset value being larger than the requested BAR
  size (Niklas Cassel)

- Prevent changing BAR size/flags in pci_epc_set_bar() to prevent reads
  from bypassing the iATU if we reduced the BAR size (Niklas Cassel)

- Verify address alignment when programming iATU so we don't attempt to
  write bits that are read-only because of the BAR size, which could lead
  to directing accesses to the wrong address (Niklas Cassel)

- Implement artpec6 pci_epc_features so we can rely on all drivers
  supporting it so we can use it in EPC core code (Niklas Cassel)

- Check for BARs of fixed size to prevent endpoint drivers from trying to
  change their size (Niklas Cassel)

- Verify that requested BAR size is a power of two when endpoint driver
  sets the BAR (Niklas Cassel)

* pci/endpoint:
  PCI: endpoint: Verify that requested BAR size is a power of two
  PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar()
  PCI: artpec6: Implement dw_pcie_ep operation get_features
  PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu()
  PCI: dwc: ep: Prevent changing BAR size/flags in pci_epc_set_bar()
  PCI: dwc: ep: Write BAR_MASK before iATU registers in pci_epc_set_bar()
  PCI: endpoint: Finish virtual EP removal in pci_epf_remove_vepf()
  PCI: endpoint: Simplify pci_epc_get()
  PCI: endpoint: Destroy the EPC device in devm_pci_epc_destroy()
  PCI: endpoint: Replace magic number '6' by PCI_STD_NUM_BARS
parents 770b18a5 0e7faea1
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -369,9 +369,22 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
	return 0;
}

static const struct pci_epc_features artpec6_pcie_epc_features = {
	.linkup_notifier = false,
	.msi_capable = true,
	.msix_capable = false,
};

static const struct pci_epc_features *
artpec6_pcie_get_features(struct dw_pcie_ep *ep)
{
	return &artpec6_pcie_epc_features;
}

static const struct dw_pcie_ep_ops pcie_ep_ops = {
	.init = artpec6_pcie_ep_init,
	.raise_irq = artpec6_pcie_raise_irq,
	.get_features = artpec6_pcie_get_features,
};

static int artpec6_pcie_probe(struct platform_device *pdev)
+38 −14
Original line number Diff line number Diff line
@@ -128,7 +128,8 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}

static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
				  dma_addr_t cpu_addr, enum pci_barno bar)
				  dma_addr_t cpu_addr, enum pci_barno bar,
				  size_t size)
{
	int ret;
	u32 free_win;
@@ -145,7 +146,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
	}

	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
					  cpu_addr, bar);
					  cpu_addr, bar, size);
	if (ret < 0) {
		dev_err(pci->dev, "Failed to program IB window\n");
		return ret;
@@ -222,19 +223,30 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
	if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
		return -EINVAL;

	reg = PCI_BASE_ADDRESS_0 + (4 * bar);

	if (!(flags & PCI_BASE_ADDRESS_SPACE))
		type = PCIE_ATU_TYPE_MEM;
	else
		type = PCIE_ATU_TYPE_IO;
	/*
	 * Certain EPF drivers dynamically change the physical address of a BAR
	 * (i.e. they call set_bar() twice, without ever calling clear_bar(), as
	 * calling clear_bar() would clear the BAR's PCI address assigned by the
	 * host).
	 */
	if (ep->epf_bar[bar]) {
		/*
		 * We can only dynamically change a BAR if the new BAR size and
		 * BAR flags do not differ from the existing configuration.
		 */
		if (ep->epf_bar[bar]->barno != bar ||
		    ep->epf_bar[bar]->size != size ||
		    ep->epf_bar[bar]->flags != flags)
			return -EINVAL;

	ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
	if (ret)
		return ret;
		/*
		 * When dynamically changing a BAR, skip writing the BAR reg, as
		 * that would clear the BAR's PCI address assigned by the host.
		 */
		goto config_atu;
	}

	if (ep->epf_bar[bar])
		return 0;
	reg = PCI_BASE_ADDRESS_0 + (4 * bar);

	dw_pcie_dbi_ro_wr_en(pci);

@@ -246,9 +258,21 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
		dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
	}

	ep->epf_bar[bar] = epf_bar;
	dw_pcie_dbi_ro_wr_dis(pci);

config_atu:
	if (!(flags & PCI_BASE_ADDRESS_SPACE))
		type = PCIE_ATU_TYPE_MEM;
	else
		type = PCIE_ATU_TYPE_IO;

	ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar,
				     size);
	if (ret)
		return ret;

	ep->epf_bar[bar] = epf_bar;

	return 0;
}

+3 −2
Original line number Diff line number Diff line
@@ -597,11 +597,12 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
}

int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
				int type, u64 cpu_addr, u8 bar)
				int type, u64 cpu_addr, u8 bar, size_t size)
{
	u32 retries, val;

	if (!IS_ALIGNED(cpu_addr, pci->region_align))
	if (!IS_ALIGNED(cpu_addr, pci->region_align) ||
	    !IS_ALIGNED(cpu_addr, size))
		return -EINVAL;

	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
+1 −1
Original line number Diff line number Diff line
@@ -491,7 +491,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
			     u64 cpu_addr, u64 pci_addr, u64 size);
int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
				int type, u64 cpu_addr, u8 bar);
				int type, u64 cpu_addr, u8 bar, size_t size);
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
+19 −18
Original line number Diff line number Diff line
@@ -60,26 +60,17 @@ struct pci_epc *pci_epc_get(const char *epc_name)
	int ret = -EINVAL;
	struct pci_epc *epc;
	struct device *dev;
	struct class_dev_iter iter;

	class_dev_iter_init(&iter, &pci_epc_class, NULL, NULL);
	while ((dev = class_dev_iter_next(&iter))) {
		if (strcmp(epc_name, dev_name(dev)))
			continue;

		epc = to_pci_epc(dev);
		if (!try_module_get(epc->ops->owner)) {
			ret = -EINVAL;
	dev = class_find_device_by_name(&pci_epc_class, epc_name);
	if (!dev)
		goto err;
		}

		class_dev_iter_exit(&iter);
		get_device(&epc->dev);
	epc = to_pci_epc(dev);
	if (try_module_get(epc->ops->owner))
		return epc;
	}

err:
	class_dev_iter_exit(&iter);
	put_device(dev);
	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(pci_epc_get);
@@ -609,10 +600,20 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
		    struct pci_epf_bar *epf_bar)
{
	int ret;
	const struct pci_epc_features *epc_features;
	enum pci_barno bar = epf_bar->barno;
	int flags = epf_bar->flags;
	int ret;

	if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
	epc_features = pci_epc_get_features(epc, func_no, vfunc_no);
	if (!epc_features)
		return -EINVAL;

	if (epc_features->bar[bar].type == BAR_FIXED &&
	    (epc_features->bar[bar].fixed_size != epf_bar->size))
		return -EINVAL;

	if (!is_power_of_2(epf_bar->size))
		return -EINVAL;

	if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
@@ -942,7 +943,7 @@ void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc)
{
	int r;

	r = devres_destroy(dev, devm_pci_epc_release, devm_pci_epc_match,
	r = devres_release(dev, devm_pci_epc_release, devm_pci_epc_match,
			   epc);
	dev_WARN_ONCE(dev, r, "couldn't find PCI EPC resource\n");
}
Loading