From a43c033cd8bcfd0742d1f52847c09bef8280b5f3 Mon Sep 17 00:00:00 2001
From: Timothy Pearson <tpearson@raptorengineeringinc.com>
Date: Fri, 5 Jun 2015 21:13:30 -0500
Subject: [PATCH 045/143] northbridge/amd/amdfam10: Enable CC6 DRAM save area
 setup

Change-Id: Ibeb35da3395dc77a21a2f92f0e1d0845be53d175
Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
---
 src/northbridge/amd/amdfam10/northbridge.c  |   70 ++++++++++++++
 src/northbridge/amd/amdmct/mct_ddr3/mct_d.c |  132 +++++++++++++++++++++++++++
 2 files changed, 202 insertions(+)

diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
index fcf85a7..e5612fa 100644
--- a/src/northbridge/amd/amdfam10/northbridge.c
+++ b/src/northbridge/amd/amdfam10/northbridge.c
@@ -706,6 +706,8 @@ struct chip_operations northbridge_amd_amdfam10_ops = {
 static void amdfam10_domain_read_resources(device_t dev)
 {
 	unsigned reg;
+	uint8_t nvram;
+	uint8_t enable_cc6;
 
 	/* Find the already assigned resource pairs */
 	get_fx_devs();
@@ -749,6 +751,74 @@ static void amdfam10_domain_read_resources(device_t dev)
 	/* Reserve lower DRAM region to force PCI MMIO region to correct location above 0xefffffff */
 	ram_resource(dev, 7, 0, rdmsr(TOP_MEM).lo >> 10);
 #endif
+
+	if (is_fam15h()) {
+		enable_cc6 = 0;
+		if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
+			enable_cc6 = !!nvram;
+
+		if (enable_cc6) {
+			uint8_t node;
+			uint8_t interleaved;
+			int8_t range;
+			int8_t max_range;
+			uint8_t max_node;
+			uint64_t max_range_limit;
+			uint32_t dword;
+			uint32_t dword2;
+			uint64_t qword;
+			uint8_t num_nodes;
+
+			/* Find highest DRAM range (DramLimitAddr) */
+			max_node = 0;
+			max_range = -1;
+			interleaved = 0;
+			max_range_limit = 0;
+			for (range = 0; range < 8; range++) {
+				dword = f1_read_config32(0x40 + (range * 0x8));
+				if (!(dword & 0x3))
+					continue;
+
+				if ((dword >> 8) & 0x7)
+					interleaved = 1;
+
+				dword = f1_read_config32(0x44 + (range * 0x8));
+				dword2 = f1_read_config32(0x144 + (range * 0x8));
+				qword = ((((uint64_t)dword) >> 16) & 0xffff) << 24;
+				qword |= (((uint64_t)dword2) & 0xff) << 40;
+
+				if (qword > max_range_limit) {
+					max_range = range;
+					max_range_limit = qword;
+					max_node = dword & 0x7;
+				}
+			}
+
+			num_nodes = 0;
+			device_t node_dev;
+			for (node = 0; node < FX_DEVS; node++) {
+				node_dev = get_node_pci(node, 0);
+				/* Test for node presence */
+				if ((node_dev) && (pci_read_config32(node_dev, PCI_VENDOR_ID) != 0xffffffff))
+					num_nodes++;
+			}
+
+			/* Calculate CC6 sotrage area size */
+			if (interleaved)
+				qword = (0x1000000 * num_nodes);
+			else
+				qword = 0x1000000;
+
+			/* Reserve the CC6 save segment */
+			reserved_ram_resource(dev, 8, max_range_limit >> 10, qword >> 10);
+
+			/* Set up the C-state base address */
+			msr_t c_state_addr_msr;
+			c_state_addr_msr = rdmsr(0xc0010073);
+			c_state_addr_msr.lo = 0xe0e0;		/* CstateAddr = 0xe0e0 */
+			wrmsr(0xc0010073, c_state_addr_msr);
+		}
+	}
 }
 
 static u32 my_find_pci_tolm(struct bus *bus, u32 tolm)
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
index 20e66f2..2798506 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -1188,6 +1188,100 @@ static void compare_nvram_spd_hashes(struct MCTStatStruc *pMCTstat,
 }
 #endif
 
+static void set_up_cc6_storage_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t num_nodes)
+{
+	uint8_t interleaved;
+	uint8_t destination_node;
+	int8_t range;
+	int8_t max_range;
+	uint8_t max_node;
+	uint64_t max_range_limit;
+	uint32_t dword;
+	uint32_t dword2;
+	uint64_t qword;
+
+	interleaved = 0;
+	if (pMCTstat->GStatus & (1 << GSB_NodeIntlv))
+		interleaved = 1;
+
+	/* Find highest DRAM range (DramLimitAddr) */
+	max_node = 0;
+	max_range = -1;
+	max_range_limit = 0;
+	for (range = 0; range < 8; range++) {
+		dword = Get_NB32(pDCTstat->dev_map, 0x40 + (range * 0x8));
+		if (!(dword & 0x3))
+			continue;
+
+		dword = Get_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8));
+		dword2 = Get_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8));
+		qword = ((((uint64_t)dword) >> 16) & 0xffff) << 24;
+		qword |= (((uint64_t)dword2) & 0xff) << 40;
+
+		if (qword > max_range_limit) {
+			max_range = range;
+			max_range_limit = qword;
+			max_node = dword & 0x7;
+		}
+	}
+
+	if (pDCTstat->Node_ID == max_node) {
+		if (max_range >= 0) {
+			if (interleaved)
+				/* Move upper limit down by 16M * the number of nodes */
+				max_range_limit -= (0x1000000 * num_nodes);
+			else
+				/* Move upper limit down by 16M */
+				max_range_limit -= 0x1000000;
+
+			/* Store modified range */
+			dword = Get_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8));
+			dword &= ~(0xffff << 16);		/* DramLimit[39:24] = max_range_limit[39:24] */
+			dword |= (max_range_limit >> 24) & 0xffff;
+			Set_NB32(pDCTstat->dev_map, 0x44 + (range * 0x8), dword);
+
+			dword = Get_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8));
+			dword &= ~(0xffff << 16);		/* DramLimit[47:40] = max_range_limit[47:40] */
+			dword |= (max_range_limit >> 40) & 0xff;
+			Set_NB32(pDCTstat->dev_map, 0x144 + (range * 0x8), dword);
+		}
+	}
+
+	/* Determine save state destination node */
+	if (interleaved)
+		destination_node = Get_NB32(pDCTstat->dev_host, 0x60) & 0x7;
+	else
+		destination_node = max_node;
+
+	/* Set save state destination node */
+	dword = Get_NB32(pDCTstat->dev_link, 0x128);
+	dword &= ~(0x3f << 12);				/* CoreSaveStateDestNode = destination_node */
+	dword |= (destination_node & 0x3f) << 12;
+	Set_NB32(pDCTstat->dev_link, 0x128, dword);
+}
+
+static void lock_dram_config(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat)
+{
+	uint32_t dword;
+
+	dword = Get_NB32(pDCTstat->dev_dct, 0x118);
+	dword |= 0x1 << 19;		/* LockDramCfg = 1 */
+	Set_NB32(pDCTstat->dev_dct, 0x118, dword);
+}
+
+static void set_cc6_save_enable(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t enable)
+{
+	uint32_t dword;
+
+	dword = Get_NB32(pDCTstat->dev_dct, 0x118);
+	dword &= ~(0x1 << 18); 		/* CC6SaveEn = enable */
+	dword |= (enable & 0x1) << 18;
+	Set_NB32(pDCTstat->dev_dct, 0x118, dword);
+}
+
 static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstatA)
 {
@@ -1237,6 +1331,7 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
 	u8 Node, NodesWmem;
 	u32 node_sys_base;
 	uint8_t nvram;
+	uint8_t enable_cc6;
 	uint8_t allow_config_restore;
 
 	uint8_t s3resume = acpi_is_wakeup_s3();
@@ -1406,6 +1501,43 @@ restartinit:
 			mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
 		}
 
+		if (is_fam15h()) {
+			enable_cc6 = 0;
+			if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
+				enable_cc6 = !!nvram;
+
+			if (enable_cc6) {
+				uint8_t num_nodes;
+
+				num_nodes = 0;
+				for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+					struct DCTStatStruc *pDCTstat;
+					pDCTstat = pDCTstatA + Node;
+
+					if (pDCTstat->NodePresent)
+						num_nodes++;
+				}
+
+				for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+					struct DCTStatStruc *pDCTstat;
+					pDCTstat = pDCTstatA + Node;
+
+					if (pDCTstat->NodePresent)
+						set_up_cc6_storage_fam15(pMCTstat, pDCTstat, num_nodes);
+				}
+
+				for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+					struct DCTStatStruc *pDCTstat;
+					pDCTstat = pDCTstatA + Node;
+
+					if (pDCTstat->NodePresent) {
+						lock_dram_config(pMCTstat, pDCTstat);
+						set_cc6_save_enable(pMCTstat, pDCTstat, 1);
+					}
+				}
+			}
+		}
+
 		mct_FinalMCT_D(pMCTstat, pDCTstatA);
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
 	}
-- 
1.7.9.5

