From e4750c39be6314e924627a6a49b8a0d47f6323d6 Mon Sep 17 00:00:00 2001
From: Alex Deucher <alexdeucher@gmail.com>
Date: Tue, 16 Mar 2010 15:31:25 -0400
Subject: [PATCH] drm/radeon/kms/pm: enable pcie lane change support

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
---
 drivers/gpu/drm/radeon/r100.c            |   14 +++++
 drivers/gpu/drm/radeon/r300.c            |   81 +++++++++++++++++++++++------
 drivers/gpu/drm/radeon/r600.c            |   16 ++++++-
 drivers/gpu/drm/radeon/radeon_asic.c     |    5 +-
 drivers/gpu/drm/radeon/radeon_atombios.c |    5 +--
 drivers/gpu/drm/radeon/radeon_combios.c  |    5 +--
 drivers/gpu/drm/radeon/radeon_reg.h      |    6 ++
 7 files changed, 104 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 56f3303..b035eb5 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -162,6 +162,20 @@ void r100_set_power_state(struct radeon_device *rdev)
 			mclk = rdev->clock.default_mclk;
 
 		/* set pcie lanes */
+		if ((rdev->flags & RADEON_IS_PCIE) &&
+		    !(rdev->flags & RADEON_IS_IGP) &&
+		    rdev->asic->set_pcie_lanes &&
+		    (rdev->pm.power_state[rdev->pm.requested_power_state_index].
+		     non_clock_info.pcie_lanes !=
+		     rdev->pm.power_state[rdev->pm.current_power_state_index].
+		     non_clock_info.pcie_lanes)) {
+			radeon_set_pcie_lanes(rdev,
+					      rdev->pm.power_state[rdev->pm.requested_power_state_index].
+					      non_clock_info.pcie_lanes);
+			DRM_INFO("Setting: p: %d\n",
+				 rdev->pm.power_state[rdev->pm.requested_power_state_index].
+				 non_clock_info.pcie_lanes);
+		}
 		/* TODO */
 
 		/* set voltage */
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 6a0b550..298a586 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -495,7 +495,7 @@ void r300_mc_init(struct radeon_device *rdev)
 
 void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes)
 {
-	uint32_t link_width_cntl, mask;
+	uint32_t link_width_cntl, mask, target_reg;
 
 	if (rdev->flags & RADEON_IS_IGP)
 		return;
@@ -503,6 +503,15 @@ void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes)
 	if (!(rdev->flags & RADEON_IS_PCIE))
 		return;
 
+	/* don't change lanes on multi-gpu cards for now */
+	if ((rdev->ddev->pdev->device == 0x9441) ||
+	    (rdev->ddev->pdev->device == 0x9443) ||
+	    (rdev->ddev->pdev->device == 0x944B) ||
+	    (rdev->ddev->pdev->device == 0x9506) ||
+	    (rdev->ddev->pdev->device == 0x9509) ||
+	    (rdev->ddev->pdev->device == 0x950F))
+		return;
+
 	/* FIXME wait for idle */
 
 	switch (lanes) {
@@ -530,26 +539,64 @@ void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes)
 		break;
 	}
 
-	link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
+	if (rdev->family >= CHIP_R600) {
+		link_width_cntl = RREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
 
-	if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) ==
-	    (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT))
-		return;
+		if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) ==
+		    (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT))
+			return;
+
+		link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK |
+				     RADEON_PCIE_LC_RECONFIG_NOW |
+				     R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE |
+				     R600_PCIE_LC_SHORT_RECONFIG_EN |
+				     R600_PCIE_LC_RENEGOTIATE_EN);
+		link_width_cntl |= mask;
+
+		/* some northbridges can renegotiate the link rather than requiring
+		 * a complete re-config.
+		 * e.g., AMD 780/790 northbridges (pci ids: 0x5956, 0x5957, 0x5958, etc.)
+		 */
+		/* if (northbridge can renegotiate)
+			link_width_cntl |= R600_PCIE_LC_RENEGOTIATE_EN;
+		   else */
+		link_width_cntl |= R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE;
+
+		WREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
+		WREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL, (link_width_cntl |
+							       RADEON_PCIE_LC_RECONFIG_NOW));
+
+		if (rdev->family >= CHIP_RV770)
+			target_reg = R700_TARGET_AND_CURRENT_PROFILE_INDEX;
+		else
+			target_reg = R600_TARGET_AND_CURRENT_PROFILE_INDEX;
+
+		/* wait for lane set to complete */
+		link_width_cntl = RREG32(target_reg);
+		while (link_width_cntl == 0xffffffff)
+			link_width_cntl = RREG32(target_reg);
 
-	link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK |
-			     RADEON_PCIE_LC_RECONFIG_NOW |
-			     RADEON_PCIE_LC_RECONFIG_LATER |
-			     RADEON_PCIE_LC_SHORT_RECONFIG_EN);
-	link_width_cntl |= mask;
-	WREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
-	WREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, (link_width_cntl |
-						     RADEON_PCIE_LC_RECONFIG_NOW));
-
-	/* wait for lane set to complete */
-	link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
-	while (link_width_cntl == 0xffffffff)
+	} else {
 		link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
 
+		if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) ==
+		    (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT))
+			return;
+
+		link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK |
+				     RADEON_PCIE_LC_RECONFIG_NOW |
+				     RADEON_PCIE_LC_RECONFIG_LATER |
+				     RADEON_PCIE_LC_SHORT_RECONFIG_EN);
+		link_width_cntl |= mask;
+		WREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
+		WREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL, (link_width_cntl |
+							     RADEON_PCIE_LC_RECONFIG_NOW));
+
+		/* wait for lane set to complete */
+		link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
+		while (link_width_cntl == 0xffffffff)
+			link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
+	}
 }
 
 int rv370_get_pcie_lanes(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 7e1233f..b569627 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -252,7 +252,21 @@ void r600_set_power_state(struct radeon_device *rdev)
 			mclk = rdev->clock.default_mclk;
 
 		/* set pcie lanes */
-		/* TODO */
+		/* set pcie lanes */
+		if ((rdev->flags & RADEON_IS_PCIE) &&
+		    !(rdev->flags & RADEON_IS_IGP) &&
+		    rdev->asic->set_pcie_lanes &&
+		    (rdev->pm.power_state[rdev->pm.requested_power_state_index].
+		     non_clock_info.pcie_lanes !=
+		     rdev->pm.power_state[rdev->pm.current_power_state_index].
+		     non_clock_info.pcie_lanes)) {
+			radeon_set_pcie_lanes(rdev,
+					      rdev->pm.power_state[rdev->pm.requested_power_state_index].
+					      non_clock_info.pcie_lanes);
+			DRM_INFO("Setting: p: %d\n",
+				 rdev->pm.power_state[rdev->pm.requested_power_state_index].
+				 non_clock_info.pcie_lanes);
+		}
 
 		/* set voltage */
 		/* TODO */
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index 91e3373..1df1e89 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -572,7 +572,7 @@ static struct radeon_asic r600_asic = {
 	.get_memory_clock = &radeon_atom_get_memory_clock,
 	.set_memory_clock = &radeon_atom_set_memory_clock,
 	.get_pcie_lanes = &rv370_get_pcie_lanes,
-	.set_pcie_lanes = NULL,
+	.set_pcie_lanes = &rv370_set_pcie_lanes,
 	.set_clock_gating = NULL,
 	.set_surface_reg = r600_set_surface_reg,
 	.clear_surface_reg = r600_clear_surface_reg,
@@ -654,7 +654,7 @@ static struct radeon_asic rv770_asic = {
 	.get_memory_clock = &radeon_atom_get_memory_clock,
 	.set_memory_clock = &radeon_atom_set_memory_clock,
 	.get_pcie_lanes = &rv370_get_pcie_lanes,
-	.set_pcie_lanes = NULL,
+	.set_pcie_lanes = &rv370_set_pcie_lanes,
 	.set_clock_gating = &radeon_atom_set_clock_gating,
 	.set_surface_reg = r600_set_surface_reg,
 	.clear_surface_reg = r600_clear_surface_reg,
@@ -694,6 +694,7 @@ static struct radeon_asic evergreen_asic = {
 	.set_engine_clock = &radeon_atom_set_engine_clock,
 	.get_memory_clock = &radeon_atom_get_memory_clock,
 	.set_memory_clock = &radeon_atom_set_memory_clock,
+	.get_pcie_lanes = NULL,
 	.set_pcie_lanes = NULL,
 	.set_clock_gating = NULL,
 	.set_surface_reg = r600_set_surface_reg,
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index bcee679..4998b24 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -1850,10 +1850,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
 		rdev->pm.power_state[state_index].default_clock_mode =
 			&rdev->pm.power_state[state_index].clock_info[0];
 		rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
-		if (rdev->asic->get_pcie_lanes)
-			rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = radeon_get_pcie_lanes(rdev);
-		else
-			rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
+		rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
 		rdev->pm.default_power_state_index = state_index;
 		rdev->pm.power_state[state_index].flags = 0;
 		state_index++;
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 4e23132..ee92615 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -2447,10 +2447,7 @@ default_mode:
 	rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
 	rdev->pm.power_state[state_index].default_clock_mode = &rdev->pm.power_state[state_index].clock_info[0];
 	rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
-	if (rdev->asic->get_pcie_lanes)
-		rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = radeon_get_pcie_lanes(rdev);
-	else
-		rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
+	rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
 	rdev->pm.power_state[state_index].flags = 0;
 	rdev->pm.default_power_state_index = state_index;
 	rdev->pm.num_power_states = state_index + 1;
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
index 1776e05..87dc5b4 100644
--- a/drivers/gpu/drm/radeon/radeon_reg.h
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
@@ -305,6 +305,7 @@
 
 /* #define RADEON_PCIE_INDEX                   0x0030 */
 /* #define RADEON_PCIE_DATA                    0x0034 */
+/* PCIE_LC_LINK_WIDTH_CNTL is PCIE on r1xx-r5xx, PCIE_PORT on r6xx-r7xx */
 #define RADEON_PCIE_LC_LINK_WIDTH_CNTL             0xa2 /* PCIE */
 #       define RADEON_PCIE_LC_LINK_WIDTH_SHIFT     0
 #       define RADEON_PCIE_LC_LINK_WIDTH_MASK      0x7
@@ -317,9 +318,14 @@
 #       define RADEON_PCIE_LC_LINK_WIDTH_X16       6
 #       define RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT  4
 #       define RADEON_PCIE_LC_LINK_WIDTH_RD_MASK   0x70
+#       define R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE   (1 << 7)
 #       define RADEON_PCIE_LC_RECONFIG_NOW         (1 << 8)
 #       define RADEON_PCIE_LC_RECONFIG_LATER       (1 << 9)
 #       define RADEON_PCIE_LC_SHORT_RECONFIG_EN    (1 << 10)
+#       define R600_PCIE_LC_RENEGOTIATE_EN         (1 << 10)
+#       define R600_PCIE_LC_SHORT_RECONFIG_EN      (1 << 11)
+#define R600_TARGET_AND_CURRENT_PROFILE_INDEX      0x70c
+#define R700_TARGET_AND_CURRENT_PROFILE_INDEX      0x66c
 
 #define RADEON_CACHE_CNTL                   0x1724
 #define RADEON_CACHE_LINE                   0x0f0c /* PCI */
-- 
1.5.6.3

