summaryrefslogtreecommitdiff
path: root/plat
diff options
context:
space:
mode:
Diffstat (limited to 'plat')
-rw-r--r--plat/imx/common/include/fsl_sip.h1
-rw-r--r--plat/imx/common/sip_svc.c4
-rw-r--r--plat/imx/imx8mq/ddrc.c201
3 files changed, 206 insertions, 0 deletions
diff --git a/plat/imx/common/include/fsl_sip.h b/plat/imx/common/include/fsl_sip.h
index e118928a..be9af94b 100644
--- a/plat/imx/common/include/fsl_sip.h
+++ b/plat/imx/common/include/fsl_sip.h
@@ -27,4 +27,5 @@
#define FSL_SIP_BUILDINFO 0xC2000003
#define FSL_SIP_BUILDINFO_GET_COMMITHASH 0x00
+#define FSL_SIP_DDR_DVFS 0xc2000004
#endif
diff --git a/plat/imx/common/sip_svc.c b/plat/imx/common/sip_svc.c
index 84a8e976..a45a0a35 100644
--- a/plat/imx/common/sip_svc.c
+++ b/plat/imx/common/sip_svc.c
@@ -43,6 +43,7 @@ extern int imx_gpc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2,
extern int imx_cpufreq_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3);
extern int imx_srtc_handler(uint32_t smc_fid, u_register_t x1,
u_register_t x2, u_register_t x3, u_register_t x4);
+extern int lpddr4_dvfs_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3);
/* Setup i.MX platform specific services Services */
static int32_t plat_svc_setup(void)
@@ -112,6 +113,9 @@ uintptr_t imx_svc_smc_handler(uint32_t smc_fid,
case FSL_SIP_GPC:
SMC_RET1(handle, imx_gpc_handler(smc_fid, x1, x2, x3));
break;
+ case FSL_SIP_DDR_DVFS:
+ SMC_RET1(handle, lpddr4_dvfs_handler(smc_fid, x1, x2, x3));
+ break;
#endif
#if (defined(PLAT_IMX8QM) || defined(PLAT_IMX8QXP))
case FSL_SIP_CPUFREQ:
diff --git a/plat/imx/imx8mq/ddrc.c b/plat/imx/imx8mq/ddrc.c
index 3cc97608..d085d303 100644
--- a/plat/imx/imx8mq/ddrc.c
+++ b/plat/imx/imx8mq/ddrc.c
@@ -35,6 +35,7 @@
#include <ddrc.h>
#include <mmio.h>
#include <platform_def.h>
+#include <spinlock.h>
#include <soc.h>
struct ddrphy_trained_csr {
@@ -2635,3 +2636,203 @@ void ddrc_exit_retention(void)
mmio_write_32(IMX_DDRC_BASE + DDRC_RFSHCTL3, 0x0);
}
+void lpddr4_dvfs_hwffc(int init_vrcg, int init_fsp, int target_freq,
+ int discamdrain)
+{
+ uint32_t tmp, tmp_t;
+
+ INFO("hwffc enter\n");
+
+ /* step 1: hwffc flow enter, set the HWFCCCTL */
+ tmp = ((init_fsp << 4) & 0x10) | ((init_vrcg << 5) & 0x20) | 0x40;
+ tmp |= 0x3;
+ mmio_write_32(IMX_DDRC_BASE + DDRC_HWFFCCTL, tmp);
+
+ /*
+ * set SWCTL.sw_done to disable quasi-dynamic register programming
+ * outside reset
+ */
+ mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x0);
+
+ mmio_write_32(IMX_DDRC_BASE + DDRC_DFIMISC, 0x11);
+
+ /*
+ * set SWCTL.sw_done to enable quasi-dynamic register programming
+ * outside reset.
+ */
+ mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x1);
+ /*wait SWSTAT.sw_done_ack to 1 */
+ while (!(mmio_read_32(IMX_DDRC_BASE + DDRC_SWSTAT) & 0x1))
+ ;
+
+ /* wait for DBGCAM is empty */
+ if (discamdrain)
+ while ((mmio_read_32(IMX_DDRC_BASE + DDRC_DBGCAM) &
+ 0x30000000) != 0x30000000)
+ ;
+
+ /* set HWFFC requirements, ddrc_csysfreqency_ddrc[1:0] = 2b'10;
+ * [15]: ccmsrcgpcmix_ddrc_csysdisdrain_ddrc;
+ * [14:13]: ccmsrcgpcmix_ddrc_csysfrequency_ddrc[1:0];
+ * [12]: ccmsrcgpcmix_ddrc_csysmode_ddrc;
+ */
+ tmp = mmio_read_32(0x303a0164);
+ tmp &= 0xFFFF0FFF;
+ tmp |= ((discamdrain << 15) & 0x8000);
+ tmp |= ((target_freq << 13) & 0x6000);
+ tmp |= 0x1000;
+ mmio_write_32(0x303a0164, tmp);
+
+ /* set the gpc_ddr1_core_csysreq to active low */
+ tmp = mmio_read_32(0x303a01fc);
+ tmp &= 0xfffffffe;
+ mmio_write_32(0x303a01fc, tmp);
+
+ /* wait gpc_ddr1_core_csysack to active low */
+ while ((mmio_read_32(0x303a01fc) & 0x10000))
+ ;
+
+ /* step B: switch the clock */
+ if (target_freq == 0x1) {
+ /* change the clock source of dram_alt_clk_root to source 5 --400MHz */
+ mmio_write_32(0x3038a008, (0x7 << 24) | (0x7 << 16));
+ mmio_write_32(0x303aa004, (0x5 << 24));
+
+ /* change the clock source of dram_apb_clk_root to source 2 --40MHz/2 */
+ mmio_write_32(0x3038a088, (0x7 << 24) | (0x7 << 16));
+ mmio_write_32(0x3038a084, (0x2 << 24) | (0x1 << 16));;
+
+ /* bypass the DRAM PLL */
+ mmio_write_32(0x30389804, 1 << 24);
+
+ } else if (target_freq == 0x2) {
+ /* change the clock source of dram_alt_clk_root to source 2 --100MHz */
+ mmio_write_32(0x3038a008, (0x7 << 24) | (0x7 << 16));
+ mmio_write_32(0x3038a004, (0x2 << 24));
+
+ /* change the clock source of dram_apb_clk_root to source 2 --40Mhz/2 */
+ mmio_write_32(0x3038a088, (0x7 << 24) | (0x7 << 16));
+ mmio_write_32(0x3038a084, (0x2 << 24) | (0x1 << 16));;
+
+ /* bypass the DRAM PLL */
+ mmio_write_32(0x30389804, 1 << 24);
+ } else {
+ /* switch to the default freq fsp = 0x1 3200mts */
+ mmio_write_32(0x3038a088,(0x7<<24)|(0x7<<16));
+ /* to source 4 --800MHz/ 4 */
+ mmio_write_32(0x3038a084,(0x4<<24)|(0x3<<16));
+ mmio_write_32(0x30389808, 1<<24);
+ }
+
+ /* step C: exit hwffc flow */
+ tmp = mmio_read_32(0x303a01fc);
+ tmp |= 0x1;
+ mmio_write_32(0x303a01fc, tmp);
+ /* wait gpc_ddr1_core_csysack to high */
+ while (!(mmio_read_32(0x303a01fc) & 0x10000))
+ ;
+
+ /* [1:0]-->2b'10, to disable HWFFC */
+ mmio_write_32(IMX_DDRC_BASE + DDRC_HWFFCCTL, 0x72);
+
+ /*wait until hwffc_in_progress = 0 */
+ while(mmio_read_32(IMX_DDRC_BASE + DDRC_HWFFCSTAT) & 0x1)
+ ;
+
+ mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x0);
+ /* set MSTR.frequency_mode MSTR.[29], MSTR2.target_frequency[1:0] */
+ tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_MSTR);
+ tmp |= 0x20000000;
+ mmio_write_32(IMX_DDRC_BASE + DDRC_MSTR, tmp);
+
+ tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_HWFFCSTAT);
+ tmp_t = (tmp >> 4) & 0x3;
+ tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_MSTR2);
+ tmp = (tmp & 0xfffffffc) | tmp_t;
+ mmio_write_32(IMX_DDRC_BASE + DDRC_MSTR2, tmp);
+
+ mmio_write_32(IMX_DDRC_BASE + DDRC_SWCTL, 0x1);
+ mmio_write_32(IMX_DDRC_BASE + DDRC_HWFFCCTL, 0x70);
+
+ /* why we need below flow */
+ mmio_write_32(IMX_DDRC_BASE + DDRC_PWRCTL, 0x1a8);
+ while ((mmio_read_32(IMX_DDRC_BASE + DDRC_STAT) & 0x3) != 0x3)
+ ;
+ /* wait SDRAM into self refresh state */
+ while(!(mmio_read_32(IMX_DDRC_BASE + DDRC_STAT) & 0x30))
+ ;
+
+ tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_CRCPARSTAT);
+ tmp = mmio_read_32(IMX_DDRC_BASE + DDRC_CRCPARSTAT);
+
+ mmio_write_32(IMX_DDRC_BASE + DDRC_PWRCTL, 0x188);
+
+ /* wait STAT to normal mode */
+ while ((mmio_read_32(IMX_DDRC_BASE + DDRC_STAT) & 0x3) != 0x1)
+ ;
+
+ INFO("hwffc exit\n");
+}
+
+spinlock_t dfs_lock;
+static unsigned int init_fsp = 0x1;
+static unsigned int init_vrcg = 0x1;
+static unsigned int discamdrain = 0x1;
+
+static uint32_t irqs_used [] = {102, 109, 110, 111};
+static volatile uint32_t wfe_done;
+static volatile bool wait_ddrc_hwffc_done = true;
+
+/*
+ * x1 for frequency setpoint info;
+ * x2 is used for online cpu info
+ */
+int lpddr4_dvfs_handler(uint32_t smc_fid,
+ u_register_t x1,
+ u_register_t x2,
+ u_register_t x3)
+{
+ uint64_t mpidr = read_mpidr_el1();
+ unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
+ unsigned int target_freq = x1;
+ uint32_t online_cores = x2;
+
+ if (target_freq == 0xf) {
+ /* set the WFE done status */
+ spin_lock(&dfs_lock);
+ wfe_done |= (1 << cpu_id * 8);
+ spin_unlock(&dfs_lock);
+
+ while (1) {
+ /* ddr frequency change done */
+ wfe();
+ if (!wait_ddrc_hwffc_done) {
+ break;
+ }
+ }
+ } else {
+ wait_ddrc_hwffc_done = true;
+ /* trigger the IRQ */
+ for (int i = 0; i < 4; i++) {
+ int irq = irqs_used[i] % 32;
+ if (cpu_id != i && (online_cores & (0x1 << (i * 8))))
+ mmio_write_32(0x38800204 + (irqs_used[i] / 32) * 4, (1 << irq));
+ }
+
+ /* make sure all the core in WFE */
+ online_cores &= ~(0x1 << (cpu_id * 8));
+ while (1) {
+ if (online_cores == wfe_done)
+ break;
+ }
+
+ lpddr4_dvfs_hwffc(init_vrcg, init_fsp, target_freq, discamdrain);
+ init_fsp = (~init_fsp) & 0x1;
+ wait_ddrc_hwffc_done = false;
+ wfe_done = 0;
+ dsb();
+ sev();
+ isb();
+ }
+ return 0;
+}