[coreboot-gerrit] Change in coreboot[master]: southbridge/amd/sr5650: Rework core setup and link training

Timothy Pearson (Code Review) gerrit at coreboot.org
Mon Mar 20 14:44:07 CET 2017


Timothy Pearson has uploaded a new change for review. ( https://review.coreboot.org/18919 )

Change subject: southbridge/amd/sr5650: Rework core setup and link training
......................................................................

southbridge/amd/sr5650: Rework core setup and link training

The core setup and link training code was incomplete, buggy,
and made several assumptions about the underlying hardware.

Rework the affected code to be in line with the algorithms
shown in the SR5650 RPR.  Also make hot plug support
configurable on a board to board basis, and hide non-hotplug
capable slots when empty.

This resolves several link training failures, including an
inability to use certain Intel quad port NICs.

Signed-off-by: Timothy Pearson <tpearson at raptorengineering.com>
Change-Id: I5786267f460d347dad2b48bb34f437800dc683a7
---
M src/southbridge/amd/sr5650/Kconfig
M src/southbridge/amd/sr5650/pcie.c
M src/southbridge/amd/sr5650/sr5650.c
M src/southbridge/amd/sr5650/sr5650.h
4 files changed, 379 insertions(+), 216 deletions(-)


  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/19/18919/1

diff --git a/src/southbridge/amd/sr5650/Kconfig b/src/southbridge/amd/sr5650/Kconfig
index 85f6b13..ad045e2 100644
--- a/src/southbridge/amd/sr5650/Kconfig
+++ b/src/southbridge/amd/sr5650/Kconfig
@@ -23,4 +23,7 @@
 	help
 	  Select to enable PCI-E MMCONFIG support on the SR5650.
 
+config PCIE_HOTPLUG_SUPPORT
+	bool
+	default n
 endif
diff --git a/src/southbridge/amd/sr5650/pcie.c b/src/southbridge/amd/sr5650/pcie.c
index e198b87..0e0f5f8 100644
--- a/src/southbridge/amd/sr5650/pcie.c
+++ b/src/southbridge/amd/sr5650/pcie.c
@@ -56,66 +56,45 @@
 * Compliant with CIM_33's PCIEPowerOffGppPorts
 * Power off unused GPP lines
 *****************************************************************/
-static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port)
+static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, uint32_t gpp_sb_sel, uint8_t port, uint8_t hw_port)
 {
-	printk(BIOS_DEBUG, "PciePowerOffGppPorts() port %d\n", port);
+	printk(BIOS_DEBUG, "PciePowerOffGppPorts() port %x (gpp_sb_sel: %08x)\n", port, gpp_sb_sel);
 	u32 reg;
 	u16 state_save;
-	uint8_t i;
 	struct southbridge_amd_sr5650_config *cfg =
 		(struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
 	u16 state = cfg->port_enable;
+
+	uint8_t disable_and_hide_unused_ports = !IS_ENABLED(CONFIG_PCIE_HOTPLUG_SUPPORT);
 
 	if (!(AtiPcieCfg.Config & PCIE_DISABLE_HIDE_UNUSED_PORTS))
 		state &= AtiPcieCfg.PortDetect;
 	state = ~state;
 	state &= (1 << 4) + (1 << 5) + (1 << 6) + (1 << 7);
 	state_save = state << 17;
-	/* Disable ports any that failed training */
-	for (i = 9; i <= 13; i++) {
-		if (!(AtiPcieCfg.PortDetect & 1 << i)) {
-			if ((port >= 9) && (port <= 13)) {
-				state |= (1 << (port + 7));
-			}
-			if (port == 9)
-				state_save |= 1 << 25;
-			if (port == 10)
-				state_save |= 1 << 26;
-			if (port == 11)
-				state_save |= 1 << 6;
-			if (port == 12)
-				state_save |= 1 << 7;
+	if (disable_and_hide_unused_ports) {
+		/* Disable and hide any ports that failed training */
+		if (!(AtiPcieCfg.PortDetect & (0x1 << port))) {
+			/* Hide the bridge */
+			pcie_bridge_set_visible(nb_dev, dev, hw_port, 0);
 
-			if (port == 13) {
-				reg = nbmisc_read_index(nb_dev, 0x2a);
-				reg |= 1 << 4;
-				nbmisc_write_index(nb_dev, 0x2a, reg);
-			}
+			/* Hold training */
+			pcie_control_port_training(nb_dev, dev, hw_port, 0);
 		}
 	}
-	state &= !(AtiPcieCfg.PortHp);
-	reg = nbmisc_read_index(nb_dev, 0x0c);
-	reg |= state;
-	nbmisc_write_index(nb_dev, 0x0c, reg);
 
-	reg = nbmisc_read_index(nb_dev, 0x08);
-	reg |= state_save;
-	nbmisc_write_index(nb_dev, 0x08, reg);
-
-	if ((AtiPcieCfg.Config & PCIE_OFF_UNUSED_GPP_LANES)
-	    && !(AtiPcieCfg.
-		 Config & (PCIE_DISABLE_HIDE_UNUSED_PORTS +
-			   PCIE_GFX_COMPLIANCE))) {
-	}
 	/* step 3 Power Down Control for Southbridge */
 	reg = nbpcie_p_read_index(dev, 0xa2);
 
+	/* WARNING
+	 * Assumes lanes are not reversed
+	 */
 	switch ((reg >> 4) & 0x7) {	/* get bit 4-6, LC_LINK_WIDTH_RD */
 	case 1:
-		nbpcie_ind_write_index(nb_dev, 0x65, 0x0e0e);
+		nbpcie_ind_write_index(nb_dev, gpp_sb_sel, 0x65, 0x0e0e);
 		break;
 	case 2:
-		nbpcie_ind_write_index(nb_dev, 0x65, 0x0c0c);
+		nbpcie_ind_write_index(nb_dev, gpp_sb_sel, 0x65, 0x0c0c);
 		break;
 	default:
 		break;
@@ -142,7 +121,7 @@
 
 	/* 4.3.3.1.1.1.step3. Programs PCIE-GPP1 to be desired port configuration 8:8 or 16:0. */
 	reg = nbmisc_read_index(nb_dev, 0x8);
-	reg &= ~(1 << 8);		/* clean */
+	reg &= ~(1 << 8);		/* clear */
 	reg |= cfg->gpp1_configuration << 8;
 	nbmisc_write_index(nb_dev, 0x8, reg);
 
@@ -286,65 +265,6 @@
 	set_nbcfg_enable_bits(nb_dev, 0x7C, 1 << 30, 0 << 30);	/* Disable writes to the BAR3. */
 	ProgK8TempMmioBase(0, EXT_CONF_BASE_ADDRESS, TEMP_MMIO_BASE_ADDRESS);
 }
-
-/*
- * GEN2 Software Compliance
- */
-void init_gen2(device_t nb_dev, device_t dev, u8 port)
-{
-	u32 reg, val;
-
-	/* for A11 (0x89 == 0) */
-	reg = 0x34;
-	if (port <= 3) {
-		val = 1<<5;
-	} else {
-		val = 1<<31;
-		if (port >= 9)
-			reg = 0x39;
-	}
-
-	/* TODO: check for rev > a11 */
-	switch (port) {
-		case 2:
-			reg = 0x34;
-			val = 1<<5;
-			break;
-		case 3:
-			reg = 0x22;
-			val = 1<<6;
-			break;
-		case 4:
-			reg = 0x34;
-			val = 1<<31;
-			break;
-		case 5:
-		case 6:
-			reg = 0x39;
-			val = 1<<31;
-			break;
-		case 7:
-		case 8:
-		case 9:
-			reg = 0x37;
-			val = 1<<port;
-			break;
-		case 10:
-			reg = 0x22;
-			val = 1<<5;
-			break;
-		default:
-			reg = 0;
-			break;
-	}
-
-	/* Enables GEN2 capability of the device */
-	set_pcie_enable_bits(dev, 0xA4, 0x1, 0x1);
-	/* Advertise the link speed to be Gen2 */
-	pci_ext_write_config32(nb_dev, dev, 0x88, 0xF0, 1<<2); /* LINK_CRTL2 */
-	set_nbmisc_enable_bits(nb_dev, reg, val, val);
-}
-
 
 /* Alternative to default CPL buffer count */
 const u8 pGpp420000[] = {0x38, 0x1C};
@@ -505,6 +425,10 @@
 void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
 {
 	uint8_t training_ok = 1;
+	uint8_t gen2_auto = 1;
+	uint8_t force_gen1 = 0;
+	uint8_t link_speed_cap = 2;
+	uint32_t hw_port = port;
 
 	u32 gpp_sb_sel = 0;
 	struct southbridge_amd_sr5650_config *cfg =
@@ -548,14 +472,16 @@
 	set_pcie_enable_bits(dev, 0xA0, 0x0000FFF0, 0x6830);
 	// PCIE should not ignore malformed packet error or ATS request
 	set_pcie_enable_bits(dev, 0x70, 1 << 12, 0);
-	//Step 14.1: Advertising Hot Plug Capabilities
-	set_pcie_enable_bits(dev, 0x10, 1 << 4, 1 << 4); //Enable power fault
 
-	set_pcie_enable_bits(nb_dev, 0xC1 | gpp_sb_sel, 1 << 0, 1 << 0);
+	//Step 14.1: Advertising Hot Plug Capabilities
+	if (IS_ENABLED(CONFIG_PCIE_HOTPLUG_SUPPORT))
+		set_pcie_enable_bits(dev, 0x10, 1 << 4, 1 << 4); // Enable power fault
+
+	set_pcie_enable_bits(nb_dev, 0xc1 | gpp_sb_sel, 1 << 0, 1 << 0);
 
 	/* init GPP core */
 	/* 4.4.2.step13.1. Sets RCB completion timeout to be 200ms */
-	pci_ext_write_config32(nb_dev, dev, 0x80, 0xF << 0, 0x6 << 0);
+	pci_ext_write_config32(nb_dev, dev, 0x80, 0xf << 0, 0x6 << 0);
 	/* 4.4.2.step13.2. RCB completion timeout on link down to shorten enumeration time. */
 	set_pcie_enable_bits(dev, 0x70, 1 << 19, 1 << 19);
 	/* 4.4.2.step13.3. Enable slave ordering rules */
@@ -569,11 +495,17 @@
 	   prevent LC from going to L1 when there are outstanding completions.*/
 	set_pcie_enable_bits(dev, 0x02, 1 << 15, 1 << 15);
 
-	/* Enables the PLL power down when all lanes are inactive.
-	 * It should be on in GPP.
-	 */
-	if (gpp_sb_sel == PCIE_CORE_INDEX_GPP3a || gpp_sb_sel == PCIE_CORE_INDEX_GPP3b || gpp_sb_sel == PCIE_CORE_INDEX_SB) {
+	if ((gpp_sb_sel == PCIE_CORE_INDEX_GPP3a)
+		|| (gpp_sb_sel == PCIE_CORE_INDEX_GPP3b)
+		|| (gpp_sb_sel == PCIE_CORE_INDEX_SB)) {
+		/* Enables the PLL power down when all lanes are inactive.
+		 * It should be on in GPP.
+		 */
 		set_pcie_enable_bits(nb_dev, 0x02 | gpp_sb_sel, 1 << 3, 1 << 3);
+
+		/* Enable BUG fix for race condition between LC wakeup from L1
+		 * and PLL calibration in GEN2
+		 */
 	}
 
 	/* 4.4.2.step13.7. Set REGS_LC_DONT_GO_TO_L0S_IF_L1_ARMED to prevent
@@ -612,8 +544,10 @@
 	   For Hot-Plug Slots: Advertise TX L0s and L1 exit latency.
 	   TX L0s exit latency to be 110b: 2us to 4us.
 	   L1 exit latency to be 111b: more than 64us.*/
-	//set_pcie_enable_bits(dev, 0xC1, 0xF << 0, 0xC << 0); /* 0xF for hotplug. */
-	set_pcie_enable_bits(dev, 0xC1, 0xF << 0, 0xF << 0); /* 0xF for hotplug. */
+	if (IS_ENABLED(CONFIG_PCIE_HOTPLUG_SUPPORT))
+		set_pcie_enable_bits(dev, 0xc1, 0xf << 0, 0xf << 0);
+	else
+		set_pcie_enable_bits(dev, 0xc1, 0xf << 0, 0xc << 0);
 	/* 4.4.2.step13.17. Always ACK an ASPM L1 entry DLLP to
 	   workaround credit control issue on PM_NAK
 	   message of SB700 and SB800. */
@@ -622,13 +556,42 @@
 		set_pcie_enable_bits(dev, 0xA0, 1 << 23, 1 << 23);
 		set_pcie_enable_bits(nb_dev, 0xC1 | gpp_sb_sel, 1 << 1, 1 << 1);
 	}
-	/* 4.4.2.step13.19. CMOS Option (Gen 2 AUTO-Part 1 - Enabled by Default) */
-	/* 4.4.2.step13.20. CMOS Option (RC Advertised Gen 2-Part1 - Disabled by Default)*/
-	set_nbcfg_enable_bits(dev, 0x88, 0xF << 0, 0x2 << 0);
-	/* Disables GEN2 capability of the device.
-	 * RPR typo- it says enable but the bit setting says disable.
-	 * Disable it here and we enable it later. */
-	set_pcie_enable_bits(dev, 0xA4, 1 << 0, 1 << 0);
+
+	if (force_gen1) {
+		link_speed_cap = 1;
+
+		/* CIMX */
+		set_nbcfg_enable_bits(dev, PCIE_LINK_CNTL2, 0xf << 0, (link_speed_cap & 0xf) << 0);
+		set_pcie_enable_bits(dev, PCIE_LC_SPEED_CNTL, 1 << 0, 0 << 0);
+		set_pcie_enable_bits(dev, PCIE_LC_SPEED_CNTL, 1 << 29, 0 << 29);
+
+		pcie_set_deemphasis_enable(nb_dev, dev, port, 0);
+		set_pcie_enable_bits(dev, PCIEP_STRAP_LC, 1 << 15, 1 << 15);
+		set_pcie_enable_bits(dev, PCIE_LC_LINK_WIDTH, 1 << 13, 1 << 13);
+	}
+
+	if (gen2_auto && !force_gen1) {
+		/* 4.4.2.step13.19. CMOS Option (Gen 2 AUTO-Part 1 - Enabled by Default) */
+		set_nbcfg_enable_bits(dev, PCIE_LINK_CNTL2, 0xf << 0, (link_speed_cap & 0xf) << 0);
+		set_pcie_enable_bits(dev, PCIE_LC_SPEED_CNTL, 1 << 0, 1 << 0);
+		set_pcie_enable_bits(dev, PCIE_LC_SPEED_CNTL, 1 << 29, 1 << 29);
+
+		/* CIMX */
+		pcie_set_deemphasis_enable(nb_dev, dev, port, 1);
+		set_pcie_enable_bits(dev, PCIEP_STRAP_LC, 1 << 15, 0 << 15);
+		set_pcie_enable_bits(dev, PCIE_LC_LINK_WIDTH, 1 << 13, 0 << 13);
+	}
+	else if (!gen2_auto && !force_gen1) {
+		/* 4.4.2.step13.20. CMOS Option (RC Advertised Gen 2-Part1 - Disabled by Default)*/
+		set_nbcfg_enable_bits(dev, PCIE_LINK_CNTL2, 0xf << 0, (link_speed_cap & 0xf) << 0);
+		set_pcie_enable_bits(dev, PCIE_LC_SPEED_CNTL, 1 << 0, 1 << 0);
+		set_pcie_enable_bits(dev, PCIE_LC_SPEED_CNTL, 1 << 29, 1 << 29);
+
+		/* CIMX */
+		pcie_set_deemphasis_enable(nb_dev, dev, port, 1);
+		set_pcie_enable_bits(dev, PCIEP_STRAP_LC, 1 << 15, 1 << 15);
+		set_pcie_enable_bits(dev, PCIE_LC_LINK_WIDTH, 1 << 13, 1 << 13);
+	}
 
 	/* 4.4.2.step13.21. Legacy Hot Plug  -CMOS Option */
 	/* NOTE: This feature can be enabled only for Hot-Plug slots implemented on SR5690 platform. */
@@ -653,7 +616,7 @@
 	/* step14.5 */
 	/* skip */
 
-	/* CIMx LPC Deadlock workaround - Enable Memory Write Map*/
+	/* CIMx LPC Deadlock workaround - Enable Memory Write Map */
 	if (gpp_sb_sel == PCIE_CORE_INDEX_SB) {
 		set_pcie_enable_bits(nb_dev, 0x10 | gpp_sb_sel, 1 << 9, 1 << 9);
 		set_htiu_enable_bits(nb_dev, 0x06, 1 << 26, 1 << 26);
@@ -662,40 +625,33 @@
 	/* This CPL setup requires more than this one register and should be done in gpp_core.
 	 * The additional setup is for the different revisions. */
 
-	/* CIMx CommonPortInit settings that are not set above. */
-	pci_ext_write_config32(nb_dev, dev, 0x88, 0xF0, 1 << 0); /* LINK_CRTL2 */
-
 	if ( port == 8 )
 		set_pcie_enable_bits(dev, 0xA0, 0, 1 << 23);
 
-#if 0 //SR56x0 pcie Gen2 code is not tested yet, we should enable it again when test finished.
-	/* set automatic Gen2 support, needs mainboard config option as Gen2 can cause issues on some platforms. */
-	init_gen2(nb_dev, dev, port);
-	set_pcie_enable_bits(dev, 0xA4, 1 << 29, 1 << 29);
-	set_pcie_enable_bits(dev, 0xC0, 1 << 15, 0);
-	set_pcie_enable_bits(dev, 0xA2, 1 << 13, 0);
-#endif
-
 	/* Hotplug Support - bit5 + bit6  capable and surprise */
-	pci_ext_write_config32(nb_dev, dev, 0x6c, 0x60, 0x60);
+	if (IS_ENABLED(CONFIG_PCIE_HOTPLUG_SUPPORT)) {
+		pci_ext_write_config32(nb_dev, dev, 0x6c, 0x60, 0x60);
 
-	/* Set interrupt pin info 0x3d */
-	pci_ext_write_config32(nb_dev, dev, 0x3c, 1 << 8, 1 << 8);
+		/* Set interrupt pin info 0x3d */
+		pci_ext_write_config32(nb_dev, dev, 0x3c, 1 << 8, 1 << 8);
 
-	/* 5.12.9.3 Hotplug step 1 - NB_PCIE_ROOT_CTRL - enable pm irq
-	The RPR is wrong - this is not a PCIEND_P register */
-	pci_ext_write_config32(nb_dev, dev, 0x74, 1 << 3, 1 << 3);
+		/* 5.12.9.3 Hotplug step 1 - NB_PCIE_ROOT_CTRL - enable pm irq
+		The RPR is wrong - this is not a PCIEND_P register */
+		pci_ext_write_config32(nb_dev, dev, 0x74, 1 << 3, 1 << 3);
 
-	/* 5.12.9.3 step 2 - PCIEP_PORT_CNTL - enable hotplug messages */
-	if ( port != 8)
-		set_pcie_enable_bits(dev, 0x10, 1 << 2, 1 << 2);
+		/* 5.12.9.3 step 2 - PCIEP_PORT_CNTL - enable hotplug messages */
+		if ( port != 8)
+			set_pcie_enable_bits(dev, 0x10, 1 << 2, 1 << 2);
 
-	/* Not sure about this PME setup */
-	/* Native PME */
-	set_pcie_enable_bits(dev, 0x10, 1 << 3, 1 << 3); /* Not set in CIMx */
+		/* Not sure about this PME setup */
+		/* Native PME */
+		set_pcie_enable_bits(dev, 0x10, 1 << 3, 1 << 3); /* Not set in CIMx */
 
-	/* PME Enable */
-	pci_ext_write_config32(nb_dev, dev, 0x54, 1 << 8, 1 << 8); /* Not in CIMx */
+		/* PME Enable */
+		pci_ext_write_config32(nb_dev, dev, 0x54, 1 << 8, 1 << 8); /* Not in CIMx */
+	} else {
+		pci_ext_write_config32(nb_dev, dev, 0x6c, 0x60, 0x20);
+	}
 
 	/* 4.4.3 Training for GPP devices */
 	/* init GPP */
@@ -706,7 +662,7 @@
 	case 5:
 	case 6:
 	case 7:
-	case 9:	/*GPP*/
+	case 9:	/* GPP */
 	case 10:
 	case 11:
 	case 12:
@@ -718,7 +674,6 @@
 
 		/* check port enable */
 		if (cfg->port_enable & (1 << port)) {
-			uint32_t hw_port = port;
 			switch (cfg->gpp3a_configuration) {
 			case 0x1: /* 4:2:0:0:0:0 */
 				if (hw_port == 9)
@@ -760,7 +715,7 @@
 				printk(BIOS_WARNING, "invalid gpp3a_configuration\n");
 				return;
 			}
-			PcieReleasePortTraining(nb_dev, dev, hw_port);
+			pcie_control_port_training(nb_dev, dev, hw_port, 1);
 			if (!(AtiPcieCfg.Config & PCIE_GPP_COMPLIANCE)) {
 				u8 res = PcieTrainPort(nb_dev, dev, hw_port);
 				printk(BIOS_DEBUG, "%s: port=0x%x hw_port=0x%x result=%d\n",
@@ -787,8 +742,10 @@
 	/* Re-enable RC ordering logic after training (from CIMx)*/
 	set_pcie_enable_bits(nb_dev, 0x20 | gpp_sb_sel, 1 << 9, 0);
 
-	/* Advertising Hot Plug Capabilities */
-	pci_ext_write_config32(nb_dev, dev, 0x6c, 0x04001B, 0x00001B);
+	if (IS_ENABLED(CONFIG_PCIE_HOTPLUG_SUPPORT)) {
+		/* Advertising Hot Plug Capabilities */
+		pci_ext_write_config32(nb_dev, dev, 0x6c, 0x04001B, 0x00001B);
+	}
 
 	/* PCIE Late Init (CIMx late init - Maybe move somewhere else? Later in the coreboot PCI device enum?) */
 	/* Set Slot Number */
@@ -821,7 +778,7 @@
 	 */
 
 	if ((port == 8) || (!training_ok)) {
-		PciePowerOffGppPorts(nb_dev, dev, port);	/* This is run for all ports that are not hotplug and don't detect devices */
+		PciePowerOffGppPorts(nb_dev, dev, gpp_sb_sel, port, hw_port);	/* This is run for all ports that are not hotplug and don't detect devices */
 	}
 }
 
diff --git a/src/southbridge/amd/sr5650/sr5650.c b/src/southbridge/amd/sr5650/sr5650.c
index 47de24c..a215c7e 100644
--- a/src/southbridge/amd/sr5650/sr5650.c
+++ b/src/southbridge/amd/sr5650/sr5650.c
@@ -2,7 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
- * Copyright (C) 2015 Timothy Pearson <tpearson at raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2015 Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -91,14 +91,14 @@
 	nb_write_index((dev), NBPCIE_INDEX, (index), (data));
 }
 
-u32 nbpcie_ind_read_index(device_t nb_dev, u32 index)
+u32 nbpcie_ind_read_index(device_t nb_dev, uint32_t gpp_sb_sel, uint32_t index)
 {
-	return nb_read_index((nb_dev), NBPCIE_INDEX, (index));
+	return nb_read_index(nb_dev, NBPCIE_INDEX, index | gpp_sb_sel);
 }
 
-void nbpcie_ind_write_index(device_t nb_dev, u32 index, u32 data)
+void nbpcie_ind_write_index(device_t nb_dev, uint32_t gpp_sb_sel, uint32_t index, uint32_t data)
 {
-	nb_write_index((nb_dev), NBPCIE_INDEX, (index), (data));
+	nb_write_index(nb_dev, NBPCIE_INDEX, index | gpp_sb_sel, data);
 }
 
 uint32_t l2cfg_ind_read_index(device_t nb_dev, uint32_t index)
@@ -157,34 +157,102 @@
 	}
 }
 
-void PcieReleasePortTraining(device_t nb_dev, device_t dev, u32 port)
+void pcie_control_port_training(device_t nb_dev, device_t dev, uint32_t port, uint8_t release)
 {
+	printk(BIOS_SPEW, "%s: port %x\n", __func__, port);
+
 	switch (port) {
 	case 2:		/* GPP1, bit4-5 */
 	case 3:
 		set_nbmisc_enable_bits(nb_dev, PCIE_LINK_CFG,
-				       1 << (port + 2), 0 << (port + 2));
+				       1 << (port + 2), (!release) << (port + 2));
 		break;
 	case 4:		/* GPP3a, bit20-24 */
 	case 5:
 	case 6:
 	case 7:
 		set_nbmisc_enable_bits(nb_dev, PCIE_LINK_CFG,
-				       1 << (port + 17), 0 << (port + 17));
+				       1 << (port + 17), (!release) << (port + 17));
 		break;
 	case 9:		/* GPP3a, bit25,26 */
 	case 10:
 		set_nbmisc_enable_bits(nb_dev, PCIE_LINK_CFG,
-				      1 << (port + 16), 0 << (port + 16));
+				      1 << (port + 16), (!release) << (port + 16));
 		break;
 	case 11:	/* GPP2, bit6-7 */
 	case 12:
 		set_nbmisc_enable_bits(nb_dev, PCIE_LINK_CFG,
-				       1 << (port - 5), 0 << (port - 5));
+				       1 << (port - 5), (!release) << (port - 5));
 		break;
-	case 13:	/* GPP3b, bit4 of NBMISCIND:0x2A */
-		set_nbmisc_enable_bits(nb_dev, 0x2A,
-				       1 << 4, 0 << 4);
+	case 13:	/* GPP3b, bit4 of PCIE_NBCFG_REG12 */
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG12,
+				       1 << 4, (!release) << 4);
+		break;
+	}
+}
+
+void pcie_bridge_set_visible(device_t nb_dev, device_t dev, uint32_t port, uint8_t visible)
+{
+	printk(BIOS_SPEW, "%s: port %x\n", __func__, port);
+
+	switch (port) {
+	case 2:		/* GPP1 */
+	case 3:
+	case 4:		/* GPP3a */
+	case 5:
+	case 6:
+	case 7:
+		set_nbmisc_enable_bits(nb_dev, IOC_P2P_CNTL,
+				       1 << port, (!visible) << port);
+		break;
+	case 9:		/* GPP3a */
+	case 10:
+	case 11:	/* GPP2 */
+	case 12:
+	case 13:	/* GPP3b */
+		set_nbmisc_enable_bits(nb_dev, IOC_P2P_CNTL,
+				      1 << (port + 7), (!visible) << (port + 7));
+		break;
+	}
+}
+
+void pcie_set_deemphasis_enable(device_t nb_dev, device_t dev, uint32_t port, uint8_t enable)
+{
+	enable &= 0x1;
+
+	switch (port) {
+	case 2:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1, enable);
+		break;
+	case 3:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 1, enable << 1);
+		break;
+	case 4:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 2, enable << 2);
+		break;
+	case 5:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 3, enable << 3);
+		break;
+	case 6:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 4, enable << 4);
+		break;
+	case 7:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 5, enable << 5);
+		break;
+	case 9:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 6, enable << 6);
+		break;
+	case 10:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG10, 1 << 7, enable << 7);
+		break;
+	case 11:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REGF, 1 << 30, enable << 30);
+		break;
+	case 12:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REGF, 1 << 31, enable << 31);
+		break;
+	case 13:
+		set_nbmisc_enable_bits(nb_dev, PCIE_NBCFG_REG15, 1 << 5, enable << 5);
 		break;
 	}
 }
@@ -196,10 +264,16 @@
 ********************************************************************************************************/
 u8 PcieTrainPort(device_t nb_dev, device_t dev, u32 port)
 {
-	u16 count = 5000;
-	u32 lc_state, reg, current_link_width, lane_mask;
-	u8 current, res = 0;
+	u16 count = 50;
+	uint32_t device_not_found_timer = 0;
+	uint32_t lc_state, reg, link_width_reqd, current_link_width, /*lane_mask,*/ prev1, prev2, prev3;
+	uint8_t current = 0;
+	uint8_t res = 0;
 	u32 gpp_sb_sel = 0;
+	uint8_t lane_reversal = 0;
+	uint8_t gen2_auto = 1;
+	uint8_t gen1_fallback = 0;
+	uint8_t lane_width_fallback = 0;
 
 	switch (port) {
 	case 2:
@@ -221,51 +295,169 @@
 	}
 
 	while (count--) {
-		udelay(40200);
+		udelay(40200);	/* 40ms + 200us delay for all devices to appear */
 		lc_state = nbpcie_p_read_index(dev, 0xa5);	/* lc_state */
-		printk(BIOS_DEBUG, "PcieLinkTraining port=%x:lc current state=%x\n",
+		printk(BIOS_DEBUG, "PcieLinkTraining port=%x:lc current state=%08x\n",
 			     port, lc_state);
-		current = lc_state & 0x3f;	/* get LC_CURRENT_STATE, bit0-5 */
+		current = lc_state & 0x3f;		/* get LC_CURRENT_STATE, bit0-5 */
+		prev1 = (lc_state >> 8) & 0x3f;		/* get LC_PREV_STATE1, bit 8-13*/
+		prev2 = (lc_state >> 16) & 0x3f;	/* get LC_PREV_STATE2, bit 16-21*/
+		prev3 = (lc_state >> 24) & 0x3f;	/* get LC_PREV_STATE3, bit 24-29*/
 
-		switch (current) {
-			/* 0x00-0x04 means no device is present */
-		case 0x06:
-			/* read back current link width [6:4]. */
-			current_link_width = (nbpcie_p_read_index(dev, 0xA2) >> 4) & 0x7;
-			/* 4 means 7:4 and 15:12
-			 * 3 means 7:2 and 15:10
-			 * 2 means 7:1 and 15:9
-			 * ignoring the reversal case
-			 */
-			lane_mask = (0xFF << (current_link_width - 2) * 2) & 0xFF;
-			reg = nbpcie_ind_read_index(nb_dev, 0x65 | gpp_sb_sel);
-			reg |= lane_mask << 8 | lane_mask;
-			/* NOTE: See the comments in rs780_pcie.c
-			 * switching_gppsb_configurations
-			 * In CIMx 4.5.0 and RPR, 4c is done before 5 & 6.
-			 * But in this way, a x4 device in port B (dev 4) of
-			 * Configuration B can only be detected as x1, instead
-			 * of x4. When the port B is being trained, the
-			 * LC_CURRENT_STATE is 6 and the LC_LINK_WIDTH_RD is 1.
-			 * We have to set the PCIEIND:0x65 as 0xE0E0 and reset
-			 * the slot. Then the card seems to work in x1 mode.
-			 */
-			reg = 0xE0E0; /*I think that the lane_mask calc above is wrong, and this can't be hardcoded because the configuration changes.*/
-			nbpcie_ind_write_index(nb_dev, 0x65 | gpp_sb_sel, reg);
-			printk(BIOS_DEBUG, "link_width=%x, lane_mask=%x",
-				     current_link_width, lane_mask);
-			set_pcie_reset();
-			mdelay(1);
-			set_pcie_dereset();
-			break;
-		case 0x07:	/* device is in compliance state (training sequence is done). Move to train the next device */
+		/* port lane reversal */
+		/* BIF_NBP:PCIE_P_PORT_LANE_STATUS[0] · PCIEIND_P:0x50 */
+		/* port lane order is normal if bit [0] = 0x0; else reversed. */
+		reg = nbpcie_p_read_index(dev, 0x50);	/* PORT_LANE_REVERSAL */
+		lane_reversal = reg & 0x1;
+		printk(BIOS_DEBUG, "PORT_LANE_REVERSAL=%s\n", (lane_reversal) ? "TRUE" : "FALSE");
+		printk(BIOS_DEBUG, "LINK_CNTL2=0x%08x\n", pci_ext_read_config32(nb_dev, dev, PCIE_LINK_CNTL2));
+		printk(BIOS_DEBUG, "PCIE_LC_LINK_WIDTH_CNTL=0x%08x\n", nbpcie_p_read_index(dev, PCIE_LC_LINK_WIDTH));
+		printk(BIOS_DEBUG, "PCIE_LC_SPEED_CNTL=0x%08x\n", nbpcie_p_read_index(dev, PCIE_LC_SPEED_CNTL));
+		printk(BIOS_DEBUG, "PCIEP_STRAP_LC=0x%08x\n", nbpcie_p_read_index(dev, PCIEP_STRAP_LC));
+
+		if (current < 0x05) {
+			/* device not responding or not present */
+			device_not_found_timer++;
+			if (device_not_found_timer > 3) {
+				count = 0;
+				break;
+			}
+		} else {
+			device_not_found_timer = 0;
+		}
+
+		if ((((current == 0x06) || (prev1 == 0x06) || (prev2 == 0x06) || (prev3 == 0x06))
+			|| ((current == 0x2a) || (prev1 == 0x2a) || (prev2 == 0x2a) || (prev3 == 0x2a)))
+			&& !lane_width_fallback) {
+
+			lane_width_fallback = 1;
+			/* read back expected link width [2:0] and current link width [6:4]. */
+			reg = nbpcie_p_read_index(dev, PCIE_LC_LINK_WIDTH);
+			link_width_reqd = reg & 0x7;
+			current_link_width = (reg >> 4) & 0x7;
+			/* debugging printout */
+			printk(BIOS_DEBUG, "current link width=%x, expected link width=%x\n",
+					current_link_width, link_width_reqd);
+
+			printk(BIOS_SPEW, "PCIE_P_PAD_FORCE_DIS (before config)=%08x\n",
+				nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS));
+
+			if (current_link_width < link_width_reqd) {
+				/* One or more of the PCIe device transmitter lanes is broken!
+				 * Attempt to mask off high lanes to see if recovery is possible.
+				 * NOTE: Recovering a PCIe 1x device with a broken transmitter
+				 * lane is impossible by definition, so we do nothing in that case.
+				 */
+				if (!lane_reversal) {
+					switch (current_link_width) {
+					case 0x4:
+						/* PCIe x8: [7:4]=0xf, [15:12]=0xf */
+						reg = nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS);
+						nbpcie_ind_write_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS, (reg | 0xf0f0));
+						break;
+					case 0x3:
+						/* PCIe x4: [7:2]=0x3f, [15:10]=0x3f */
+						reg = nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS);
+						nbpcie_ind_write_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS, (reg | 0xfcfc));
+						break;
+					case 0x2:
+						/* PCIe x2: [7:1]=0x7f, [15:9]=0x7f */
+						reg = nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS);
+						nbpcie_ind_write_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS, (reg | 0xfefe));
+						break;
+					}
+				} else {	/* lane_reversal = true */
+					switch (current_link_width) {
+					case 0x4:
+						/* PCIe x8: [3:0]=0xf, [11:8]=0xf */
+						reg = nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS);
+						nbpcie_ind_write_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS, (reg | 0x0f0f));
+						break;
+					case 0x3:
+						/* PCIe x4: [5:0]=0x3f, [13:8]=0x3f */
+						reg = nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS);
+						nbpcie_ind_write_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS, (reg | 0x3f3f));
+						break;
+					case 0x2:
+						/* PCIe x2: [6:0]=0x7f, [14:8]=0x7f */
+						reg = nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS);
+						nbpcie_ind_write_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS, (reg | 0x7f7f));
+						break;
+					}
+				}
+				/* reset */
+				set_pcie_reset();
+				mdelay(1);
+				set_pcie_dereset();
+
+				printk(BIOS_SPEW, "PCIE_P_PAD_FORCE_DIS (after config)=%08x\n",
+					nbpcie_ind_read_index(nb_dev, gpp_sb_sel, PCIE_P_PAD_FORCE_DIS));
+				continue;
+			}
+		}
+
+		/* STEP 5 */
+		if (((((current == 0x09) || (prev1 == 0x09) || (prev2 == 0x09) || (prev3 == 0x09))
+			|| ((current == 0x06) || (prev1 == 0x06) || (prev2 == 0x06) || (prev3 == 0x06)))
+			|| ((current == 0x2a) || (prev1 == 0x2a) || (prev2 == 0x2a) || (prev3 == 0x2a)))
+			&& !gen1_fallback) {
+			gen1_fallback = 1;
+			if (gen2_auto) {
+				/* set bits [3:0] of TARGET_LINK_SPEED to 0x1 */
+				/* PCIE_LINK_CNTL2 - RW - 16 bits - [pcieConfigDev[13:2]:0x88]*/
+				reg = pci_ext_read_config32(nb_dev, dev, PCIE_LINK_CNTL2);
+				printk(BIOS_DEBUG, "value of reg is 0x%x\n", reg);
+				if ((reg & 0xf) != 0x1) {
+					uint8_t original_speed = reg & 0xf;
+					pci_ext_write_config32(nb_dev, dev, PCIE_LINK_CNTL2, 0xf, 0x1);
+					reg = pci_ext_read_config32(nb_dev, dev, PCIE_LINK_CNTL2);
+					printk(BIOS_DEBUG, "TARGET_LINK_SPEED set to 0x%x (initially 0x%x)\n",
+						(reg & 0xf), original_speed);
+					printk(BIOS_DEBUG, "value read back is 0x%x\n", reg);
+				}
+
+				/* LC_GEN2_EN_STRAP */
+				reg = nbpcie_p_read_index(dev, PCIE_LC_SPEED_CNTL);
+				reg &= ~0x1;
+				nbpcie_p_write_index(dev, PCIE_LC_SPEED_CNTL, reg);
+
+				/* LC_UPCONFIGURE_DIS */
+				reg = nbpcie_p_read_index(dev, PCIE_LC_LINK_WIDTH);
+				reg |= 0x1 << 13;
+				nbpcie_p_write_index(dev, PCIE_LC_LINK_WIDTH, reg);
+
+				/* STRAP_AUTO_RC_SPEED_NEGOTIATION_DIS */
+				reg = nbpcie_p_read_index(dev, PCIEP_STRAP_LC);
+				reg |= 0x1 << 15;
+				nbpcie_p_write_index(dev, PCIEP_STRAP_LC, reg);
+
+				/* LC_MULT_UPSTREAM_AUTO_SPD_CHNG_EN */
+				reg = nbpcie_p_read_index(dev, PCIE_LC_SPEED_CNTL);
+				reg &= ~(0x1 << 29);
+				nbpcie_p_write_index(dev, PCIE_LC_SPEED_CNTL, reg);
+
+				/* Clear deemphasis select for operation at Gen1 speeds */
+				pcie_set_deemphasis_enable(nb_dev, dev, port, 0);
+
+				/* toggle GPIO reset to the PCIe link*/
+				set_pcie_reset();
+				mdelay(1);
+				set_pcie_dereset();
+				continue;
+			} else {
+				/* TODO
+				 * Gen2 Advertised RC - implement step 7 in PCIe Link Training Sequence
+				 */
+			}
+		}
+
+
+		if (current == 0x07) {
+			/* device is in compliance state (training sequence is done). Move to train the next device */
 			res = 1;
 			count = 0;
-			break;
-		case 0x10:
-			reg =
-			    pci_ext_read_config32(nb_dev, dev,
-						  PCIE_VC0_RESOURCE_STATUS);
+		} else if (current == 0x10) {
+			reg = pci_ext_read_config32(nb_dev, dev, PCIE_VC0_RESOURCE_STATUS);
 			printk(BIOS_DEBUG, "PcieTrainPort reg=0x%x\n", reg);
 			/* check bit1 */
 			if (reg & VC_NEGOTIATION_PENDING) {	/* bit1=1 means the link needs to be re-trained. */
@@ -282,18 +474,18 @@
 				res = 1;
 				count = 0;
 			}
-			break;
-		default:
-			/* CIMx Unknown Workaround - There is a device that won't train. Try to reset it. */
-			/* if there are no device resets and nothing works, CIMx does a cf9 system reset (yikes!) */
-			set_pcie_reset();
-			mdelay(1);
-			set_pcie_dereset();
-			res = 0;
-			count = 0;	/* break loop */
-			break;
 		}
 	}
+
+	if ((current > 0x04) && (current != 0x07) && (current != 0x10)) {
+		/* CIMx Unknown Workaround - There is a device that won't train. Try to reset it. */
+		/* if there are no device resets and nothing works, CIMx does a cf9 system reset (yikes!) */
+		printk(BIOS_DEBUG, "%s: PCIe link training failure.  Resetting device...\n", __func__);
+		set_pcie_reset();
+		mdelay(1);
+		set_pcie_dereset();
+		res = 0;
+	}
 	return res;
 }
 
diff --git a/src/southbridge/amd/sr5650/sr5650.h b/src/southbridge/amd/sr5650/sr5650.h
index ea7005c..35b10a5 100644
--- a/src/southbridge/amd/sr5650/sr5650.h
+++ b/src/southbridge/amd/sr5650/sr5650.h
@@ -58,7 +58,12 @@
 /* -------------------- ----------------------
 * NBMISCIND
  ------------------- -----------------------*/
-#define	PCIE_LINK_CFG			0x8
+#define PCIE_LINK_CFG			0x8
+#define IOC_P2P_CNTL			0xc
+#define PCIE_NBCFG_REGF			0x27
+#define PCIE_NBCFG_REG10		0x28
+#define PCIE_NBCFG_REG12		0x2a
+#define PCIE_NBCFG_REG15		0x2d
 #define	PCIE_NBCFG_REG7			0x37
 #define	STRAPS_OUTPUT_MUX_7		0x67
 #define	STRAPS_OUTPUT_MUX_A		0x6a
@@ -67,8 +72,12 @@
 * PCIEIND
  ------------------- -----------------------*/
 #define	PCIE_CI_CNTL			0x20
+#define PCIE_P_PAD_FORCE_DIS		0x65
+#define PCIE_LINK_CNTL2			0x88
 #define	PCIE_LC_LINK_WIDTH		0xa2
+#define	PCIE_LC_SPEED_CNTL		0xa4
 #define PCIE_LC_STATE0			0xa5
+#define	PCIEP_STRAP_LC			0xc0
 #define	PCIE_VC0_RESOURCE_STATUS	0x12a	/* 16bit read only */
 
 #define	PCIE_CORE_INDEX_SB		(0x05 << 16) /* see rpr 4.3.2.2, bdg 2.1 */
@@ -91,8 +100,8 @@
 /* ----------------- export functions ----------------- */
 u32 nbpcie_p_read_index(device_t dev, u32 index);
 void nbpcie_p_write_index(device_t dev, u32 index, u32 data);
-u32 nbpcie_ind_read_index(device_t nb_dev, u32 index);
-void nbpcie_ind_write_index(device_t nb_dev, u32 index, u32 data);
+u32 nbpcie_ind_read_index(device_t nb_dev, uint32_t gpp_sb_sel, uint32_t index);
+void nbpcie_ind_write_index(device_t nb_dev, uint32_t gpp_sb_sel, uint32_t index, uint32_t data);
 uint32_t l2cfg_ind_read_index(device_t nb_dev, uint32_t index);
 void l2cfg_ind_write_index(device_t nb_dev, uint32_t index, uint32_t data);
 uint32_t l1cfg_ind_read_index(device_t nb_dev, uint32_t index);
@@ -118,7 +127,9 @@
 void sr5650_gfx_init(device_t nb_dev, device_t dev, u32 port);
 void avoid_lpc_dma_deadlock(device_t nb_dev, device_t sb_dev);
 void config_gpp_core(device_t nb_dev, device_t sb_dev);
-void PcieReleasePortTraining(device_t nb_dev, device_t dev, u32 port);
+void pcie_set_deemphasis_enable(device_t nb_dev, device_t dev, uint32_t port, uint8_t enable);
+void pcie_control_port_training(device_t nb_dev, device_t dev, uint32_t port, uint8_t release);
+void pcie_bridge_set_visible(device_t nb_dev, device_t dev, uint32_t port, uint8_t visible);
 u8 PcieTrainPort(device_t nb_dev, device_t dev, u32 port);
 void pcie_config_misc_clk(device_t nb_dev);
 void fam10_optimization(void);

-- 
To view, visit https://review.coreboot.org/18919
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5786267f460d347dad2b48bb34f437800dc683a7
Gerrit-PatchSet: 1
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Owner: Timothy Pearson <tpearson at raptorengineering.com>



More information about the coreboot-gerrit mailing list