feat(imx8ulp): adjust the voltage when sys dvfs enabled

When system level DVFS is enabled, voltage can be changed to
optimize the power consumption.

Signed-off-by: Jacky Bai <ping.bai@nxp.com>
Reviewed-by: Ye Li <ye.li@nxp.com>
Change-Id: Idfa0e637402078f3daf6e7c4ea1abb9af7675494
diff --git a/plat/imx/imx8ulp/dram.c b/plat/imx/imx8ulp/dram.c
index 5beda7e..8a8ef44 100644
--- a/plat/imx/imx8ulp/dram.c
+++ b/plat/imx/imx8ulp/dram.c
@@ -141,6 +141,7 @@
 spinlock_t dfs_lock;
 static volatile uint32_t core_count;
 static volatile bool in_progress;
+static volatile bool sys_dvfs;
 static int num_fsp;
 
 static void ddr_init(void)
@@ -499,6 +500,8 @@
 #define DDR_DFS_GET_FSP_COUNT	0x10
 #define DDR_BYPASS_DRATE	U(400)
 
+extern int upower_pmic_i2c_write(uint32_t reg_addr, uint32_t reg_val);
+
 /* Normally, we only switch frequency between 1(bypass) and 2(highest) */
 int lpddr4_dfs(uint32_t freq_index)
 {
@@ -515,6 +518,14 @@
 		return -1;
 	}
 
+	/*
+	 * increase the voltage to 1.1V firstly before increase frequency
+	 * and APD enter OD mode
+	 */
+	if (freq_index == 2U && sys_dvfs) {
+		upower_pmic_i2c_write(0x22, 0x28);
+	}
+
 	/* Enable LPI_WAKEUP_EN */
 	ddr_ctl_144 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144);
 	mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, LPI_WAKEUP_EN);
@@ -563,6 +574,13 @@
 		return -1;
 	}
 
+	/* decrease the BUCK3 voltage after frequency changed to lower
+	 * and APD in ND_MODE
+	 */
+	if (freq_index == 1U && sys_dvfs) {
+		upower_pmic_i2c_write(0x22, 0x20);
+	}
+
 	/* DFS done successfully */
 	return 0;
 }
@@ -606,6 +624,7 @@
 
 	/* start lpddr frequency scaling */
 	in_progress = true;
+	sys_dvfs = x3 ? true : false;
 	dsb();
 
 	/* notify other core wait for scaling done */
diff --git a/plat/imx/imx8ulp/upower/upower_hal.c b/plat/imx/imx8ulp/upower/upower_hal.c
index 45b59c2..337857b 100644
--- a/plat/imx/imx8ulp/upower/upower_hal.c
+++ b/plat/imx/imx8ulp/upower/upower_hal.c
@@ -145,3 +145,57 @@
 
 	return 0;
 }
+
+int upower_pmic_i2c_write(uint32_t reg_addr, uint32_t reg_val)
+{
+	int ret, ret_val;
+	upwr_resp_t err_code;
+
+	ret = upwr_xcp_i2c_access(0x32, 1, 1, reg_addr, reg_val, NULL);
+	if (ret) {
+		WARN("pmic i2c read failed ret %d\n", ret);
+		return ret;
+	}
+
+	upower_wait_resp();
+	ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK) {
+		WARN("i2c poll Failure %d, err_code %d, ret_val 0x%x\n",
+		     ret, err_code, ret_val);
+		return ret;
+	}
+
+	VERBOSE("PMIC write reg[0x%x], val[0x%x]\n", reg_addr, reg_val);
+
+	return 0;
+}
+
+int upower_pmic_i2c_read(uint32_t reg_addr, uint32_t *reg_val)
+{
+	int ret, ret_val;
+	upwr_resp_t err_code;
+
+	if (reg_val == NULL) {
+		return -1;
+	}
+
+	ret = upwr_xcp_i2c_access(0x32, -1, 1, reg_addr, 0, NULL);
+	if (ret) {
+		WARN("pmic i2c read failed ret %d\n", ret);
+		return ret;
+	}
+
+	upower_wait_resp();
+	ret = upwr_poll_req_status(UPWR_SG_EXCEPT, NULL, &err_code, &ret_val, 1000);
+	if (ret != UPWR_REQ_OK) {
+		WARN("i2c poll Failure %d, err_code %d, ret_val 0x%x\n",
+			ret, err_code, ret_val);
+		return ret;
+	}
+
+	*reg_val = ret_val;
+
+	VERBOSE("PMIC read reg[0x%x], val[0x%x]\n", reg_addr, *reg_val);
+
+	return 0;
+}