From c0a4e6b889a702cc2c8375619ce7b093f6b3b1de Mon Sep 17 00:00:00 2001
From: Yuanquan Chen <B41889@freescale.com>
Date: Mon, 26 Nov 2012 23:49:45 +0000
Subject: [PATCH] powerpc/p4080ds: fix PCI-e x8 link training down failure

Due to SerDes configuration error, if we set the PCI-e controller link width
as x8 in RCW and add a narrower width(such as x4, x2 or x1) PCI-e device to
PCI-e slot, it fails to train down to the PCI-e device's link width. According
to p4080ds errata PCIe-A003, we reset the PCI-e controller link width to x4 in
u-boot. Then it can train down to x2 or x1 width to make the PCI-e link between
RC and EP.

Signed-off-by: Yuanquan Chen <B41889@freescale.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
---
 arch/powerpc/cpu/mpc85xx/cmd_errata.c     |  3 +++
 arch/powerpc/include/asm/config_mpc85xx.h |  1 +
 drivers/pci/fsl_pci_init.c                | 22 ++++++++++++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/arch/powerpc/cpu/mpc85xx/cmd_errata.c b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
index 7d38e1c3a9..e5ecf5dae5 100644
--- a/arch/powerpc/cpu/mpc85xx/cmd_errata.c
+++ b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
@@ -247,6 +247,9 @@ static int do_errata(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 #ifdef CONFIG_SYS_FSL_ERRATUM_A004580
 	/* This work-around is implemented in PBI, so just check for it */
 	check_erratum_a4580(svr);
+#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003
+	puts("Work-around for Erratum PCIe-A003 enabled\n");
 #endif
 	return 0;
 }
diff --git a/arch/powerpc/include/asm/config_mpc85xx.h b/arch/powerpc/include/asm/config_mpc85xx.h
index 965d4312c7..0b9638bcee 100644
--- a/arch/powerpc/include/asm/config_mpc85xx.h
+++ b/arch/powerpc/include/asm/config_mpc85xx.h
@@ -421,6 +421,7 @@
 #define CONFIG_SYS_FSL_ERRATUM_SRIO_A004034
 #define CONFIG_SYS_FSL_ERRATUM_A004849
 #define CONFIG_SYS_FSL_ERRATUM_A004580
+#define CONFIG_SYS_P4080_ERRATUM_PCIE_A003
 
 #elif defined(CONFIG_PPC_P5020) /* also supports P5010 */
 #define CONFIG_SYS_PPC64		/* 64-bit core */
diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c
index 48ae16374d..77ac1f7c7b 100644
--- a/drivers/pci/fsl_pci_init.c
+++ b/drivers/pci/fsl_pci_init.c
@@ -470,6 +470,28 @@ void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info)
 		}
 #endif
 
+#ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003
+		if (enabled == 0) {
+			serdes_corenet_t *srds_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
+			temp32 = in_be32(&srds_regs->srdspccr0);
+
+			if ((temp32 >> 28) == 3) {
+				int i;
+
+				out_be32(&srds_regs->srdspccr0, 2 << 28);
+				setbits_be32(&pci->pdb_stat, 0x08000000);
+				in_be32(&pci->pdb_stat);
+				udelay(100);
+				clrbits_be32(&pci->pdb_stat, 0x08000000);
+				asm("sync;isync");
+				for (i=0; i < 100 && ltssm < PCI_LTSSM_L0; i++) {
+					pci_hose_read_config_word(hose, dev, PCI_LTSSM, &ltssm);
+					udelay(1000);
+				}
+				enabled = ltssm >= PCI_LTSSM_L0;
+			}
+		}
+#endif
 		if (!enabled) {
 			/* Let the user know there's no PCIe link */
 			printf("no link, regs @ 0x%lx\n", pci_info->regs);
-- 
2.39.5