Commit 29b10c60 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/dwc'

- Always set DesignWare "TLP Digest" bit so generic code can enable ECRC
  via the AER Capability (Vidya Sagar)

- Drop support for config space in DT 'ranges' (Rob Herring)

- Increase width of outbound iATU size to u64 (Shradha Todi)

- Add upper limit address for outbound iATU (Shradha Todi)

- Allow dwc-based drivers that don't override any default ops (Jisheng
  Zhang)

- Drop unnecessary dw_pcie_ops from the al driver (Jisheng Zhang)

* pci/dwc:
  PCI: al: Remove useless dw_pcie_ops
  PCI: dwc: Don't assume the ops in dw_pcie always exist
  PCI: dwc: Add upper limit address for outbound iATU
  PCI: dwc: Change size to u64 for EP outbound iATU
  PCI: dwc: Drop support for config space in 'ranges'
  PCI: dwc: Work around ECRC configuration issue
parents 59189d06 2a34b86f
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -314,9 +314,6 @@ static const struct dw_pcie_host_ops al_pcie_host_ops = {
	.host_init = al_pcie_host_init,
};

static const struct dw_pcie_ops dw_pcie_ops = {
};

static int al_pcie_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -334,7 +331,6 @@ static int al_pcie_probe(struct platform_device *pdev)
		return -ENOMEM;

	pci->dev = dev;
	pci->ops = &dw_pcie_ops;
	pci->pp.ops = &al_pcie_host_ops;

	al_pcie->pci = pci;
+3 −5
Original line number Diff line number Diff line
@@ -434,9 +434,7 @@ static void dw_pcie_ep_stop(struct pci_epc *epc)
	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);

	if (!pci->ops->stop_link)
		return;

	if (pci->ops && pci->ops->stop_link)
		pci->ops->stop_link(pci);
}

@@ -445,7 +443,7 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);

	if (!pci->ops->start_link)
	if (!pci->ops || !pci->ops->start_link)
		return -EINVAL;

	return pci->ops->start_link(pci);
+13 −34
Original line number Diff line number Diff line
@@ -305,8 +305,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
	if (cfg_res) {
		pp->cfg0_size = resource_size(cfg_res);
		pp->cfg0_base = cfg_res->start;
	} else if (!pp->va_cfg0_base) {

		pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, cfg_res);
		if (IS_ERR(pp->va_cfg0_base))
			return PTR_ERR(pp->va_cfg0_base);
	} else {
		dev_err(dev, "Missing *config* reg space\n");
		return -ENODEV;
	}

	if (!pci->dbi_base) {
@@ -322,38 +327,12 @@ int dw_pcie_host_init(struct pcie_port *pp)

	pp->bridge = bridge;

	/* Get the I/O and memory ranges from DT */
	resource_list_for_each_entry(win, &bridge->windows) {
		switch (resource_type(win->res)) {
		case IORESOURCE_IO:
	/* Get the I/O range from DT */
	win = resource_list_first_type(&bridge->windows, IORESOURCE_IO);
	if (win) {
		pp->io_size = resource_size(win->res);
		pp->io_bus_addr = win->res->start - win->offset;
		pp->io_base = pci_pio_to_address(win->res->start);
			break;
		case 0:
			dev_err(dev, "Missing *config* reg space\n");
			pp->cfg0_size = resource_size(win->res);
			pp->cfg0_base = win->res->start;
			if (!pci->dbi_base) {
				pci->dbi_base = devm_pci_remap_cfgspace(dev,
								pp->cfg0_base,
								pp->cfg0_size);
				if (!pci->dbi_base) {
					dev_err(dev, "Error with ioremap\n");
					return -ENOMEM;
				}
			}
			break;
		}
	}

	if (!pp->va_cfg0_base) {
		pp->va_cfg0_base = devm_pci_remap_cfgspace(dev,
					pp->cfg0_base, pp->cfg0_size);
		if (!pp->va_cfg0_base) {
			dev_err(dev, "Error with ioremap in function\n");
			return -ENOMEM;
		}
	}

	if (pci->link_gen < 1)
@@ -425,7 +404,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
	dw_pcie_setup_rc(pp);
	dw_pcie_msi_init(pp);

	if (!dw_pcie_link_up(pci) && pci->ops->start_link) {
	if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
		ret = pci->ops->start_link(pci);
		if (ret)
			goto err_free_msi;
+60 −10
Original line number Diff line number Diff line
@@ -141,7 +141,7 @@ u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size)
	int ret;
	u32 val;

	if (pci->ops->read_dbi)
	if (pci->ops && pci->ops->read_dbi)
		return pci->ops->read_dbi(pci, pci->dbi_base, reg, size);

	ret = dw_pcie_read(pci->dbi_base + reg, size, &val);
@@ -156,7 +156,7 @@ void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
{
	int ret;

	if (pci->ops->write_dbi) {
	if (pci->ops && pci->ops->write_dbi) {
		pci->ops->write_dbi(pci, pci->dbi_base, reg, size, val);
		return;
	}
@@ -171,7 +171,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
{
	int ret;

	if (pci->ops->write_dbi2) {
	if (pci->ops && pci->ops->write_dbi2) {
		pci->ops->write_dbi2(pci, pci->dbi_base2, reg, size, val);
		return;
	}
@@ -186,7 +186,7 @@ static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg)
	int ret;
	u32 val;

	if (pci->ops->read_dbi)
	if (pci->ops && pci->ops->read_dbi)
		return pci->ops->read_dbi(pci, pci->atu_base, reg, 4);

	ret = dw_pcie_read(pci->atu_base + reg, 4, &val);
@@ -200,7 +200,7 @@ static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val)
{
	int ret;

	if (pci->ops->write_dbi) {
	if (pci->ops && pci->ops->write_dbi) {
		pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val);
		return;
	}
@@ -225,6 +225,47 @@ static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
	dw_pcie_writel_atu(pci, offset + reg, val);
}

static inline u32 dw_pcie_enable_ecrc(u32 val)
{
	/*
	 * DesignWare core version 4.90A has a design issue where the 'TD'
	 * bit in the Control register-1 of the ATU outbound region acts
	 * like an override for the ECRC setting, i.e., the presence of TLP
	 * Digest (ECRC) in the outgoing TLPs is solely determined by this
	 * bit. This is contrary to the PCIe spec which says that the
	 * enablement of the ECRC is solely determined by the AER
	 * registers.
	 *
	 * Because of this, even when the ECRC is enabled through AER
	 * registers, the transactions going through ATU won't have TLP
	 * Digest as there is no way the PCI core AER code could program
	 * the TD bit which is specific to the DesignWare core.
	 *
	 * The best way to handle this scenario is to program the TD bit
	 * always. It affects only the traffic from root port to downstream
	 * devices.
	 *
	 * At this point,
	 * When ECRC is enabled in AER registers, everything works normally
	 * When ECRC is NOT enabled in AER registers, then,
	 * on Root Port:- TLP Digest (DWord size) gets appended to each packet
	 *                even through it is not required. Since downstream
	 *                TLPs are mostly for configuration accesses and BAR
	 *                accesses, they are not in critical path and won't
	 *                have much negative effect on the performance.
	 * on End Point:- TLP Digest is received for some/all the packets coming
	 *                from the root port. TLP Digest is ignored because,
	 *                as per the PCIe Spec r5.0 v1.0 section 2.2.3
	 *                "TLP Digest Rules", when an endpoint receives TLP
	 *                Digest when its ECRC check functionality is disabled
	 *                in AER registers, received TLP Digest is just ignored.
	 * Since there is no issue or error reported either side, best way to
	 * handle the scenario is to program TD bit by default.
	 */

	return val | PCIE_ATU_TD;
}

static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
					     int index, int type,
					     u64 cpu_addr, u64 pci_addr,
@@ -248,6 +289,8 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
	val = type | PCIE_ATU_FUNC_NUM(func_no);
	val = upper_32_bits(size - 1) ?
		val | PCIE_ATU_INCREASE_REGION_SIZE : val;
	if (pci->version == 0x490A)
		val = dw_pcie_enable_ecrc(val);
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
				 PCIE_ATU_ENABLE);
@@ -273,7 +316,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
{
	u32 retries, val;

	if (pci->ops->cpu_addr_fixup)
	if (pci->ops && pci->ops->cpu_addr_fixup)
		cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);

	if (pci->iatu_unroll_enabled) {
@@ -290,12 +333,19 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
			   upper_32_bits(cpu_addr));
	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
			   lower_32_bits(cpu_addr + size - 1));
	if (pci->version >= 0x460A)
		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
				   upper_32_bits(cpu_addr + size - 1));
	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
			   lower_32_bits(pci_addr));
	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
			   upper_32_bits(pci_addr));
	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
			   PCIE_ATU_FUNC_NUM(func_no));
	val = type | PCIE_ATU_FUNC_NUM(func_no);
	val = ((upper_32_bits(size - 1)) && (pci->version >= 0x460A)) ?
		val | PCIE_ATU_INCREASE_REGION_SIZE : val;
	if (pci->version == 0x490A)
		val = dw_pcie_enable_ecrc(val);
	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);

	/*
@@ -321,7 +371,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,

void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
				  int type, u64 cpu_addr, u64 pci_addr,
				  u32 size)
				  u64 size)
{
	__dw_pcie_prog_outbound_atu(pci, func_no, index, type,
				    cpu_addr, pci_addr, size);
@@ -481,7 +531,7 @@ int dw_pcie_link_up(struct dw_pcie *pci)
{
	u32 val;

	if (pci->ops->link_up)
	if (pci->ops && pci->ops->link_up)
		return pci->ops->link_up(pci);

	val = readl(pci->dbi_base + PCIE_PORT_DEBUG1);
+3 −1
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@
#define PCIE_ATU_TYPE_IO		0x2
#define PCIE_ATU_TYPE_CFG0		0x4
#define PCIE_ATU_TYPE_CFG1		0x5
#define PCIE_ATU_TD			BIT(8)
#define PCIE_ATU_FUNC_NUM(pf)           ((pf) << 20)
#define PCIE_ATU_CR2			0x908
#define PCIE_ATU_ENABLE			BIT(31)
@@ -99,6 +100,7 @@
#define PCIE_ATU_DEV(x)			FIELD_PREP(GENMASK(23, 19), x)
#define PCIE_ATU_FUNC(x)		FIELD_PREP(GENMASK(18, 16), x)
#define PCIE_ATU_UPPER_TARGET		0x91C
#define PCIE_ATU_UPPER_LIMIT		0x924

#define PCIE_MISC_CONTROL_1_OFF		0x8BC
#define PCIE_DBI_RO_WR_EN		BIT(0)
@@ -297,7 +299,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
			       u64 size);
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
				  int type, u64 cpu_addr, u64 pci_addr,
				  u32 size);
				  u64 size);
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
			     int bar, u64 cpu_addr,
			     enum dw_pcie_as_type as_type);