Merge pull request #678 from soby-mathew/sm/PSCI_AArch32

Introduce AArch32 support for PSCI library
diff --git a/docs/user-guide.md b/docs/user-guide.md
index 8af8377..a6959b1 100644
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -1042,10 +1042,10 @@
 FVPs (64-bit versions only).
 
 *   `Foundation_Platform` (Version 9.5, Build 9.5.40)
-*   `FVP_Base_AEMv8A-AEMv8A` (Version 7.2, Build 0.8.7202)
-*   `FVP_Base_Cortex-A57x4-A53x4` (Version 7.2, Build 0.8.7202)
-*   `FVP_Base_Cortex-A57x1-A53x1` (Version 7.2, Build 0.8.7202)
-*   `FVP_Base_Cortex-A57x2-A53x4` (Version 7.2, Build 0.8.7202)
+*   `FVP_Base_AEMv8A-AEMv8A` (Version 7.6, Build 0.8.7605)
+*   `FVP_Base_Cortex-A57x4-A53x4` (Version 7.6, Build 0.8.7605)
+*   `FVP_Base_Cortex-A57x1-A53x1` (Version 7.6, Build 0.8.7605)
+*   `FVP_Base_Cortex-A57x2-A53x4` (Version 7.6, Build 0.8.7605)
 
 NOTE: The build numbers quoted above are those reported by launching the FVP
 with the `--version` parameter. `Foundation_Platform` tarball for `--version`
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index 8cb8020..d13e2c9 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -284,9 +284,10 @@
 	write_icc_igrpen0_el1(read_icc_igrpen0_el1() &
 			      ~IGRPEN1_EL1_ENABLE_G0_BIT);
 
-	/* Disable Group1 Secure interrupts */
+	/* Disable Group1 Secure and Non-Secure interrupts */
 	write_icc_igrpen1_el3(read_icc_igrpen1_el3() &
-			      ~IGRPEN1_EL3_ENABLE_G1S_BIT);
+			      ~(IGRPEN1_EL3_ENABLE_G1NS_BIT |
+			      IGRPEN1_EL3_ENABLE_G1S_BIT));
 
 	/* Synchronise accesses to group enable registers */
 	isb();
diff --git a/drivers/emmc/emmc.c b/drivers/emmc/emmc.c
index 5fe28ef..3fae2a1 100644
--- a/drivers/emmc/emmc.c
+++ b/drivers/emmc/emmc.c
@@ -40,6 +40,12 @@
 static const emmc_ops_t *ops;
 static unsigned int emmc_ocr_value;
 static emmc_csd_t emmc_csd;
+static unsigned int emmc_flags;
+
+static int is_cmd23_enabled(void)
+{
+	return (!!(emmc_flags & EMMC_FLAG_CMD23));
+}
 
 static int emmc_device_state(void)
 {
@@ -174,11 +180,23 @@
 	ret = ops->prepare(lba, buf, size);
 	assert(ret == 0);
 
-	memset(&cmd, 0, sizeof(emmc_cmd_t));
-	if (size > EMMC_BLOCK_SIZE)
+	if (is_cmd23_enabled()) {
+		memset(&cmd, 0, sizeof(emmc_cmd_t));
+		/* set block count */
+		cmd.cmd_idx = EMMC_CMD23;
+		cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
+		cmd.resp_type = EMMC_RESPONSE_R1;
+		ret = ops->send_cmd(&cmd);
+		assert(ret == 0);
+
+		memset(&cmd, 0, sizeof(emmc_cmd_t));
 		cmd.cmd_idx = EMMC_CMD18;
-	else
-		cmd.cmd_idx = EMMC_CMD17;
+	} else {
+		if (size > EMMC_BLOCK_SIZE)
+			cmd.cmd_idx = EMMC_CMD18;
+		else
+			cmd.cmd_idx = EMMC_CMD17;
+	}
 	if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
 		cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
 	else
@@ -193,11 +211,13 @@
 	/* wait buffer empty */
 	emmc_device_state();
 
-	if (size > EMMC_BLOCK_SIZE) {
-		memset(&cmd, 0, sizeof(emmc_cmd_t));
-		cmd.cmd_idx = EMMC_CMD12;
-		ret = ops->send_cmd(&cmd);
-		assert(ret == 0);
+	if (is_cmd23_enabled() == 0) {
+		if (size > EMMC_BLOCK_SIZE) {
+			memset(&cmd, 0, sizeof(emmc_cmd_t));
+			cmd.cmd_idx = EMMC_CMD12;
+			ret = ops->send_cmd(&cmd);
+			assert(ret == 0);
+		}
 	}
 	/* Ignore improbable errors in release builds */
 	(void)ret;
@@ -218,11 +238,24 @@
 	ret = ops->prepare(lba, buf, size);
 	assert(ret == 0);
 
-	memset(&cmd, 0, sizeof(emmc_cmd_t));
-	if (size > EMMC_BLOCK_SIZE)
+	if (is_cmd23_enabled()) {
+		/* set block count */
+		memset(&cmd, 0, sizeof(emmc_cmd_t));
+		cmd.cmd_idx = EMMC_CMD23;
+		cmd.cmd_arg = size / EMMC_BLOCK_SIZE;
+		cmd.resp_type = EMMC_RESPONSE_R1;
+		ret = ops->send_cmd(&cmd);
+		assert(ret == 0);
+
+		memset(&cmd, 0, sizeof(emmc_cmd_t));
 		cmd.cmd_idx = EMMC_CMD25;
-	else
-		cmd.cmd_idx = EMMC_CMD24;
+	} else {
+		memset(&cmd, 0, sizeof(emmc_cmd_t));
+		if (size > EMMC_BLOCK_SIZE)
+			cmd.cmd_idx = EMMC_CMD25;
+		else
+			cmd.cmd_idx = EMMC_CMD24;
+	}
 	if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
 		cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
 	else
@@ -237,11 +270,13 @@
 	/* wait buffer empty */
 	emmc_device_state();
 
-	if (size > EMMC_BLOCK_SIZE) {
-		memset(&cmd, 0, sizeof(emmc_cmd_t));
-		cmd.cmd_idx = EMMC_CMD12;
-		ret = ops->send_cmd(&cmd);
-		assert(ret == 0);
+	if (is_cmd23_enabled() == 0) {
+		if (size > EMMC_BLOCK_SIZE) {
+			memset(&cmd, 0, sizeof(emmc_cmd_t));
+			cmd.cmd_idx = EMMC_CMD12;
+			ret = ops->send_cmd(&cmd);
+			assert(ret == 0);
+		}
 	}
 	/* Ignore improbable errors in release builds */
 	(void)ret;
@@ -328,7 +363,8 @@
 	return size_erased;
 }
 
-void emmc_init(const emmc_ops_t *ops_ptr, int clk, int width)
+void emmc_init(const emmc_ops_t *ops_ptr, int clk, int width,
+	       unsigned int flags)
 {
 	assert((ops_ptr != 0) &&
 	       (ops_ptr->init != 0) &&
@@ -342,6 +378,7 @@
 		(width == EMMC_BUS_WIDTH_4) ||
 		(width == EMMC_BUS_WIDTH_8)));
 	ops = ops_ptr;
+	emmc_flags = flags;
 
 	emmc_enumerate(clk, width);
 }
diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c
index 198b723..4ec59bc 100644
--- a/drivers/io/io_block.c
+++ b/drivers/io/io_block.c
@@ -198,6 +198,7 @@
 	io_block_ops_t *ops;
 	size_t aligned_length, skip, count, left, padding, block_size;
 	int lba;
+	int buffer_not_aligned;
 
 	assert(entity->info != (uintptr_t)NULL);
 	cur = (block_dev_state_t *)entity->info;
@@ -208,6 +209,17 @@
 	       (length > 0) &&
 	       (ops->read != 0));
 
+	if ((buffer & (block_size - 1)) != 0) {
+		/*
+		 * buffer isn't aligned with block size.
+		 * Block device always relies on DMA operation.
+		 * It's better to make the buffer as block size aligned.
+		 */
+		buffer_not_aligned = 1;
+	} else {
+		buffer_not_aligned = 0;
+	}
+
 	skip = cur->file_pos % block_size;
 	aligned_length = ((skip + length) + (block_size - 1)) &
 			 ~(block_size - 1);
@@ -216,8 +228,13 @@
 	do {
 		lba = (cur->file_pos + cur->base) / block_size;
 		if (left >= buf->length) {
-			/* Since left is larger, it's impossible to padding. */
-			if (skip) {
+			/*
+			 * Since left is larger, it's impossible to padding.
+			 *
+			 * If buffer isn't aligned, we need to use aligned
+			 * buffer instead.
+			 */
+			if (skip || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
 				 * aligned with block size, we need to use
@@ -231,10 +248,11 @@
 			}
 			assert(count == buf->length);
 			cur->file_pos += count - skip;
-			if (skip) {
+			if (skip || buffer_not_aligned) {
 				/*
-				 * Since it's not aligned with block size,
-				 * block buffer is used to store data.
+				 * Since there's not aligned block size caused
+				 * by skip or not aligned buffer, block buffer
+				 * is used to store data.
 				 */
 				memcpy((void *)buffer,
 				       (void *)(buf->offset + skip),
@@ -242,13 +260,16 @@
 			}
 			left = left - (count - skip);
 		} else {
-			if (skip || padding) {
+			if (skip || padding || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
 				 * aligned with block size, we have to read
 				 * full block by block buffer instead.
 				 * The size isn't aligned with block size.
 				 * Use block buffer to avoid overflow.
+				 *
+				 * If buffer isn't aligned, use block buffer
+				 * to avoid DMA error.
 				 */
 				count = ops->read(lba, buf->offset, left);
 			} else
@@ -256,10 +277,10 @@
 			assert(count == left);
 			left = left - (skip + padding);
 			cur->file_pos += left;
-			if (skip || padding) {
+			if (skip || padding || buffer_not_aligned) {
 				/*
-				 * Since it's not aligned with block size,
-				 * block buffer is used to store data.
+				 * Since there's not aligned block size or
+				 * buffer, block buffer is used to store data.
 				 */
 				memcpy((void *)buffer,
 				       (void *)(buf->offset + skip),
@@ -283,6 +304,7 @@
 	io_block_ops_t *ops;
 	size_t aligned_length, skip, count, left, padding, block_size;
 	int lba;
+	int buffer_not_aligned;
 
 	assert(entity->info != (uintptr_t)NULL);
 	cur = (block_dev_state_t *)entity->info;
@@ -294,6 +316,17 @@
 	       (ops->read != 0) &&
 	       (ops->write != 0));
 
+	if ((buffer & (block_size - 1)) != 0) {
+		/*
+		 * buffer isn't aligned with block size.
+		 * Block device always relies on DMA operation.
+		 * It's better to make the buffer as block size aligned.
+		 */
+		buffer_not_aligned = 1;
+	} else {
+		buffer_not_aligned = 0;
+	}
+
 	skip = cur->file_pos % block_size;
 	aligned_length = ((skip + length) + (block_size - 1)) &
 			 ~(block_size - 1);
@@ -303,12 +336,12 @@
 		lba = (cur->file_pos + cur->base) / block_size;
 		if (left >= buf->length) {
 			/* Since left is larger, it's impossible to padding. */
-			if (skip) {
+			if (skip || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
-				 * aligned with block size, we need to use
-				 * block buffer to write block. Since block
-				 * device is always relied on DMA operation.
+				 * aligned with block size or buffer isn't
+				 * aligned, we need to use block buffer to
+				 * write block.
 				 */
 				count = ops->read(lba, buf->offset,
 						  buf->length);
@@ -324,7 +357,7 @@
 			cur->file_pos += count - skip;
 			left = left - (count - skip);
 		} else {
-			if (skip || padding) {
+			if (skip || padding || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
 				 * aligned with block size, we need to avoid
@@ -332,6 +365,9 @@
 				 * skipping the beginning is the only way.
 				 * The size isn't aligned with block size.
 				 * Use block buffer to avoid overflow.
+				 *
+				 * If buffer isn't aligned, use block buffer
+				 * to avoid DMA error.
 				 */
 				count = ops->read(lba, buf->offset, left);
 				assert(count == left);
diff --git a/include/drivers/emmc.h b/include/drivers/emmc.h
index 61d4495..5f78dce 100644
--- a/include/drivers/emmc.h
+++ b/include/drivers/emmc.h
@@ -49,6 +49,7 @@
 #define EMMC_CMD13			13
 #define EMMC_CMD17			17
 #define EMMC_CMD18			18
+#define EMMC_CMD23			23
 #define EMMC_CMD24			24
 #define EMMC_CMD25			25
 #define EMMC_CMD35			35
@@ -111,6 +112,8 @@
 #define EMMC_STATE_BTST			9
 #define EMMC_STATE_SLP			10
 
+#define EMMC_FLAG_CMD23			(1 << 0)
+
 typedef struct emmc_cmd {
 	unsigned int	cmd_idx;
 	unsigned int	cmd_arg;
@@ -177,6 +180,7 @@
 size_t emmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size);
 size_t emmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size);
 size_t emmc_rpmb_erase_blocks(int lba, size_t size);
-void emmc_init(const emmc_ops_t *ops, int clk, int bus_width);
+void emmc_init(const emmc_ops_t *ops, int clk, int bus_width,
+	       unsigned int flags);
 
 #endif	/* __EMMC_H__ */
diff --git a/plat/rockchip/common/pmusram/pmu_sram_cpus_on.S b/plat/rockchip/common/pmusram/pmu_sram_cpus_on.S
index 3378272..9f94b0c 100644
--- a/plat/rockchip/common/pmusram/pmu_sram_cpus_on.S
+++ b/plat/rockchip/common/pmusram/pmu_sram_cpus_on.S
@@ -70,9 +70,7 @@
 psram_data:
 	.quad	PSRAM_DT_BASE
 sys_wakeup_entry:
-#if !ERROR_DEPRECATED
 	.quad	psci_entrypoint
-#endif
 pmu_cpuson_entrypoint_end:
 	.word	0
 endfunc pmu_cpuson_entrypoint
diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.c b/plat/rockchip/rk3399/drivers/pmu/pmu.c
index 72d71bf..01f84e9 100644
--- a/plat/rockchip/rk3399/drivers/pmu/pmu.c
+++ b/plat/rockchip/rk3399/drivers/pmu/pmu.c
@@ -45,6 +45,8 @@
 #include <soc.h>
 #include <pmu.h>
 #include <pmu_com.h>
+#include <pwm.h>
+#include <soc.h>
 
 DEFINE_BAKERY_LOCK(rockchip_pd_lock);
 
@@ -775,6 +777,36 @@
 	mmio_setbits_32(PMU_BASE + PMU_WKUP_CFG4, BIT(PMU_GPIO_WKUP_EN));
 	mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, slp_mode_cfg);
 
+	/*
+	 * About to switch PMU counters to 32K; switch all timings to 32K
+	 * for simplicity even if we don't plan on using them.
+	 */
+	mmio_write_32(PMU_BASE + PMU_SCU_L_PWRDN_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_SCU_L_PWRUP_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_SCU_B_PWRDN_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_SCU_B_PWRUP_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_CENTER_PWRDN_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_CENTER_PWRUP_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_WAKEUP_RST_CLR_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_OSC_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_DDRIO_PWRON_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_PLLLOCK_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_PLLRST_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_STABLE_CNT, CYCL_32K_CNT_MS(3));
+
+	mmio_clrbits_32(PMU_BASE + PMU_SFT_CON, BIT(PMU_24M_EN_CFG));
+
+	mmio_write_32(PMU_BASE + PMU_PLL_CON, PLL_PD_HW);
+	mmio_write_32(PMUGRF_BASE + PMUGRF_SOC_CON0, EXTERNAL_32K);
+	mmio_write_32(PMUGRF_BASE, IOMUX_CLK_32K); /* 32k iomux */
+}
+
+static void sys_slp_unconfig(void)
+{
+	/*
+	 * About to switch PMU counters to 24M; switch all timings to 24M
+	 * for simplicity even if we don't plan on using them.
+	 */
 	mmio_write_32(PMU_BASE + PMU_SCU_L_PWRDN_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_SCU_L_PWRUP_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_SCU_B_PWRDN_CNT, CYCL_24M_CNT_MS(3));
@@ -782,16 +814,13 @@
 	mmio_write_32(PMU_BASE + PMU_CENTER_PWRDN_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_CENTER_PWRUP_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_WAKEUP_RST_CLR_CNT, CYCL_24M_CNT_MS(3));
-	mmio_write_32(PMU_BASE + PMU_OSC_CNT, CYCL_32K_CNT_MS(3));
+	mmio_write_32(PMU_BASE + PMU_OSC_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_DDRIO_PWRON_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_PLLLOCK_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_PLLRST_CNT, CYCL_24M_CNT_MS(3));
 	mmio_write_32(PMU_BASE + PMU_STABLE_CNT, CYCL_24M_CNT_MS(3));
-	mmio_clrbits_32(PMU_BASE + PMU_SFT_CON, BIT(PMU_24M_EN_CFG));
 
-	mmio_write_32(PMU_BASE + PMU_PLL_CON, PLL_PD_HW);
-	mmio_write_32(PMUGRF_BASE + PMUGRF_SOC_CON0, EXTERNAL_32K);
-	mmio_write_32(PMUGRF_BASE, IOMUX_CLK_32K); /*32k iomux*/
+	mmio_setbits_32(PMU_BASE + PMU_SFT_CON, BIT(PMU_24M_EN_CFG));
 }
 
 static void set_hw_idle(uint32_t hw_idle)
@@ -804,72 +833,6 @@
 	mmio_clrbits_32(PMU_BASE + PMU_BUS_CLR, hw_idle);
 }
 
-struct pwm_data_s pwm_data;
-
-/*
- * Save the PWMs data.
- */
-static void save_pwms(void)
-{
-	uint32_t i;
-
-	pwm_data.iomux_bitmask = 0;
-
-	/* Save all IOMUXes */
-	if (mmio_read_32(GRF_BASE + GRF_GPIO4C_IOMUX) & GPIO4C2_IOMUX_PWM)
-		pwm_data.iomux_bitmask |= PWM0_IOMUX_PWM_EN;
-	if (mmio_read_32(GRF_BASE + GRF_GPIO4C_IOMUX) & GPIO4C6_IOMUX_PWM)
-		pwm_data.iomux_bitmask |= PWM1_IOMUX_PWM_EN;
-	if (mmio_read_32(PMUGRF_BASE + PMUGRF_GPIO1C_IOMUX) &
-			 GPIO1C3_IOMUX_PWM)
-		pwm_data.iomux_bitmask |= PWM2_IOMUX_PWM_EN;
-	if (mmio_read_32(PMUGRF_BASE + PMUGRF_GPIO0A_IOMUX) &
-			 GPIO0A6_IOMUX_PWM)
-		pwm_data.iomux_bitmask |= PWM3_IOMUX_PWM_EN;
-
-	for (i = 0; i < 4; i++) {
-		/* Save cnt, period, duty and ctrl for PWM i */
-		pwm_data.cnt[i] = mmio_read_32(PWM_BASE + PWM_CNT(i));
-		pwm_data.duty[i] = mmio_read_32(PWM_BASE + PWM_PERIOD_HPR(i));
-		pwm_data.period[i] = mmio_read_32(PWM_BASE + PWM_DUTY_LPR(i));
-		pwm_data.ctrl[i] = mmio_read_32(PWM_BASE + PWM_CTRL(i));
-	}
-
-	/* PWMs all IOMUXes switch to the gpio mode */
-	mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, GPIO4C2_IOMUX_GPIO);
-	mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, GPIO4C6_IOMUX_GPIO);
-	mmio_write_32(PMUGRF_BASE  + PMUGRF_GPIO1C_IOMUX, GPIO1C3_IOMUX_GPIO);
-	mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO0A_IOMUX, GPIO0A6_IOMUX_GPIO);
-}
-
-/*
- * Restore the PWMs data.
- */
-static void restore_pwms(void)
-{
-	uint32_t i;
-
-	/* Restore all IOMUXes */
-	if (pwm_data.iomux_bitmask & PWM3_IOMUX_PWM_EN)
-		mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO0A_IOMUX,
-			      GPIO0A6_IOMUX_PWM);
-	if (pwm_data.iomux_bitmask & PWM2_IOMUX_PWM_EN)
-		mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO1C_IOMUX,
-			      GPIO1C3_IOMUX_PWM);
-	if (pwm_data.iomux_bitmask & PWM1_IOMUX_PWM_EN)
-		mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, GPIO4C6_IOMUX_PWM);
-	if (pwm_data.iomux_bitmask & PWM0_IOMUX_PWM_EN)
-		mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, GPIO4C2_IOMUX_PWM);
-
-	for (i = 0; i < 4; i++) {
-		/* Restore ctrl, duty, period and cnt for PWM i */
-		mmio_write_32(PWM_BASE + PWM_CTRL(i), pwm_data.ctrl[i]);
-		mmio_write_32(PWM_BASE + PWM_DUTY_LPR(i), pwm_data.period[i]);
-		mmio_write_32(PWM_BASE + PWM_PERIOD_HPR(i), pwm_data.duty[i]);
-		mmio_write_32(PWM_BASE + PWM_CNT(i), pwm_data.cnt[i]);
-	}
-}
-
 static int sys_pwr_domain_suspend(void)
 {
 	uint32_t wait_cnt = 0;
@@ -916,7 +879,10 @@
 	}
 	mmio_setbits_32(PMU_BASE + PMU_PWRDN_CON, BIT(PMU_SCU_B_PWRDWN_EN));
 
-	save_pwms();
+	plls_suspend_prepare();
+	disable_dvfs_plls();
+	disable_pwms();
+	disable_nodvfs_plls();
 
 	return 0;
 }
@@ -926,9 +892,14 @@
 	uint32_t wait_cnt = 0;
 	uint32_t status = 0;
 
-	restore_pwms();
+	enable_nodvfs_plls();
+	enable_pwms();
+	/* PWM regulators take time to come up; give 300us to be safe. */
+	udelay(300);
+	enable_dvfs_plls();
+	plls_resume_finish();
 
-	pmu_sgrf_rst_hld();
+	sys_slp_unconfig();
 
 	mmio_write_32(SGRF_BASE + SGRF_SOC_CON0_1(1),
 		      (cpu_warm_boot_addr >> CPU_BOOT_ADDR_ALIGN) |
@@ -964,6 +935,7 @@
 		}
 	}
 
+	pmu_sgrf_rst_hld_release();
 	pmu_scu_b_pwrup();
 
 	pmu_power_domains_resume();
diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.h b/plat/rockchip/rk3399/drivers/pmu/pmu.h
index ddb1c16..c821efc 100644
--- a/plat/rockchip/rk3399/drivers/pmu/pmu.h
+++ b/plat/rockchip/rk3399/drivers/pmu/pmu.h
@@ -812,16 +812,13 @@
 #define PMUGRF_GPIO1A_IOMUX	0x10
 #define PMUGRF_GPIO1C_IOMUX	0x18
 
+#define PMUGRF_GPIO0A6_IOMUX_SHIFT      12
+#define PMUGRF_GPIO0A6_IOMUX_PWM        0x1
+#define PMUGRF_GPIO1C3_IOMUX_SHIFT      6
+#define PMUGRF_GPIO1C3_IOMUX_PWM        0x1
+
 #define AP_PWROFF		0x0a
 
-#define GPIO0A6_IOMUX_GPIO	BITS_WITH_WMASK(0, 3, 12)
-#define GPIO0A6_IOMUX_PWM	BITS_WITH_WMASK(1, 3, 12)
-#define GPIO1C3_IOMUX_GPIO	BITS_WITH_WMASK(0, 3, 6)
-#define GPIO1C3_IOMUX_PWM	BITS_WITH_WMASK(1, 3, 6)
-#define GPIO4C2_IOMUX_GPIO	BITS_WITH_WMASK(0, 3, 4)
-#define GPIO4C2_IOMUX_PWM	BITS_WITH_WMASK(1, 3, 4)
-#define GPIO4C6_IOMUX_GPIO	BITS_WITH_WMASK(0, 3, 12)
-#define GPIO4C6_IOMUX_PWM	BITS_WITH_WMASK(1, 3, 12)
 #define GPIO1A6_IOMUX		BITS_WITH_WMASK(0, 3, 12)
 
 #define TSADC_INT_PIN		38
@@ -913,15 +910,6 @@
 	mmio_write_32(base + CPU_AXI_QOS_EXTCONTROL, array[6]); \
 } while (0)
 
-/* there are 4 PWMs on rk3399 */
-struct pwm_data_s {
-	uint32_t iomux_bitmask;
-	uint64_t cnt[4];
-	uint64_t duty[4];
-	uint64_t period[4];
-	uint64_t ctrl[4];
-};
-
 struct pmu_slpdata_s {
 	uint32_t cci_m0_qos[CPU_AXI_QOS_NUM_REGS];
 	uint32_t cci_m1_qos[CPU_AXI_QOS_NUM_REGS];
diff --git a/plat/rockchip/rk3399/drivers/pwm/pwm.c b/plat/rockchip/rk3399/drivers/pwm/pwm.c
new file mode 100644
index 0000000..7845c63
--- /dev/null
+++ b/plat/rockchip/rk3399/drivers/pwm/pwm.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <plat_private.h>
+#include <pmu.h>
+#include <pwm.h>
+#include <soc.h>
+
+#define PWM0_IOMUX_PWM_EN		(1 << 0)
+#define PWM1_IOMUX_PWM_EN		(1 << 1)
+#define PWM2_IOMUX_PWM_EN		(1 << 2)
+#define PWM3_IOMUX_PWM_EN		(1 << 3)
+
+struct pwm_data_s {
+	uint32_t iomux_bitmask;
+	uint32_t enable_bitmask;
+};
+
+static struct pwm_data_s pwm_data;
+
+/*
+ * Disable the PWMs.
+ */
+void disable_pwms(void)
+{
+	uint32_t i, val;
+
+	pwm_data.iomux_bitmask = 0;
+
+	/* Save PWMs pinmux and change PWMs pinmux to GPIOs */
+	val = mmio_read_32(GRF_BASE + GRF_GPIO4C_IOMUX);
+	if (((val >> GRF_GPIO4C2_IOMUX_SHIFT) &
+		GRF_IOMUX_2BIT_MASK) == GRF_GPIO4C2_IOMUX_PWM) {
+		pwm_data.iomux_bitmask |= PWM0_IOMUX_PWM_EN;
+		val = BITS_WITH_WMASK(GRF_IOMUX_GPIO, GRF_IOMUX_2BIT_MASK,
+				    GRF_GPIO4C2_IOMUX_SHIFT);
+		mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, val);
+	}
+
+	val = mmio_read_32(GRF_BASE + GRF_GPIO4C_IOMUX);
+	if (((val >> GRF_GPIO4C6_IOMUX_SHIFT) &
+		GRF_IOMUX_2BIT_MASK) == GRF_GPIO4C6_IOMUX_PWM) {
+		pwm_data.iomux_bitmask |= PWM1_IOMUX_PWM_EN;
+		val = BITS_WITH_WMASK(GRF_IOMUX_GPIO, GRF_IOMUX_2BIT_MASK,
+				    GRF_GPIO4C6_IOMUX_SHIFT);
+		mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, val);
+	}
+
+	val = mmio_read_32(PMUGRF_BASE + PMUGRF_GPIO1C_IOMUX);
+	if (((val >> PMUGRF_GPIO1C3_IOMUX_SHIFT) &
+		GRF_IOMUX_2BIT_MASK) == PMUGRF_GPIO1C3_IOMUX_PWM) {
+		pwm_data.iomux_bitmask |= PWM2_IOMUX_PWM_EN;
+		val = BITS_WITH_WMASK(GRF_IOMUX_GPIO, GRF_IOMUX_2BIT_MASK,
+				    PMUGRF_GPIO1C3_IOMUX_SHIFT);
+		mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO1C_IOMUX, val);
+	}
+
+	val = mmio_read_32(PMUGRF_BASE + PMUGRF_GPIO0A_IOMUX);
+	if (((val >> PMUGRF_GPIO0A6_IOMUX_SHIFT) &
+		GRF_IOMUX_2BIT_MASK) == PMUGRF_GPIO0A6_IOMUX_PWM) {
+		pwm_data.iomux_bitmask |= PWM3_IOMUX_PWM_EN;
+		val = BITS_WITH_WMASK(GRF_IOMUX_GPIO, GRF_IOMUX_2BIT_MASK,
+				    PMUGRF_GPIO0A6_IOMUX_SHIFT);
+		mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO0A_IOMUX, val);
+	}
+
+	/* Disable the pwm channel */
+	pwm_data.enable_bitmask = 0;
+	for (i = 0; i < 4; i++) {
+		val = mmio_read_32(PWM_BASE + PWM_CTRL(i));
+		if ((val & PWM_ENABLE) != PWM_ENABLE)
+			continue;
+		pwm_data.enable_bitmask |= (1 << i);
+		mmio_write_32(PWM_BASE + PWM_CTRL(i), val & ~PWM_ENABLE);
+	}
+}
+
+/*
+ * Enable the PWMs.
+ */
+void enable_pwms(void)
+{
+	uint32_t i, val;
+
+	for (i = 0; i < 4; i++) {
+		val = mmio_read_32(PWM_BASE + PWM_CTRL(i));
+		if (!(pwm_data.enable_bitmask & (1 << i)))
+			continue;
+		mmio_write_32(PWM_BASE + PWM_CTRL(i), val | PWM_ENABLE);
+	}
+
+	/* Restore all IOMUXes */
+	if (pwm_data.iomux_bitmask & PWM3_IOMUX_PWM_EN) {
+		val = BITS_WITH_WMASK(PMUGRF_GPIO0A6_IOMUX_PWM,
+				    GRF_IOMUX_2BIT_MASK,
+				    PMUGRF_GPIO0A6_IOMUX_SHIFT);
+		mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO0A_IOMUX, val);
+	}
+
+	if (pwm_data.iomux_bitmask & PWM2_IOMUX_PWM_EN) {
+		val = BITS_WITH_WMASK(PMUGRF_GPIO1C3_IOMUX_PWM,
+				    GRF_IOMUX_2BIT_MASK,
+				    PMUGRF_GPIO1C3_IOMUX_SHIFT);
+		mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO1C_IOMUX, val);
+	}
+
+	if (pwm_data.iomux_bitmask & PWM1_IOMUX_PWM_EN) {
+		val = BITS_WITH_WMASK(GRF_GPIO4C6_IOMUX_PWM,
+				    GRF_IOMUX_2BIT_MASK,
+				    GRF_GPIO4C6_IOMUX_SHIFT);
+		mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, val);
+	}
+
+	if (pwm_data.iomux_bitmask & PWM0_IOMUX_PWM_EN) {
+		val = BITS_WITH_WMASK(GRF_GPIO4C2_IOMUX_PWM,
+				    GRF_IOMUX_2BIT_MASK,
+				    GRF_GPIO4C2_IOMUX_SHIFT);
+		mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, val);
+	}
+}
diff --git a/plat/rockchip/rk3399/drivers/pwm/pwm.h b/plat/rockchip/rk3399/drivers/pwm/pwm.h
new file mode 100644
index 0000000..0f75a8d
--- /dev/null
+++ b/plat/rockchip/rk3399/drivers/pwm/pwm.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PWM_H__
+#define __PWM_H__
+
+void disable_pwms(void);
+void enable_pwms(void);
+
+#endif
diff --git a/plat/rockchip/rk3399/drivers/soc/soc.c b/plat/rockchip/rk3399/drivers/soc/soc.c
index 9ccf90c..29bf6dd 100644
--- a/plat/rockchip/rk3399/drivers/soc/soc.c
+++ b/plat/rockchip/rk3399/drivers/soc/soc.c
@@ -236,7 +236,22 @@
 	set_pll_bypass(pll_id);
 }
 
-void plls_suspend(void)
+void disable_dvfs_plls(void)
+{
+	_pll_suspend(CPLL_ID);
+	_pll_suspend(NPLL_ID);
+	_pll_suspend(VPLL_ID);
+	_pll_suspend(GPLL_ID);
+	_pll_suspend(ABPLL_ID);
+	_pll_suspend(ALPLL_ID);
+}
+
+void disable_nodvfs_plls(void)
+{
+	_pll_suspend(PPLL_ID);
+}
+
+void plls_suspend_prepare(void)
 {
 	uint32_t i, pll_id;
 
@@ -251,14 +266,6 @@
 		slp_data.pmucru_clksel_con[i] =
 			mmio_read_32(PMUCRU_BASE +
 				     PMUCRU_CLKSEL_OFFSET + i * REG_SIZE);
-
-	_pll_suspend(CPLL_ID);
-	_pll_suspend(NPLL_ID);
-	_pll_suspend(VPLL_ID);
-	_pll_suspend(PPLL_ID);
-	_pll_suspend(GPLL_ID);
-	_pll_suspend(ABPLL_ID);
-	_pll_suspend(ALPLL_ID);
 }
 
 void clk_gate_con_save(void)
@@ -308,7 +315,13 @@
 			      PLL_NO_BYPASS_MODE);
 }
 
+static void _pll_resume(uint32_t pll_id)
+{
+	set_plls_nobypass(pll_id);
+	set_pll_normal_mode(pll_id);
+}
+
-static void plls_resume_prepare(void)
+void plls_resume_finish(void)
 {
 	int i;
 
@@ -321,15 +334,19 @@
 			      REG_SOC_WMSK | slp_data.pmucru_clksel_con[i]);
 }
 
-void plls_resume(void)
+void enable_dvfs_plls(void)
 {
-	int pll_id;
+	_pll_resume(ALPLL_ID);
+	_pll_resume(ABPLL_ID);
+	_pll_resume(GPLL_ID);
+	_pll_resume(VPLL_ID);
+	_pll_resume(NPLL_ID);
+	_pll_resume(CPLL_ID);
+}
 
-	plls_resume_prepare();
-	for (pll_id = ALPLL_ID; pll_id < END_PLL_ID; pll_id++) {
-		set_plls_nobypass(pll_id);
-		set_pll_normal_mode(pll_id);
-	}
+void enable_nodvfs_plls(void)
+{
+	_pll_resume(PPLL_ID);
 }
 
 void soc_global_soft_reset_init(void)
diff --git a/plat/rockchip/rk3399/drivers/soc/soc.h b/plat/rockchip/rk3399/drivers/soc/soc.h
index 26c0df6..1ea6e5e 100644
--- a/plat/rockchip/rk3399/drivers/soc/soc.h
+++ b/plat/rockchip/rk3399/drivers/soc/soc.h
@@ -240,6 +240,22 @@
 #define CPU_BOOT_ADDR_WMASK	0xffff0000
 #define CPU_BOOT_ADDR_ALIGN	16
 
+#define GRF_IOMUX_2BIT_MASK     0x3
+#define GRF_IOMUX_GPIO          0x0
+
+#define GRF_GPIO4C2_IOMUX_SHIFT         4
+#define GRF_GPIO4C2_IOMUX_PWM           0x1
+#define GRF_GPIO4C6_IOMUX_SHIFT         12
+#define GRF_GPIO4C6_IOMUX_PWM           0x1
+
+#define PWM_CNT(n)			(0x0000 + 0x10 * (n))
+#define PWM_PERIOD_HPR(n)		(0x0004 + 0x10 * (n))
+#define PWM_DUTY_LPR(n)			(0x0008 + 0x10 * (n))
+#define PWM_CTRL(n)			(0x000c + 0x10 * (n))
+
+#define PWM_DISABLE			(0 << 0)
+#define PWM_ENABLE			(1 << 0)
+
 /*
  * When system reset in running state, we want the cpus to be reboot
  * from maskrom (system reboot),
@@ -263,8 +279,12 @@
 
 /* funciton*/
 void __dead2 soc_global_soft_reset(void);
-void plls_resume(void);
-void plls_suspend(void);
+void plls_suspend_prepare(void);
+void disable_dvfs_plls(void);
+void disable_nodvfs_plls(void);
+void plls_resume_finish(void);
+void enable_dvfs_plls(void);
+void enable_nodvfs_plls(void);
 void clk_gate_con_save(void);
 void clk_gate_con_disable(void);
 void clk_gate_con_restore(void);
diff --git a/plat/rockchip/rk3399/platform.mk b/plat/rockchip/rk3399/platform.mk
index a3bb426..94992f6 100644
--- a/plat/rockchip/rk3399/platform.mk
+++ b/plat/rockchip/rk3399/platform.mk
@@ -38,6 +38,7 @@
                                 -I${RK_PLAT_COMMON}/drivers/pmu/			\
 				-I${RK_PLAT_SOC}/				\
                                 -I${RK_PLAT_SOC}/drivers/pmu/                   \
+				-I${RK_PLAT_SOC}/drivers/pwm/			\
                                 -I${RK_PLAT_SOC}/drivers/soc/                   \
                                 -I${RK_PLAT_SOC}/include/                       \
 
@@ -74,6 +75,7 @@
 				${RK_PLAT_SOC}/plat_sip_calls.c			\
 				${RK_PLAT_SOC}/drivers/gpio/rk3399_gpio.c	\
                                 ${RK_PLAT_SOC}/drivers/pmu/pmu.c                \
+				${RK_PLAT_SOC}/drivers/pwm/pwm.c	\
                                 ${RK_PLAT_SOC}/drivers/soc/soc.c
 
 ENABLE_PLAT_COMPAT      :=      0
diff --git a/plat/rockchip/rk3399/rk3399_def.h b/plat/rockchip/rk3399/rk3399_def.h
index 787ec0c..8ee71a8 100644
--- a/plat/rockchip/rk3399/rk3399_def.h
+++ b/plat/rockchip/rk3399/rk3399_def.h
@@ -161,14 +161,4 @@
 #define RK3399_G1S_IRQS			ARM_IRQ_SEC_PHY_TIMER
 #define RK3399_G0_IRQS			ARM_IRQ_SEC_SGI_6
 
-#define PWM0_IOMUX_PWM_EN		(1 << 0)
-#define PWM1_IOMUX_PWM_EN		(1 << 1)
-#define PWM2_IOMUX_PWM_EN		(1 << 2)
-#define PWM3_IOMUX_PWM_EN		(1 << 3)
-
-#define PWM_CNT(n)			0x0000 + 0x10 * n
-#define PWM_PERIOD_HPR(n)		0x0004 + 0x10 * n
-#define PWM_DUTY_LPR(n)			0x0008 + 0x10 * n
-#define PWM_CTRL(n)			0x000c + 0x10 * n
-
 #endif /* __PLAT_DEF_H__ */
diff --git a/tools/fiptool/Makefile b/tools/fiptool/Makefile
index ec9dd99..3bc372a 100644
--- a/tools/fiptool/Makefile
+++ b/tools/fiptool/Makefile
@@ -68,8 +68,8 @@
 	@${ECHO_BLANK_LINE}
 
 fip_create: fip_create.sh
-	mkdir -p ../fip_create
-	install -m 755 fip_create.sh ../fip_create/fip_create
+	${Q}mkdir -p ../fip_create
+	${Q}install -m 755 fip_create.sh ../fip_create/fip_create
 
 %.o: %.c %.h ${COPIED_H_FILES} Makefile
 	@echo "  CC      $<"