[linux-yocto] [PATCH 10/48] drivers/pci: Work Around an Axxia Hardware Problem

Daniel Dragomir daniel.dragomir at windriver.com
Mon Dec 11 05:13:40 PST 2017


From: John Jacques <john.jacques at intel.com>

If the link does not come up, reduce the width and
try again.

Signed-off-by: John Jacques <john.jacques at intel.com>
---
 drivers/misc/axxia-pei.c      | 348 ++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/host/pcie-axxia.c | 159 +++++++++++--------
 drivers/pci/host/pcie-axxia.h |   1 +
 include/linux/axxia-pei.h     |   1 +
 4 files changed, 448 insertions(+), 61 deletions(-)

diff --git a/drivers/misc/axxia-pei.c b/drivers/misc/axxia-pei.c
index daf0381..bd0565f 100644
--- a/drivers/misc/axxia-pei.c
+++ b/drivers/misc/axxia-pei.c
@@ -148,6 +148,17 @@ static int pei_trace;
 module_param(pei_trace, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 MODULE_PARM_DESC(pei_trace, "enable pei_trace");
 
+/*
+ * ------------------------------------------------------------------------------
+ * get_config
+ */
+
+static inline unsigned
+get_config(unsigned int control)
+{
+	return (0xf & (control >> 22));
+}
+
 static u32
 pei_readl(void *address)
 {
@@ -1772,6 +1783,343 @@ int axxia_pei_is_control_set(void)
 }
 EXPORT_SYMBOL(axxia_pei_is_control_set);
 
+/*
+ * ------------------------------------------------------------------------------
+ * pei_reset_56xx
+ */
+
+static int
+pei_reset_56xx(enum PCIMode mode, unsigned int control)
+{
+	unsigned int ctrl0;
+
+	ncr_read32(NCP_REGION_ID(0x115, 0), 0, &ctrl0);
+
+	switch (get_config(control)) {
+	case 1:
+		/*
+		 * PEI0x4  (HSS10-ch0,1; HSS11-ch0,1)
+		 * PEI1x4  (HSS12-ch0,1; HSS13-ch0,1)
+		 */
+
+		switch (mode) {
+		case PEI0:
+			enable_reset(0);
+			enable_reset(1);
+			ctrl0 &= ~(1 << 0);
+			break;
+		case PEI1:
+			enable_reset(2);
+			enable_reset(3);
+			ctrl0 &= ~(1 << 1);
+			break;
+		default:
+			pr_err("Invalid PEI for mode %d!\n",
+			       get_config(control));
+			return -1;
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+
+		switch (mode) {
+		case PEI0:
+			release_reset(0);
+			release_reset(1);
+			ctrl0 |= (1 << 0);
+			break;
+		case PEI1:
+			release_reset(2);
+			release_reset(3);
+			ctrl0 |= (1 << 1);
+			break;
+		default:
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+	case 2:
+		/*
+		 * PEI0x2  (HSS10-ch0,1)
+		 * PEI2x2  (HSS11-ch0,1)
+		 * PEI1x2  (HSS12-ch0,1)
+		 * UNUSED  (HSS13-ch0,1)
+		 */
+
+		/* Disable srio0 and srio1. */
+		ctrl0 &= ~((1 << 10) | (1 << 3));
+
+		switch (mode) {
+		case PEI0:
+			enable_reset(0);
+			ctrl0 &= ~(1 << 0);
+			break;
+		case PEI1:
+			enable_reset(2);
+			ctrl0 &= ~(1 << 1);
+			break;
+		case PEI2:
+			enable_reset(1);
+			ctrl0 &= ~(1 << 2);
+			break;
+		default:
+			pr_err("Invalid PEI for mode %d!\n",
+			       get_config(control));
+			return -1;
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+
+		switch (mode) {
+		case PEI0:
+			release_reset(0);
+			ctrl0 |= (1 << 0);
+			break;
+		case PEI1:
+			release_reset(2);
+			ctrl0 |= (1 << 1);
+			break;
+		case PEI2:
+			release_reset(1);
+			ctrl0 |= (1 << 2);
+			break;
+		default:
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+	case 3:
+		/*
+		 * PEI0x2  (HSS10-ch0,1)
+		 * SRIO0x2 (HSS11-ch0,1)
+		 * UNUSED  (HSS12-ch0,1)
+		 * PEI2x2  (HSS13-ch0,1)
+		 * INFO: Formerly case 6...
+		 */
+
+		switch (mode) {
+		case PEI0:
+			enable_reset(0);
+			ctrl0 &= ~(1 << 0);
+			break;
+		case PEI2:
+			enable_reset(3);
+			ctrl0 &= ~(1 << 2);
+			break;
+		default:
+			pr_err("Invalid PEI for mode %d!\n",
+			       get_config(control));
+			return -1;
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+
+		switch (mode) {
+		case PEI0:
+			release_reset(0);
+			ctrl0 |= (1 << 0);
+			break;
+		case PEI2:
+			release_reset(3);
+			ctrl0 |= (1 << 2);
+			break;
+		default:
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+	case 4:
+		/*
+		 * SRIO1x2 (HSS10-ch0,1)
+		 * SRIO0x2 (HSS11-ch0,1)
+		 * UNUSED  (HSS12-ch0,1)
+		 * PEI2x2  (HSS13-ch0,1)
+		 */
+
+		switch (mode) {
+		case PEI2:
+			enable_reset(3);
+			ctrl0 &= ~(1 << 2);
+			break;
+		default:
+			pr_err("Invalid PEI for mode %d!\n",
+			       get_config(control));
+			return -1;
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+
+		switch (mode) {
+		case PEI2:
+			release_reset(3);
+			ctrl0 |= (1 << 2);
+			break;
+		default:
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+
+		/* Undocumented Configurations */
+
+	case 15:
+		/*
+		 * UNUSED  (HSS10-ch0,1)
+		 * SRIO0x2 (HSS11-ch0,1)
+		 * PEI1x4  (HSS12-ch0,1; HSS13-ch0,1)
+		 */
+
+		switch (mode) {
+		case PEI1:
+			enable_reset(2);
+			enable_reset(3);
+			ctrl0 &= ~(1 << 1);
+			break;
+		default:
+			pr_err("Invalid PEI for mode %d!\n",
+			       get_config(control));
+			return -1;
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+
+		switch (mode) {
+		case PEI1:
+			release_reset(2);
+			release_reset(3);
+			ctrl0 |= (1 << 1);
+			break;
+		default:
+			break;
+		}
+
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+	default:
+		pr_err("Invalid Configuration: 0x%x\n", get_config(control));
+		return -1;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * pei_reset_67xx
+ */
+
+static int
+pei_reset_67xx(unsigned int control)
+{
+	unsigned int ctrl0;
+
+	ncr_read32(NCP_REGION_ID(0x115, 0), 0, &ctrl0);
+
+	switch (get_config(control)) {
+	case 1:
+		/*
+		 * PEI0x2  (HSS15-ch0,1)
+		 */
+
+		enable_reset(0);
+		ctrl0 &= ~(1 << 0);
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		release_reset(0);
+		ctrl0 |= (1 << 0);
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+	case 2:
+		/*
+		 * PEI0x1  (HSS15-ch0)
+		 */
+
+		enable_reset(0);
+		ctrl0 &= ~(1 << 0);
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		release_reset(0);
+		ctrl0 |= (1 << 0);
+		ncr_write32(NCP_REGION_ID(0x115, 0), 0, ctrl0);
+		break;
+	default:
+		pr_err("Invalid Configuration: 0x%x\n", get_config(control));
+		return -1;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * axxia_pei_reset
+ *
+ * Assumes that pei_init() has been run.
+ *
+ * Only resets PCI ports.
+ */
+
+int
+axxia_pei_reset(unsigned int pei)
+{
+	unsigned int control;
+	enum PCIMode mode;
+	unsigned int ctrl0;
+
+	if (0 == axxia_pei_is_control_set()) {
+		pr_err("Control is Not Set\n");
+		return -1;
+	}
+
+	control = axxia_pei_get_control();
+
+	if (is_5500) {
+		pr_err("Invalid Target\n");
+		return -1;
+	}
+
+	if (is_6700 && 0 != pei) {
+		pr_err("Invalid PEI: %d\n", pei);
+		return -1;
+	}
+
+	switch (pei) {
+	case 0:
+		mode = PEI0;
+		break;
+	case 1:
+		mode = PEI1;
+		break;
+	case 2:
+		mode = PEI2;
+		break;
+	default:
+		pr_err("Invalid PEI: %d\n", pei);
+		return -1;
+		break;
+	}
+
+	disable_ltssm(mode);
+	ncr_read32(NCP_REGION_ID(0x115, 0), 0, &ctrl0);
+
+	if (is_5600)
+		return pei_reset_56xx(mode, control);
+	else if (is_6700)
+		return pei_reset_67xx(control);
+
+	pr_err("Invalid Target\n");
+
+	return -1;
+}
+EXPORT_SYMBOL(axxia_pei_reset);
 
 /*
   ------------------------------------------------------------------------------
diff --git a/drivers/pci/host/pcie-axxia.c b/drivers/pci/host/pcie-axxia.c
index 7f8f355..10e4769 100644
--- a/drivers/pci/host/pcie-axxia.c
+++ b/drivers/pci/host/pcie-axxia.c
@@ -587,79 +587,104 @@ void axxia_pcie_setup_rc(struct pcie_port *pp)
 	u32 membase;
 	u32 memlimit;
 
-	/*
-	 * To work around a hardware problem, set
-	 * PCIE_LINK_WIDTH_SPEED_CONTROL to 1 lane in all cases.
-	 */
+	if (1 > pp->lanes)
+		return;
+
+	if (-1 == pp->pei_nr)
+		return;
+
+	for (;;) {
+		/*
+		 * To work around a hardware problem, set
+		 * PCIE_LINK_WIDTH_SPEED_CONTROL to 1 lane in all cases.
+		 */
+
+		axxia_pcie_rd_own_conf(pp,
+				       PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
+		val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+		val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+		axxia_pcie_wr_own_conf(pp,
+				       PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
 
-	axxia_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
-	val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
-	val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
-	axxia_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
-
-	/* Set the number of lanes based on the device tree. */
-	axxia_pcie_rd_own_conf(pp, PCIE_PORT_LINK_CONTROL, 4, &val);
-	val &= ~PORT_LINK_MODE_MASK;
-
-	switch (pp->lanes) {
-	case 1:
-		val |= PORT_LINK_MODE_1_LANES;
-		break;
-	case 2:
-		val |= PORT_LINK_MODE_2_LANES;
-		break;
-	case 4:
-		val |= PORT_LINK_MODE_4_LANES;
-		break;
-	case 8:
-		val |= PORT_LINK_MODE_8_LANES;
-		break;
-	default:
-		break;
+		/* Set the number of lanes based on the device tree. */
+		axxia_pcie_rd_own_conf(pp, PCIE_PORT_LINK_CONTROL, 4, &val);
+		val &= ~PORT_LINK_MODE_MASK;
+
+		switch (pp->lanes) {
+		case 1:
+			val |= PORT_LINK_MODE_1_LANES;
+			break;
+		case 2:
+			val |= PORT_LINK_MODE_2_LANES;
+			break;
+		case 4:
+			val |= PORT_LINK_MODE_4_LANES;
+			break;
+		case 8:
+			val |= PORT_LINK_MODE_8_LANES;
+			break;
+		default:
+			break;
+		}
+		axxia_pcie_wr_own_conf(pp, PCIE_PORT_LINK_CONTROL, 4, val);
+
+		/* Add Mikes tweak for GEN3_EQ_CONTROL */
+		axxia_pcie_writel_rc(pp, 0x1017201, PCIE_GEN3_EQ_CONTROL_OFF);
+
+		/* setup bus numbers */
+		axxia_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
+		val &= 0xff000000;
+		val |= 0x00010100;
+		axxia_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
+
+		/* setup memory base, memory limit */
+		membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
+		memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000;
+		val = memlimit | membase;
+		axxia_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
+
+		/* setup command register */
+		axxia_pcie_readl_rc(pp, PCI_COMMAND, &val);
+		val &= 0xffff0000;
+		val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+			PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+		axxia_pcie_writel_rc(pp, val, PCI_COMMAND);
+
+		/* LTSSM enable */
+		axxia_cc_gpreg_readl(pp, PEI_GENERAL_CORE_CTL_REG, &val);
+		msleep(100);
+		val |= 0x1;
+		axxia_cc_gpreg_writel(pp, val, PEI_GENERAL_CORE_CTL_REG);
+		msleep(100);
+
+		if (axxia_pcie_link_up(pp))
+			break;
+
+		if (1 >= pp->lanes)
+			break;
+
+		pp->lanes >>= 1;
+		axxia_pei_reset(pp->pei_nr);
+		mdelay(100);
 	}
-	axxia_pcie_wr_own_conf(pp, PCIE_PORT_LINK_CONTROL, 4, val);
-
-	/* Add Mikes tweak for GEN3_EQ_CONTROL */
-	axxia_pcie_writel_rc(pp, 0x1017201, PCIE_GEN3_EQ_CONTROL_OFF);
-
-	/* setup bus numbers */
-	axxia_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
-	val &= 0xff000000;
-	val |= 0x00010100;
-	axxia_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
-
-	/* setup memory base, memory limit */
-	membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
-	memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000;
-	val = memlimit | membase;
-	axxia_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
-
-	/* setup command register */
-	axxia_pcie_readl_rc(pp, PCI_COMMAND, &val);
-	val &= 0xffff0000;
-	val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-		PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-	axxia_pcie_writel_rc(pp, val, PCI_COMMAND);
-
-	/* LTSSM enable */
-	axxia_cc_gpreg_readl(pp, PEI_GENERAL_CORE_CTL_REG, &val);
-	msleep(100);
-	val |= 0x1;
-	axxia_cc_gpreg_writel(pp, val, PEI_GENERAL_CORE_CTL_REG);
-	msleep(100);
 }
 
 
 static int axxia_pcie_establish_link(struct pcie_port *pp)
 {
+	u32 value;
+
 	/* setup root complex */
 	axxia_pcie_setup_rc(pp);
 
-	if (axxia_pcie_link_up(pp))
-		dev_info(pp->dev, "Link up\n");
-	else
+	if (!axxia_pcie_link_up(pp))
 		return 1;
 
+	axxia_pcie_readl_rc(pp, 0x80, &value);
+	dev_info(pp->dev, "PEI%d Link Up: Gen%d x%d\n", pp->pei_nr,
+		 (((value & 0xf0000) >> 16) & 0xff),
+		 (((value & 0x3f00000) >> 20) & 0xff));
+
 	return 0;
 }
 
@@ -1030,6 +1055,18 @@ static int axxia_pcie_probe(struct platform_device *pdev)
 	pp->dev = &pdev->dev;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+
+	if (0xa002000000 == res->start) {
+		pp->pei_nr = 0;
+	} else if (0xa004000000 == res->start) {
+		pp->pei_nr = 1;
+	} else if (0xa006000000 == res->start) {
+		pp->pei_nr = 2;
+	} else {
+		pr_warn("Unknown PEI!\n");
+		pp->pei_nr = -1;
+	}
+
 	pp->dbi_base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(pp->dbi_base))
 		return PTR_ERR(pp->dbi_base);
diff --git a/drivers/pci/host/pcie-axxia.h b/drivers/pci/host/pcie-axxia.h
index 5a965d7..4ea4105 100644
--- a/drivers/pci/host/pcie-axxia.h
+++ b/drivers/pci/host/pcie-axxia.h
@@ -26,6 +26,7 @@
 struct pcie_port {
 	struct device		*dev;
 	u8			root_bus_nr;
+	s8                      pei_nr;
 	void __iomem		*dbi_base;
 	void __iomem		*cc_gpreg_base;
 	void __iomem		*axi_gpreg_base;
diff --git a/include/linux/axxia-pei.h b/include/linux/axxia-pei.h
index 9744945..e1f40a2 100644
--- a/include/linux/axxia-pei.h
+++ b/include/linux/axxia-pei.h
@@ -18,6 +18,7 @@
 #ifndef __AXXIA_PEI_H
 #define __AXXIA_PEI_H
 
+int axxia_pei_reset(unsigned int);
 int axxia_pei_setup(unsigned int, unsigned int);
 int axxia_pcie_reset(void);
 
-- 
2.7.4



More information about the linux-yocto mailing list