[linux-yocto] [PATCH 03/15] drivers/pci: Work Around an Axxia Hardware Problem
Daniel Dragomir
daniel.dragomir at windriver.com
Wed Aug 2 05:46:38 PDT 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 | 340 ++++++++++++++++++++++++++++++++++++++++++
drivers/pci/host/pcie-axxia.c | 161 ++++++++++++--------
drivers/pci/host/pcie-axxia.h | 1 +
include/linux/axxia-pei.h | 1 +
4 files changed, 441 insertions(+), 62 deletions(-)
diff --git a/drivers/misc/axxia-pei.c b/drivers/misc/axxia-pei.c
index 2c0fabd..e9baf8b 100644
--- a/drivers/misc/axxia-pei.c
+++ b/drivers/misc/axxia-pei.c
@@ -150,6 +150,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 control)
+{
+ return (0xf & (control >> 22));
+}
+
static u32
pei_readl(void *address)
{
@@ -1777,6 +1788,335 @@ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 3b7bc19..e7394c4 100644
--- a/drivers/pci/host/pcie-axxia.c
+++ b/drivers/pci/host/pcie-axxia.c
@@ -609,79 +609,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.
- */
-
- 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;
+ 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);
+
+ /* 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;
}
@@ -1131,6 +1156,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 bf2d62f..2fa15f6 100644
--- a/drivers/pci/host/pcie-axxia.h
+++ b/drivers/pci/host/pcie-axxia.h
@@ -29,6 +29,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 9ef3426..6ead0d1 100644
--- a/include/linux/axxia-pei.h
+++ b/include/linux/axxia-pei.h
@@ -19,6 +19,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