diff options
Diffstat (limited to 'plat')
-rw-r--r-- | plat/imx/common/include/fsl_sip.h | 1 | ||||
-rw-r--r-- | plat/imx/common/sip_svc.c | 4 | ||||
-rw-r--r-- | plat/imx/imx8mq/ddrc.c | 201 |
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; +} |