summaryrefslogtreecommitdiff
path: root/plat/imx/imx8mq
diff options
context:
space:
mode:
authorBai Ping <ping.bai@nxp.com>2017-10-31 11:17:48 +0800
committerAbel Vesa <abel.vesa@nxp.com>2018-06-11 10:08:40 +0300
commit031cbe458830538c5e265c8c0eec290100236281 (patch)
tree375b186c8393cdf5c166c597e1e3472e3ac79579 /plat/imx/imx8mq
parent68c2cdd8cf00624e70449101169b49c266ad2703 (diff)
plat: imx8mq add ddr frequency support on imx8mq
When changing the DDR frequency, the DDRC will block AXI access, so the code for changing the frequency need to be run on OCRAM not make sure no DDR access at this stage. the DDR frequency change request is from EL1 linux kernel side, we use the SiP service call to trap the DDR frequency change operation from linux kernel to ATF. Signed-off-by: Bai Ping <ping.bai@nxp.com>
Diffstat (limited to 'plat/imx/imx8mq')
-rw-r--r--plat/imx/imx8mq/ddrc.c201
1 files changed, 201 insertions, 0 deletions
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;
+}