siemens/mc_apl1: Fix accuracy issue with IDT PMIC

Due to an accuracy issue on IMON in the IDT PMIC, the reported system
power consumption was higher than the actual consumption. To prevent
this problem, a logic must be implemented in mainboard_init(). This logic
consists of slope and offset as constants for Vcc and Vnn, which need
to be programmed by coreboot. This fix compensates for the accuracy
issue.

Change-Id: I77faf95951d03ac6ce97a6721dba6e8466122a25
Signed-off-by: Mario Scheithauer <mario.scheithauer@siemens.com>
Reviewed-on: https://review.coreboot.org/25585
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
diff --git a/src/mainboard/siemens/mc_apl1/mainboard.c b/src/mainboard/siemens/mc_apl1/mainboard.c
index fd19cd1..18cb660 100644
--- a/src/mainboard/siemens/mc_apl1/mainboard.c
+++ b/src/mainboard/siemens/mc_apl1/mainboard.c
@@ -15,14 +15,17 @@
  */
 
 #include <console/console.h>
+#include <delay.h>
 #include <device/device.h>
 #include <device/pci.h>
 #include <device/pci_ids.h>
 #include <gpio.h>
 #include <hwilib.h>
 #include <i210.h>
+#include <intelblocks/cpulib.h>
 #include <intelblocks/lpc_lib.h>
 #include <intelblocks/pcr.h>
+#include <intelblocks/systemagent.h>
 #include <soc/pci_devs.h>
 #include <soc/pcr_ids.h>
 #include <string.h>
@@ -35,6 +38,11 @@
 #define MAX_PATH_DEPTH		12
 #define MAX_NUM_MAPPINGS	10
 
+#define BIOS_MAILBOX_WAIT_MAX_MS	1000
+#define BIOS_MAILBOX_DATA		0x7080
+#define BIOS_MAILBOX_INTERFACE		0x7084
+#define  RUN_BUSY_STS			(1 << 31)
+
 /** \brief This function can decide if a given MAC address is valid or not.
  *         Currently, addresses filled with 0xff or 0x00 are not valid.
  * @param  mac  Buffer to the MAC address to check
@@ -110,6 +118,77 @@
 	return CB_ERR;
 }
 
+/** \brief This function fixes an accuracy issue with IDT PMIC.
+ *         The current reported system power consumption is higher than the
+ *         actual consumption. With a correction of slope and offset for Vcc
+ *         and Vnn, the issue is solved.
+ */
+static void config_pmic_imon(void)
+{
+	struct stopwatch sw;
+	uint32_t power_max;
+
+	printk(BIOS_DEBUG, "PMIC: Configure PMIC IMON - Start\n");
+
+	/* Calculate CPU TDP in mW */
+	power_max = cpu_get_power_max();
+	printk(BIOS_INFO, "PMIC: CPU TDP %d mW.\n", power_max);
+
+	/*
+	 * Fix Vnn slope and offset value.
+	 * slope  = 0x4a4  # 2.32
+	 * offset = 0xfa0d # -2.975
+	 */
+	stopwatch_init_msecs_expire(&sw, BIOS_MAILBOX_WAIT_MAX_MS);
+	/* Read P_CR_BIOS_MAILBOX_INTERFACE_0_0_0_MCHBAR and check RUN_BUSY. */
+	while ((MCHBAR32(BIOS_MAILBOX_INTERFACE) & RUN_BUSY_STS)) {
+		if (stopwatch_expired(&sw)) {
+			printk(BIOS_ERR, "PMIC: Power consumption measurement "
+					"setup fails for Vnn.\n");
+			return;
+		}
+	}
+	/* Set Vnn values into P_CR_BIOS_MAILBOX_DATA_0_0_0_MCHBAR. */
+	MCHBAR32(BIOS_MAILBOX_DATA) = 0xfa0d04a4;
+	/* Set command, address and busy bit. */
+	MCHBAR32(BIOS_MAILBOX_INTERFACE) = 0x8000011d;
+	printk(BIOS_DEBUG, "PMIC: Fix Vnn slope and offset value.\n");
+
+	/*
+	 * Fix Vcc slope and offset value.
+	 * Premium and High SKU:
+	 * slope  = 0x466  # 2.2
+	 * offset = 0xe833 # -11.9
+	 * Low and Intermediate SKU:
+	 * slope  = 0x3b3  # 1.85
+	 * offset = 0xed33 # -9.4
+	 */
+	stopwatch_init_msecs_expire(&sw, BIOS_MAILBOX_WAIT_MAX_MS);
+	while ((MCHBAR32(BIOS_MAILBOX_INTERFACE) & RUN_BUSY_STS)) {
+		if (stopwatch_expired(&sw)) {
+			printk(BIOS_ERR, "PMIC: Power consumption measurement "
+					"setup fails for Vcc.\n");
+			return;
+		}
+	}
+
+	/*
+	 * CPU TDP limit between Premium/High and Low/Intermediate SKU
+	 * is 9010 mW.
+	 */
+	if (power_max > 9010) {
+		MCHBAR32(BIOS_MAILBOX_DATA) = 0xe8330466;
+		MCHBAR32(BIOS_MAILBOX_INTERFACE) = 0x8000001d;
+		printk(BIOS_INFO, "PMIC: Fix Vcc for Premium SKU.\n");
+	} else {
+		MCHBAR32(BIOS_MAILBOX_DATA) = 0xed3303b3;
+		MCHBAR32(BIOS_MAILBOX_INTERFACE) = 0x8000001d;
+		printk(BIOS_INFO, "PMIC: Fix Vcc for Low SKU.\n");
+	}
+
+	printk(BIOS_DEBUG, "PMIC: Configure PMIC IMON - End\n");
+}
+
 static void mainboard_init(void *chip_info)
 {
 	const struct pad_config *pads;
@@ -117,6 +196,8 @@
 
 	pads = brd_gpio_table(&num);
 	gpio_configure_pads(pads, num);
+
+	config_pmic_imon();
 }
 
 static void mainboard_final(void *chip_info)
@@ -125,7 +206,7 @@
 	uint16_t cmd = 0;
 	device_t dev = NULL;
 
-	/**
+	/*
 	 * Set up the DP2LVDS converter.
 	 * ptn3460_init() may only be executed after i2c bus init.
 	 */