haswell: Enable 2x Refresh mode

Send the PCODE mailbox command to enable 2x refresh mode if it
is not already enabled by MRC.

BUG=chrome-os-partner:35209
BRANCH=haswell
TEST=build and boot on peppy, check /sys/firmware/log for
successful report that 2x Refresh is enabled.  Ensure it is
also enabled after suspend/resume cycle.

Change-Id: I8473ace7302674eb79f537d431bc8bf84c4abeaa
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/240701
Reviewed-by: Shawn N <shawnn@chromium.org>
(cherry picked from commit 6b0827894d85556d7aa84cadd8c0f72311ab0716)
Reviewed-on: https://chromium-review.googlesource.com/252990
Commit-Queue: Steven Jian <steven.jian@intel.com>
Tested-by: Steven Jian <steven.jian@intel.com>
diff --git a/src/cpu/intel/haswell/haswell.h b/src/cpu/intel/haswell/haswell.h
index 9326140..fb464d0 100644
--- a/src/cpu/intel/haswell/haswell.h
+++ b/src/cpu/intel/haswell/haswell.h
@@ -136,6 +136,12 @@
 /* Data is passed through bits 31:0 of the data register. */
 #define BIOS_MAILBOX_DATA			0x5da0
 
+#define MAILBOX_BIOS_CMD_READ_DDR_FORCE_2X_REFRESH	0x17
+#define MAILBOX_BIOS_CMD_WRITE_DDR_FORCE_2X_REFRESH	0x18
+
+#define FORCE_2X_REFRESH_LOCK			(1 << 31)
+#define FORCE_2X_REFRESH_ENABLE			(1 << 0)
+
 /* Region of SMM space is reserved for multipurpose use. It falls below
  * the IED region and above the SMM handler. */
 #define RESERVED_SMM_SIZE CONFIG_SMM_RESERVED_SIZE
diff --git a/src/cpu/intel/haswell/haswell_init.c b/src/cpu/intel/haswell/haswell_init.c
index 3c4ce56..6546f23 100644
--- a/src/cpu/intel/haswell/haswell_init.c
+++ b/src/cpu/intel/haswell/haswell_init.c
@@ -304,6 +304,60 @@
 	return MCHBAR32(BIOS_MAILBOX_DATA);
 }
 
+static int pcode_mailbox_write(u32 command, u32 data)
+{
+	if (pcode_ready() < 0) {
+		printk(BIOS_ERR, "PCODE: mailbox timeout on wait ready.\n");
+		return -1;
+	}
+
+	MCHBAR32(BIOS_MAILBOX_DATA) = data;
+
+	/* Send command and start transaction */
+	MCHBAR32(BIOS_MAILBOX_INTERFACE) = command | MAILBOX_RUN_BUSY;
+
+	if (pcode_ready() < 0) {
+		printk(BIOS_ERR, "PCODE: mailbox timeout on completion.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void enable_2x_refresh(void)
+{
+	u32 read_command, write_command;
+	u32 value;
+
+	read_command = MAILBOX_BIOS_CMD_READ_DDR_FORCE_2X_REFRESH;
+	write_command = MAILBOX_BIOS_CMD_WRITE_DDR_FORCE_2X_REFRESH;
+
+	/* Read 2x Refresh register to see if it needs to be set */
+	value = pcode_mailbox_read(read_command);
+
+	if (value & FORCE_2X_REFRESH_ENABLE) {
+		printk(BIOS_INFO, "2x Refresh already enabled\n");
+		return;
+	} else if (value & FORCE_2X_REFRESH_LOCK) {
+		printk(BIOS_INFO, "2x Refresh register already locked\n");
+		return;
+	}
+
+	/* Enable 2x Refresh and lock */
+	value |= FORCE_2X_REFRESH_ENABLE | FORCE_2X_REFRESH_LOCK;
+	if (pcode_mailbox_write(write_command, value) < 0)
+		printk(BIOS_ERR, "Error writing 2x Refresh register");
+
+	/* Read back 2x Refresh and check if enabled */
+	value = pcode_mailbox_read(read_command);
+
+	if (value & FORCE_2X_REFRESH_ENABLE)
+		printk(BIOS_INFO, "2x Refresh Enabled: 0x%08x\n", value);
+	else
+		printk(BIOS_ERR, "ERROR: 2x Refresh NOT enabled: 0x%08x\n",
+		       value);
+}
+
 static void initialize_vr_config(void)
 {
 	msr_t msr;
@@ -729,6 +783,8 @@
 		configure_pch_power_sharing();
 	}
 
+	enable_2x_refresh();
+
 	/* Call through the cpu driver's initialization. */
 	cpu_initialize(0);
 }