summaryrefslogtreecommitdiff
path: root/drivers/ddr/imx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ddr/imx')
-rw-r--r--drivers/ddr/imx/Kconfig4
-rw-r--r--drivers/ddr/imx/imx8m/Kconfig48
-rw-r--r--drivers/ddr/imx/imx8m/Makefile10
-rw-r--r--drivers/ddr/imx/imx8m/ddr_init.c476
-rw-r--r--drivers/ddr/imx/imx8ulp/Kconfig18
-rw-r--r--drivers/ddr/imx/imx8ulp/Makefile9
-rw-r--r--drivers/ddr/imx/imx8ulp/ddr_init.c296
-rw-r--r--drivers/ddr/imx/imx9/Kconfig27
-rw-r--r--drivers/ddr/imx/imx9/Makefile10
-rw-r--r--drivers/ddr/imx/imx9/ddr_init.c772
-rw-r--r--drivers/ddr/imx/phy/Kconfig4
-rw-r--r--drivers/ddr/imx/phy/Makefile9
-rw-r--r--drivers/ddr/imx/phy/ddrphy_csr.c732
-rw-r--r--drivers/ddr/imx/phy/ddrphy_train.c97
-rw-r--r--drivers/ddr/imx/phy/ddrphy_utils.c177
-rw-r--r--drivers/ddr/imx/phy/helper.c227
16 files changed, 2916 insertions, 0 deletions
diff --git a/drivers/ddr/imx/Kconfig b/drivers/ddr/imx/Kconfig
new file mode 100644
index 00000000000..328fbabb6db
--- /dev/null
+++ b/drivers/ddr/imx/Kconfig
@@ -0,0 +1,4 @@
+source "drivers/ddr/imx/imx8m/Kconfig"
+source "drivers/ddr/imx/imx8ulp/Kconfig"
+source "drivers/ddr/imx/imx9/Kconfig"
+source "drivers/ddr/imx/phy/Kconfig"
diff --git a/drivers/ddr/imx/imx8m/Kconfig b/drivers/ddr/imx/imx8m/Kconfig
new file mode 100644
index 00000000000..08b6787a543
--- /dev/null
+++ b/drivers/ddr/imx/imx8m/Kconfig
@@ -0,0 +1,48 @@
+menu "i.MX8M DDR controllers"
+ depends on ARCH_IMX8M
+
+config IMX8M_DRAM
+ bool "imx8m dram"
+ select IMX_SNPS_DDR_PHY
+
+config IMX8M_LPDDR4
+ bool "imx8m lpddr4"
+ select IMX8M_DRAM
+ help
+ Select the i.MX8M LPDDR4 driver support on i.MX8M SOC.
+
+config IMX8M_DDR4
+ bool "imx8m ddr4"
+ select IMX8M_DRAM
+ help
+ Select the i.MX8M DDR4 driver support on i.MX8M SOC.
+
+config IMX8M_DDR3L
+ bool "imx8m ddr3l"
+ select IMX8M_DRAM
+ help
+ Select the i.MX8M DDR3L driver support on i.MX8M SOC.
+
+config SAVED_DRAM_TIMING_BASE
+ hex "Define the base address for saved dram timing"
+ help
+ after DRAM is trained, need to save the dram related timming
+ info into memory for low power use. OCRAM_S is used for this
+ purpose on i.MX8MM.
+ default 0x180000
+
+config IMX8M_DRAM_INLINE_ECC
+ bool "imx8mp inline ECC"
+ depends on IMX8MP && IMX8M_LPDDR4
+ help
+ Select this config if you want to use inline ecc feature for
+ imx8mp-evk board.
+
+config IMX8M_VDD_SOC_850MV
+ bool "imx8mp change the vdd_soc voltage to 850mv"
+ depends on IMX8MP
+
+config IMX8M_LPDDR4_FREQ0_2400MTS
+ bool "imx8m PDDR4 freq0 change from 4000MTS to 2400MTS"
+
+endmenu
diff --git a/drivers/ddr/imx/imx8m/Makefile b/drivers/ddr/imx/imx8m/Makefile
new file mode 100644
index 00000000000..aed91dc23f4
--- /dev/null
+++ b/drivers/ddr/imx/imx8m/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright 2018 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_IMX8M_DRAM) += ddr_init.o
+obj-y += ../phy/
+endif
diff --git a/drivers/ddr/imx/imx8m/ddr_init.c b/drivers/ddr/imx/imx8m/ddr_init.c
new file mode 100644
index 00000000000..e9209ce8b61
--- /dev/null
+++ b/drivers/ddr/imx/imx8m/ddr_init.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018-2019 NXP
+ */
+
+#include <errno.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+
+static unsigned int g_cdd_rr_max[4];
+static unsigned int g_cdd_rw_max[4];
+static unsigned int g_cdd_wr_max[4];
+static unsigned int g_cdd_ww_max[4];
+
+void ddr_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num)
+{
+ int i = 0;
+
+ for (i = 0; i < num; i++) {
+ reg32_write(ddrc_cfg->reg, ddrc_cfg->val);
+ ddrc_cfg++;
+ }
+}
+
+#ifdef CONFIG_IMX8M_DRAM_INLINE_ECC
+void ddrc_inline_ecc_scrub(unsigned int start_address,
+ unsigned int range_address)
+{
+ unsigned int tmp;
+
+ /* Step1: Enable quasi-dynamic programming */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
+ /* Step2: Set ECCCFG1.ecc_parity_region_lock to 1 */
+ reg32setbit(DDRC_ECCCFG1(0), 0x4);
+ /* Step3: Block the AXI ports from taking the transaction */
+ reg32_write(DDRC_PCTRL_0(0), 0x0);
+ /* Step4: Set scrub start address */
+ reg32_write(DDRC_SBRSTART0(0), start_address);
+ /* Step5: Set scrub range address */
+ reg32_write(DDRC_SBRRANGE0(0), range_address);
+ /* Step6: Set scrub_mode to write */
+ reg32_write(DDRC_SBRCTL(0), 0x00000014);
+ /* Step7: Set the desired pattern through SBRWDATA0 registers */
+ reg32_write(DDRC_SBRWDATA0(0), 0x55aa55aa);
+ /* Step8: Enable the SBR by programming SBRCTL.scrub_en=1 */
+ reg32setbit(DDRC_SBRCTL(0), 0x0);
+ /* Step9: Poll SBRSTAT.scrub_done=1 */
+ tmp = reg32_read(DDRC_SBRSTAT(0));
+ while (tmp != 0x00000002)
+ tmp = reg32_read(DDRC_SBRSTAT(0)) & 0x2;
+ /* Step10: Poll SBRSTAT.scrub_busy=0 */
+ tmp = reg32_read(DDRC_SBRSTAT(0));
+ while (tmp != 0x0)
+ tmp = reg32_read(DDRC_SBRSTAT(0)) & 0x1;
+ /* Step11: Disable SBR by programming SBRCTL.scrub_en=0 */
+ clrbits_le32(DDRC_SBRCTL(0), 0x1);
+ /* Step12: Prepare for normal scrub operation(Read) and set scrub_interval*/
+ reg32_write(DDRC_SBRCTL(0), 0x100);
+ /* Step13: Enable the SBR by programming SBRCTL.scrub_en=1 */
+ reg32_write(DDRC_SBRCTL(0), 0x101);
+ /* Step14: Enable AXI ports by programming */
+ reg32_write(DDRC_PCTRL_0(0), 0x1);
+ /* Step15: Disable quasi-dynamic programming */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+}
+
+void ddrc_inline_ecc_scrub_end(unsigned int start_address,
+ unsigned int range_address)
+{
+ /* Step1: Enable quasi-dynamic programming */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
+ /* Step2: Block the AXI ports from taking the transaction */
+ reg32_write(DDRC_PCTRL_0(0), 0x0);
+ /* Step3: Set scrub start address */
+ reg32_write(DDRC_SBRSTART0(0), start_address);
+ /* Step4: Set scrub range address */
+ reg32_write(DDRC_SBRRANGE0(0), range_address);
+ /* Step5: Disable SBR by programming SBRCTL.scrub_en=0 */
+ clrbits_le32(DDRC_SBRCTL(0), 0x1);
+ /* Step6: Prepare for normal scrub operation(Read) and set scrub_interval */
+ reg32_write(DDRC_SBRCTL(0), 0x100);
+ /* Step7: Enable the SBR by programming SBRCTL.scrub_en=1 */
+ reg32_write(DDRC_SBRCTL(0), 0x101);
+ /* Step8: Enable AXI ports by programming */
+ reg32_write(DDRC_PCTRL_0(0), 0x1);
+ /* Step9: Disable quasi-dynamic programming */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+}
+#endif
+
+void __weak board_dram_ecc_scrub(void)
+{
+}
+
+void lpddr4_mr_write(unsigned int mr_rank, unsigned int mr_addr,
+ unsigned int mr_data)
+{
+ unsigned int tmp;
+ /*
+ * 1. Poll MRSTAT.mr_wr_busy until it is 0.
+ * This checks that there is no outstanding MR transaction.
+ * No writes should be performed to MRCTRL0 and MRCTRL1 if
+ * MRSTAT.mr_wr_busy = 1.
+ */
+ do {
+ tmp = reg32_read(DDRC_MRSTAT(0));
+ } while (tmp & 0x1);
+ /*
+ * 2. Write the MRCTRL0.mr_type, MRCTRL0.mr_addr, MRCTRL0.mr_rank and
+ * (for MRWs) MRCTRL1.mr_data to define the MR transaction.
+ */
+ reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4));
+ reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8) | mr_data);
+ reg32setbit(DDRC_MRCTRL0(0), 31);
+}
+
+unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr)
+{
+ unsigned int tmp;
+
+ reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x1);
+ do {
+ tmp = reg32_read(DDRC_MRSTAT(0));
+ } while (tmp & 0x1);
+
+ reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4) | 0x1);
+ reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8));
+ reg32setbit(DDRC_MRCTRL0(0), 31);
+ do {
+ tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0));
+ } while ((tmp & 0x8) == 0);
+ tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0));
+ reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4);
+ while (tmp) { //try to find a significant byte in the word
+ if (tmp & 0xff) {
+ tmp &= 0xff;
+ break;
+ }
+ tmp >>= 8;
+ }
+
+ return tmp;
+}
+
+static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
+ unsigned int addr_end)
+{
+ unsigned int i, imax = 0;
+
+ for (i = addr_start; i <= addr_end; i++) {
+ if (((data[i] >> 7) == 0) && data[i] > imax)
+ imax = data[i];
+ }
+
+ return imax;
+}
+
+void get_trained_CDD(u32 fsp)
+{
+ unsigned int i, ddr_type, tmp;
+ unsigned int cdd_cha[12], cdd_chb[12];
+ unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
+ unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
+
+ ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
+ if (ddr_type == 0x20) {
+ for (i = 0; i < 6; i++) {
+ tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54013 + i) * 4);
+ cdd_cha[i * 2] = tmp & 0xff;
+ cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ for (i = 0; i < 7; i++) {
+ tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x5402c + i) * 4);
+ if (i == 0) {
+ cdd_cha[0] = (tmp >> 8) & 0xff;
+ } else if (i == 6) {
+ cdd_cha[11] = tmp & 0xff;
+ } else {
+ cdd_chb[i * 2 - 1] = tmp & 0xff;
+ cdd_chb[i * 2] = (tmp >> 8) & 0xff;
+ }
+ }
+
+ cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
+ cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
+ cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
+ cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
+ cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
+ cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
+ cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
+ cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
+ g_cdd_rr_max[fsp] =
+ cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
+ g_cdd_rw_max[fsp] =
+ cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
+ g_cdd_wr_max[fsp] =
+ cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
+ g_cdd_ww_max[fsp] =
+ cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
+ } else {
+ unsigned int ddr4_cdd[64];
+
+ for (i = 0; i < 29; i++) {
+ tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + (0x54012 + i) * 4);
+ ddr4_cdd[i * 2] = tmp & 0xff;
+ ddr4_cdd[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ g_cdd_rr_max[fsp] = look_for_max(ddr4_cdd, 1, 12);
+ g_cdd_ww_max[fsp] = look_for_max(ddr4_cdd, 13, 24);
+ g_cdd_rw_max[fsp] = look_for_max(ddr4_cdd, 25, 40);
+ g_cdd_wr_max[fsp] = look_for_max(ddr4_cdd, 41, 56);
+ }
+}
+
+void update_umctl2_rank_space_setting(unsigned int pstat_num)
+{
+ unsigned int i, ddr_type;
+ unsigned int addr_slot, rdata, tmp, tmp_t;
+ unsigned int ddrc_w2r, ddrc_r2w, ddrc_wr_gap, ddrc_rd_gap;
+
+ ddr_type = reg32_read(DDRC_MSTR(0)) & 0x3f;
+ for (i = 0; i < pstat_num; i++) {
+ addr_slot = i ? (i + 1) * 0x1000 : 0;
+ if (ddr_type == 0x20) {
+ /* update r2w:[13:8], w2r:[5:0] */
+ rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
+ ddrc_w2r = rdata & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
+ else
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
+ ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+
+ ddrc_r2w = (rdata >> 8) & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
+ else
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
+ ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+
+ tmp_t = (rdata & 0xffffc0c0) | (ddrc_r2w << 8) | ddrc_w2r;
+ reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
+ } else {
+ /* update w2r:[5:0] */
+ rdata = reg32_read(DDRC_DRAMTMG9(0) + addr_slot);
+ ddrc_w2r = rdata & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1);
+ else
+ tmp = ddrc_w2r + (g_cdd_wr_max[i] >> 1) + 1;
+ ddrc_w2r = (tmp > 0x3f) ? 0x3f : tmp;
+ tmp_t = (rdata & 0xffffffc0) | ddrc_w2r;
+ reg32_write((DDRC_DRAMTMG9(0) + addr_slot), tmp_t);
+
+ /* update r2w:[13:8] */
+ rdata = reg32_read(DDRC_DRAMTMG2(0) + addr_slot);
+ ddrc_r2w = (rdata >> 8) & 0x3f;
+ if (is_imx8mp())
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1);
+ else
+ tmp = ddrc_r2w + (g_cdd_rw_max[i] >> 1) + 1;
+ ddrc_r2w = (tmp > 0x3f) ? 0x3f : tmp;
+
+ tmp_t = (rdata & 0xffffc0ff) | (ddrc_r2w << 8);
+ reg32_write((DDRC_DRAMTMG2(0) + addr_slot), tmp_t);
+ }
+
+ if (!is_imx8mq()) {
+ /*
+ * update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static)
+ */
+ rdata = reg32_read(DDRC_RANKCTL(0) + addr_slot);
+ ddrc_wr_gap = (rdata >> 8) & 0xf;
+ if (is_imx8mp())
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1);
+ else
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[i] >> 1) + 1;
+ ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ ddrc_rd_gap = (rdata >> 4) & 0xf;
+ if (is_imx8mp())
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1);
+ else
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[i] >> 1) + 1;
+ ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
+ reg32_write((DDRC_RANKCTL(0) + addr_slot), tmp_t);
+ }
+ }
+
+ if (is_imx8mq()) {
+ /* update rankctl: wr_gap:11:8; rd:gap:7:4; quasi-dymic, doc wrong(static) */
+ rdata = reg32_read(DDRC_RANKCTL(0));
+ ddrc_wr_gap = (rdata >> 8) & 0xf;
+ tmp = ddrc_wr_gap + (g_cdd_ww_max[0] >> 1) + 1;
+ ddrc_wr_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ ddrc_rd_gap = (rdata >> 4) & 0xf;
+ tmp = ddrc_rd_gap + (g_cdd_rr_max[0] >> 1) + 1;
+ ddrc_rd_gap = (tmp > 0xf) ? 0xf : tmp;
+
+ tmp_t = (rdata & 0xfffff00f) | (ddrc_wr_gap << 8) | (ddrc_rd_gap << 4);
+ reg32_write(DDRC_RANKCTL(0), tmp_t);
+ }
+}
+
+int ddr_init(struct dram_timing_info *dram_timing)
+{
+ unsigned int tmp, initial_drate, target_freq;
+ int ret;
+
+ debug("DDRINFO: start DRAM init\n");
+
+ /* Step1: Follow the power up procedure */
+ if (is_imx8mq()) {
+ reg32_write(SRC_DDRC_RCR_ADDR + 0x04, 0x8F00000F);
+ reg32_write(SRC_DDRC_RCR_ADDR, 0x8F00000F);
+ reg32_write(SRC_DDRC_RCR_ADDR + 0x04, 0x8F000000);
+ } else {
+ reg32_write(SRC_DDRC_RCR_ADDR, 0x8F00001F);
+ reg32_write(SRC_DDRC_RCR_ADDR, 0x8F00000F);
+ }
+
+ debug("DDRINFO: cfg clk\n");
+ /* change the clock source of dram_apb_clk_root: source 4 800MHz /4 = 200MHz */
+ clock_set_target_val(DRAM_APB_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(4) |
+ CLK_ROOT_PRE_DIV(CLK_ROOT_PRE_DIV4));
+
+ /* disable iso */
+ reg32_write(0x303A00EC, 0x0000ffff); /* PGC_CPU_MAPPING */
+ reg32setbit(0x303A00F8, 5); /* PU_PGC_SW_PUP_REQ */
+
+ initial_drate = dram_timing->fsp_msg[0].drate;
+ /* default to the frequency point 0 clock */
+ ddrphy_init_set_dfi_clk(initial_drate);
+
+ /* D-aasert the presetn */
+ reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000006);
+
+ /* Step2: Program the dwc_ddr_umctl2 registers */
+ debug("DDRINFO: ddrc config start\n");
+ ddr_cfg_umctl2(dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num);
+ debug("DDRINFO: ddrc config done\n");
+
+ /* Step3: De-assert reset signal(core_ddrc_rstn & aresetn_n) */
+ reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000004);
+ reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000000);
+
+ /*
+ * Step4: Disable auto-refreshes, self-refresh, powerdown, and
+ * assertion of dfi_dram_clk_disable by setting RFSHCTL3.dis_auto_refresh = 1,
+ * PWRCTL.powerdown_en = 0, and PWRCTL.selfref_en = 0, PWRCTL.en_dfi_dram_clk_disable = 0
+ */
+ reg32_write(DDRC_DBG1(0), 0x00000000);
+ reg32_write(DDRC_RFSHCTL3(0), 0x0000001);
+ reg32_write(DDRC_PWRCTL(0), 0xa0);
+
+ /* if ddr type is LPDDR4, do it */
+ tmp = reg32_read(DDRC_MSTR(0));
+ if (tmp & (0x1 << 5) && !is_imx8mn())
+ reg32_write(DDRC_DDR_SS_GPR0, 0x01); /* LPDDR4 mode */
+
+ /* determine the initial boot frequency */
+ target_freq = reg32_read(DDRC_MSTR2(0)) & 0x3;
+ target_freq = (tmp & (0x1 << 29)) ? target_freq : 0x0;
+
+ /* Step5: Set SWCT.sw_done to 0 */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
+
+ /* Set the default boot frequency point */
+ clrsetbits_le32(DDRC_DFIMISC(0), (0x1f << 8), target_freq << 8);
+ /* Step6: Set DFIMISC.dfi_init_complete_en to 0 */
+ clrbits_le32(DDRC_DFIMISC(0), 0x1);
+
+ /* Step7: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+ do {
+ tmp = reg32_read(DDRC_SWSTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /*
+ * Step8 ~ Step13: Start PHY initialization and training by
+ * accessing relevant PUB registers
+ */
+ debug("DDRINFO:ddrphy config start\n");
+
+ ret = ddr_cfg_phy(dram_timing);
+ if (ret)
+ return ret;
+
+ debug("DDRINFO: ddrphy config done\n");
+
+ /*
+ * step14 CalBusy.0 =1, indicates the calibrator is actively
+ * calibrating. Wait Calibrating done.
+ */
+ do {
+ tmp = reg32_read(DDRPHY_CalBusy(0));
+ } while ((tmp & 0x1));
+
+ debug("DDRINFO:ddrphy calibration done\n");
+
+ /* Step15: Set SWCTL.sw_done to 0 */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
+
+ /* Apply rank-to-rank workaround */
+ update_umctl2_rank_space_setting(dram_timing->fsp_msg_num - 1);
+
+ /* Step16: Set DFIMISC.dfi_init_start to 1 */
+ setbits_le32(DDRC_DFIMISC(0), (0x1 << 5));
+
+ /* Step17: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+ do {
+ tmp = reg32_read(DDRC_SWSTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /* Step18: Polling DFISTAT.dfi_init_complete = 1 */
+ do {
+ tmp = reg32_read(DDRC_DFISTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /* Step19: Set SWCTL.sw_done to 0 */
+ reg32_write(DDRC_SWCTL(0), 0x00000000);
+
+ /* Step20: Set DFIMISC.dfi_init_start to 0 */
+ clrbits_le32(DDRC_DFIMISC(0), (0x1 << 5));
+
+ /* Step21: optional */
+
+ /* Step22: Set DFIMISC.dfi_init_complete_en to 1 */
+ setbits_le32(DDRC_DFIMISC(0), 0x1);
+
+ /* Step23: Set PWRCTL.selfref_sw to 0 */
+ clrbits_le32(DDRC_PWRCTL(0), (0x1 << 5));
+
+ /* Step24: Set SWCTL.sw_done to 1; need polling SWSTAT.sw_done_ack */
+ reg32_write(DDRC_SWCTL(0), 0x00000001);
+ do {
+ tmp = reg32_read(DDRC_SWSTAT(0));
+ } while ((tmp & 0x1) == 0x0);
+
+ /* Step25: Wait for dwc_ddr_umctl2 to move to normal operating mode by monitoring
+ * STAT.operating_mode signal */
+ do {
+ tmp = reg32_read(DDRC_STAT(0));
+ } while ((tmp & 0x3) != 0x1);
+
+ /* Step26: Set back register in Step4 to the original values if desired */
+ reg32_write(DDRC_RFSHCTL3(0), 0x0000000);
+
+ /* enable port 0 */
+ reg32_write(DDRC_PCTRL_0(0), 0x00000001);
+ debug("DDRINFO: ddrmix config done\n");
+
+ board_dram_ecc_scrub();
+
+ /* enable selfref_en by default */
+ setbits_le32(DDRC_PWRCTL(0), 0x1);
+
+ /* save the dram timing config into memory */
+ dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+
+ return 0;
+}
+
+ulong ddrphy_addr_remap(uint32_t paddr_apb_from_ctlr)
+{
+ return 4 * paddr_apb_from_ctlr;
+}
diff --git a/drivers/ddr/imx/imx8ulp/Kconfig b/drivers/ddr/imx/imx8ulp/Kconfig
new file mode 100644
index 00000000000..005f581f4ba
--- /dev/null
+++ b/drivers/ddr/imx/imx8ulp/Kconfig
@@ -0,0 +1,18 @@
+menu "i.MX8ULP DDR controllers"
+ depends on ARCH_IMX8ULP
+
+config IMX8ULP_DRAM
+ bool "imx8m dram"
+
+config IMX8ULP_DRAM_PHY_PLL_BYPASS
+ bool "Enable the DDR PHY PLL bypass mode, so PHY clock is from DDR_CLK"
+ depends on IMX8ULP_DRAM
+
+config SAVED_DRAM_TIMING_BASE
+ hex "Define the base address for saved dram timing"
+ help
+ The DRAM config timing data need to be saved into sram
+ for low power use.
+ default 0x20055000
+
+endmenu
diff --git a/drivers/ddr/imx/imx8ulp/Makefile b/drivers/ddr/imx/imx8ulp/Makefile
new file mode 100644
index 00000000000..7f44a92180f
--- /dev/null
+++ b/drivers/ddr/imx/imx8ulp/Makefile
@@ -0,0 +1,9 @@
+#
+# Copyright 2021 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_IMX8ULP_DRAM) += ddr_init.o
+endif
diff --git a/drivers/ddr/imx/imx8ulp/ddr_init.c b/drivers/ddr/imx/imx8ulp/ddr_init.c
new file mode 100644
index 00000000000..172e260a55d
--- /dev/null
+++ b/drivers/ddr/imx/imx8ulp/ddr_init.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0+ OR MIT
+/*
+ * Copyright 2021 NXP
+ */
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/imx-regs.h>
+
+#define DENALI_CTL_00 (DDR_CTL_BASE_ADDR + 4 * 0)
+#define CTL_START 0x1
+
+#define DENALI_CTL_03 (DDR_CTL_BASE_ADDR + 4 * 3)
+#define DENALI_CTL_197 (DDR_CTL_BASE_ADDR + 4 * 197)
+#define DENALI_CTL_250 (DDR_CTL_BASE_ADDR + 4 * 250)
+#define DENALI_CTL_251 (DDR_CTL_BASE_ADDR + 4 * 251)
+#define DENALI_CTL_266 (DDR_CTL_BASE_ADDR + 4 * 266)
+#define DFI_INIT_COMPLETE 0x2
+
+#define DENALI_CTL_614 (DDR_CTL_BASE_ADDR + 4 * 614)
+#define DENALI_CTL_615 (DDR_CTL_BASE_ADDR + 4 * 615)
+
+#define DENALI_PI_00 (DDR_PI_BASE_ADDR + 4 * 0)
+#define PI_START 0x1
+
+#define DENALI_PI_04 (DDR_PI_BASE_ADDR + 4 * 4)
+#define DENALI_PI_11 (DDR_PI_BASE_ADDR + 4 * 11)
+#define DENALI_PI_12 (DDR_PI_BASE_ADDR + 4 * 12)
+#define DENALI_CTL_23 (DDR_CTL_BASE_ADDR + 4 * 23)
+#define DENALI_CTL_25 (DDR_CTL_BASE_ADDR + 4 * 25)
+
+#define DENALI_PHY_1624 (DDR_PHY_BASE_ADDR + 4 * 1624)
+#define DENALI_PHY_1625 (DDR_PHY_BASE_ADDR + 4 * 1625)
+#define DENALI_PHY_1537 (DDR_PHY_BASE_ADDR + 4 * 1537)
+#define PHY_FREQ_SEL_MULTICAST_EN(X) ((X) << 8)
+#define PHY_FREQ_SEL_INDEX(X) ((X) << 16)
+
+#define DENALI_PHY_1547 (DDR_PHY_BASE_ADDR + 4 * 1547)
+#define DENALI_PHY_1555 (DDR_PHY_BASE_ADDR + 4 * 1555)
+#define DENALI_PHY_1564 (DDR_PHY_BASE_ADDR + 4 * 1564)
+#define DENALI_PHY_1565 (DDR_PHY_BASE_ADDR + 4 * 1565)
+
+static void ddr_enable_pll_bypass(void)
+{
+ u32 reg_val;
+
+ /* PI_INIT_LVL_EN=0x0 (DENALI_PI_04) */
+ reg_val = readl(DENALI_PI_04) & ~0x1;
+ writel(reg_val, DENALI_PI_04);
+
+ /* PI_FREQ_MAP=0x1 (DENALI_PI_12) */
+ writel(0x1, DENALI_PI_12);
+
+ /* PI_INIT_WORK_FREQ=0x0 (DENALI_PI_11) */
+ reg_val = readl(DENALI_PI_11) & ~(0x1f << 8);
+ writel(reg_val, DENALI_PI_11);
+
+ /* DFIBUS_FREQ_INIT=0x0 (DENALI_CTL_23) */
+ reg_val = readl(DENALI_CTL_23) & ~(0x3 << 24);
+ writel(reg_val, DENALI_CTL_23);
+
+ /* PHY_LP4_BOOT_DISABLE=0x0 (DENALI_PHY_1547) */
+ reg_val = readl(DENALI_PHY_1547) & ~(0x1 << 8);
+ writel(reg_val, DENALI_PHY_1547);
+
+ /* PHY_PLL_BYPASS=0x1 (DENALI_PHY_1624) */
+ reg_val = readl(DENALI_PHY_1624) | 0x1;
+ writel(reg_val, DENALI_PHY_1624);
+
+ /* PHY_LP4_BOOT_PLL_BYPASS to 0x1 (DENALI_PHY_1555) */
+ reg_val = readl(DENALI_PHY_1555) | 0x1;
+ writel(reg_val, DENALI_PHY_1555);
+
+ /* FREQ_CHANGE_TYPE_F0 = 0x0/FREQ_CHANGE_TYPE_F1 = 0x1/FREQ_CHANGE_TYPE_F2 = 0x2 */
+ reg_val = 0x020100;
+ writel(reg_val, DENALI_CTL_25);
+}
+
+int ddr_calibration(unsigned int fsp_table[3])
+{
+ u32 reg_val;
+ u32 int_status_init, phy_freq_req, phy_freq_type;
+ u32 lock_0, lock_1, lock_2;
+ u32 freq_chg_pt, freq_chg_cnt;
+ u32 is_lpddr4 = 0;
+
+ if (IS_ENABLED(CONFIG_IMX8ULP_DRAM_PHY_PLL_BYPASS)) {
+ ddr_enable_pll_bypass();
+ freq_chg_cnt = 0;
+ freq_chg_pt = 0;
+ } else {
+ reg_val = (readl(DENALI_CTL_00)>>8)&0xf;
+ if(reg_val == 0x7) {
+ /* LPDDR3 type */
+ set_ddr_clk(fsp_table[1] >> 1);
+ freq_chg_cnt = 0;
+ freq_chg_pt = 0;
+ } else if(reg_val == 0xb) {
+ /* LPDDR4/4x type */
+ is_lpddr4 = 1;
+ reg_val = readl(DENALI_CTL_250);
+ if (((reg_val >> 16) & 0x3) == 1)
+ freq_chg_cnt = 2;
+ else
+ freq_chg_cnt = 3;
+
+ reg_val = readl(DENALI_PI_12);
+ if(reg_val == 0x3)
+ freq_chg_pt = 1;
+ else if(reg_val == 0x7)
+ freq_chg_pt = 2;
+ else {
+ printf("frequency map(0x%x) is wrong, please check!\r\n", reg_val);
+ return -1;
+ }
+ } else {
+ printf("Incorrect DDR type configured!\r\n");
+ return -1;
+ }
+ }
+
+ /* Assert PI_START parameter and then assert START parameter in Controller. */
+ reg_val = readl(DENALI_PI_00) | PI_START;
+ writel(reg_val, DENALI_PI_00);
+
+ reg_val = readl(DENALI_CTL_00) | CTL_START;
+ writel(reg_val, DENALI_CTL_00);
+
+ /* Poll for init_done_bit in Controller interrupt status register (INT_STATUS_INIT) */
+ do {
+ if (!freq_chg_cnt) {
+ int_status_init = (readl(DENALI_CTL_266) >> 8) & 0xff;
+ /* DDR subsystem is ready for traffic. */
+ if (int_status_init & DFI_INIT_COMPLETE) {
+ debug("complete\n");
+ break;
+ }
+ }
+
+ /*
+ * During leveling, PHY will request for freq change and SoC clock logic
+ * should provide requested frequency
+ * Polling SIM LPDDR_CTRL2 Bit phy_freq_chg_req until be 1'b1
+ */
+ reg_val = readl(AVD_SIM_LPDDR_CTRL2);
+ /* DFS interrupt is set */
+ phy_freq_req = ((reg_val >> 7) & 0x1) && ((reg_val >> 15) & 0x1);
+ if (phy_freq_req) {
+ phy_freq_type = reg_val & 0x1F;
+ if (phy_freq_type == 0x00) {
+ debug("Poll for freq_chg_req on SIM register and change to F0 frequency.\n");
+ set_ddr_clk(fsp_table[phy_freq_type] >> 1);
+
+ /* Write 1'b1 at LPDDR_CTRL2 bit phy_freq_cfg_ack */
+ reg_val = readl(AVD_SIM_LPDDR_CTRL2);
+ writel(reg_val | (0x1 << 6), AVD_SIM_LPDDR_CTRL2);
+ } else if (phy_freq_type == 0x01) {
+ debug("Poll for freq_chg_req on SIM register and change to F1 frequency.\n");
+ set_ddr_clk(fsp_table[phy_freq_type] >> 1);
+
+ /* Write 1'b1 at LPDDR_CTRL2 bit phy_freq_cfg_ack */
+ reg_val = readl(AVD_SIM_LPDDR_CTRL2);
+ writel(reg_val | (0x1 << 6), AVD_SIM_LPDDR_CTRL2);
+ if (freq_chg_pt == 1)
+ freq_chg_cnt--;
+ } else if (phy_freq_type == 0x02) {
+ debug("Poll for freq_chg_req on SIM register and change to F2 frequency.\n");
+ set_ddr_clk(fsp_table[phy_freq_type] >> 1);
+
+ /* Write 1'b1 at LPDDR_CTRL2 bit phy_freq_cfg_ack */
+ reg_val = readl(AVD_SIM_LPDDR_CTRL2);
+ writel(reg_val | (0x1 << 6), AVD_SIM_LPDDR_CTRL2);
+ if (freq_chg_pt == 2)
+ freq_chg_cnt--;
+ }
+
+ /* Hardware clear the ack on falling edge of LPDDR_CTRL2:phy_freq_chg_reg */
+ /* Ensure the ack is clear before starting to poll request again */
+ while ((readl(AVD_SIM_LPDDR_CTRL2) & BIT(6)))
+ ;
+ }
+ } while (1);
+
+ /* Check PLL lock status */
+ lock_0 = readl(DENALI_PHY_1564) & 0xffff;
+ lock_1 = (readl(DENALI_PHY_1564) >> 16) & 0xffff;
+ lock_2 = readl(DENALI_PHY_1565) & 0xffff;
+
+ if ((lock_0 & 0x3) != 0x3 || (lock_1 & 0x3) != 0x3 || (lock_2 & 0x3) != 0x3) {
+ debug("De-Skew PLL failed to lock\n");
+ debug("lock_0=0x%x, lock_1=0x%x, lock_2=0x%x\n", lock_0, lock_1, lock_2);
+ return -1;
+ }
+
+ debug("De-Skew PLL is locked and ready\n");
+
+ /* Change LPDDR4 FREQ1 to bypass mode if it is lower than 200MHz */
+ if(is_lpddr4 && fsp_table[1] < 400) {
+ /* Set FREQ1 to bypass mode */
+ reg_val = PHY_FREQ_SEL_MULTICAST_EN(0) | PHY_FREQ_SEL_INDEX(0);
+ writel(reg_val, DENALI_PHY_1537);
+
+ /* PHY_PLL_BYPASS=0x1 (DENALI_PHY_1624) */
+ reg_val =readl(DENALI_PHY_1624) | 0x1;
+ writel(reg_val, DENALI_PHY_1624);
+
+ /* DENALI_PHY_1625: bypass mode in PHY PLL */
+ reg_val =readl(DENALI_PHY_1625) & ~0xf;
+ writel(reg_val, DENALI_PHY_1625);
+ }
+
+ return 0;
+}
+
+static void save_dram_config(struct dram_timing_info2 *timing_info, unsigned long saved_timing_base)
+{
+ int i = 0;
+ struct dram_timing_info2 *saved_timing = (struct dram_timing_info2 *)saved_timing_base;
+ struct dram_cfg_param *cfg;
+
+ saved_timing->ctl_cfg_num = timing_info->ctl_cfg_num;
+ saved_timing->phy_f1_cfg_num = timing_info->phy_f1_cfg_num;
+ saved_timing->phy_f2_cfg_num = timing_info->phy_f2_cfg_num;
+
+ /* save the fsp table */
+ for (i = 0; i < 3; i++)
+ saved_timing->fsp_table[i] = timing_info->fsp_table[i];
+
+ cfg = (struct dram_cfg_param *)(saved_timing_base +
+ sizeof(*timing_info));
+
+ /* save ctl config */
+ saved_timing->ctl_cfg = cfg;
+ for (i = 0; i < timing_info->ctl_cfg_num; i++) {
+ cfg->reg = timing_info->ctl_cfg[i].reg;
+ cfg->val = timing_info->ctl_cfg[i].val;
+ cfg++;
+ }
+
+ /* save phy f1 config */
+ saved_timing->phy_f1_cfg = cfg;
+ for (i = 0; i < timing_info->phy_f1_cfg_num; i++) {
+ cfg->reg = timing_info->phy_f1_cfg[i].reg;
+ cfg->val = timing_info->phy_f1_cfg[i].val;
+ cfg++;
+ }
+
+ /* save phy f2 config */
+ saved_timing->phy_f2_cfg = cfg;
+ for (i = 0; i < timing_info->phy_f2_cfg_num; i++) {
+ cfg->reg = timing_info->phy_f2_cfg[i].reg;
+ cfg->val = timing_info->phy_f2_cfg[i].val;
+ cfg++;
+ }
+}
+
+int ddr_init(struct dram_timing_info2 *dram_timing)
+{
+ int i;
+
+ if (IS_ENABLED(CONFIG_IMX8ULP_DRAM_PHY_PLL_BYPASS)) {
+ /* Use PLL bypass for boot freq */
+ /* Since PLL can't generate the double freq, Need ddr clock to generate it. */
+ set_ddr_clk(dram_timing->fsp_table[0]); /* Set to boot freq */
+ setbits_le32(AVD_SIM_BASE_ADDR, 0x1); /* SIM_DDR_CTRL_DIV2_EN */
+ } else {
+ set_ddr_clk(dram_timing->fsp_table[0] >> 1); /* Set to boot freq */
+ clrbits_le32(AVD_SIM_BASE_ADDR, 0x1); /* SIM_DDR_CTRL_DIV2_EN */
+ }
+
+ /* save the dram config into sram for low power mode */
+ save_dram_config(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+
+ /* Initialize CTL registers */
+ for (i = 0; i < dram_timing->ctl_cfg_num; i++)
+ writel(dram_timing->ctl_cfg[i].val, (ulong)dram_timing->ctl_cfg[i].reg);
+
+ /* Initialize PI registers */
+ for (i = 0; i < dram_timing->pi_cfg_num; i++)
+ writel(dram_timing->pi_cfg[i].val, (ulong)dram_timing->pi_cfg[i].reg);
+
+ /* Write PHY regiters for all 3 frequency points (48Mhz/384Mhz/528Mhz): f1_index=0 */
+ writel(PHY_FREQ_SEL_MULTICAST_EN(1) | PHY_FREQ_SEL_INDEX(0), DENALI_PHY_1537);
+ for (i = 0; i < dram_timing->phy_f1_cfg_num; i++)
+ writel(dram_timing->phy_f1_cfg[i].val, (ulong)dram_timing->phy_f1_cfg[i].reg);
+
+ /* Write PHY regiters for freqency point 2 (528Mhz): f2_index=1 */
+ writel(PHY_FREQ_SEL_MULTICAST_EN(0) | PHY_FREQ_SEL_INDEX(1), DENALI_PHY_1537);
+ for (i = 0; i < dram_timing->phy_f2_cfg_num; i++)
+ writel(dram_timing->phy_f2_cfg[i].val, (ulong)dram_timing->phy_f2_cfg[i].reg);
+
+ /* Re-enable MULTICAST mode */
+ writel(PHY_FREQ_SEL_MULTICAST_EN(1) | PHY_FREQ_SEL_INDEX(0), DENALI_PHY_1537);
+
+ return ddr_calibration(dram_timing->fsp_table);
+}
diff --git a/drivers/ddr/imx/imx9/Kconfig b/drivers/ddr/imx/imx9/Kconfig
new file mode 100644
index 00000000000..b1795eec353
--- /dev/null
+++ b/drivers/ddr/imx/imx9/Kconfig
@@ -0,0 +1,27 @@
+menu "i.MX9 DDR controllers"
+ depends on ARCH_IMX9
+
+config IMX9_DRAM
+ bool "imx9 dram"
+ select IMX_SNPS_DDR_PHY
+
+config IMX9_LPDDR4X
+ bool "imx9 lpddr4 and lpddr4x"
+ select IMX9_DRAM
+ help
+ Select the i.MX9 LPDDR4/4X driver support on i.MX9 SOC.
+
+config IMX9_DRAM_PM_COUNTER
+ bool "imx9 DDRC performance monitor counter"
+ default y
+ help
+ Enable DDR controller performance monitor counter for reference events.
+
+config SAVED_DRAM_TIMING_BASE
+ hex "Define the base address for saved dram timing"
+ help
+ after DRAM is trained, need to save the dram related timming
+ info into memory for low power use.
+ default 0x2051C000
+
+endmenu
diff --git a/drivers/ddr/imx/imx9/Makefile b/drivers/ddr/imx/imx9/Makefile
new file mode 100644
index 00000000000..9403f988b32
--- /dev/null
+++ b/drivers/ddr/imx/imx9/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright 2018 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_IMX9_DRAM) += ddr_init.o
+obj-y += ../phy/
+endif
diff --git a/drivers/ddr/imx/imx9/ddr_init.c b/drivers/ddr/imx/imx9/ddr_init.c
new file mode 100644
index 00000000000..5b0ad773875
--- /dev/null
+++ b/drivers/ddr/imx/imx9/ddr_init.c
@@ -0,0 +1,772 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ */
+
+#include <errno.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+static unsigned int g_cdd_rr_max[4];
+static unsigned int g_cdd_rw_max[4];
+static unsigned int g_cdd_wr_max[4];
+static unsigned int g_cdd_ww_max[4];
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+void ddrphy_coldreset(void)
+{
+ /* dramphy_apb_n default 1 , assert -> 0, de_assert -> 1 */
+ /* dramphy_reset_n default 0 , assert -> 0, de_assert -> 1 */
+ /* dramphy_PwrOKIn default 0 , assert -> 1, de_assert -> 0 */
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ /* src_gen_dphy_PwrOKIn_sw_rst_de_assert() */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_assert */
+ setbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+ mdelay(10);
+ /* src_gen_dphy_PwrOKIn_sw_rst_assert */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(0));
+ mdelay(10);
+
+ /* src_gen_dphy_apb_sw_rst_de_assert */
+ clrbits_le32(REG_SRC_DPHY_SW_CTRL, BIT(0));
+ /* src_gen_dphy_sw_rst_de_assert() */
+ clrbits_le32(REG_SRC_DPHY_SINGLE_RESET_SW_CTRL, BIT(2));
+}
+
+void check_ddrc_idle(void)
+{
+ u32 regval;
+
+ do {
+ regval = readl(REG_DDRDSR_2);
+ if (regval & BIT(31))
+ break;
+ } while (1);
+}
+
+void check_dfi_init_complete(void)
+{
+ u32 regval;
+
+ do {
+ regval = readl(REG_DDRDSR_2);
+ if (regval & BIT(2))
+ break;
+ } while (1);
+ setbits_le32(REG_DDRDSR_2, BIT(2));
+}
+
+void ddrc_config(struct dram_timing_info *dram_timing)
+{
+ u32 num = dram_timing->ddrc_cfg_num;
+ struct dram_cfg_param *ddrc_config;
+ int i = 0;
+
+ ddrc_config = dram_timing->ddrc_cfg;
+ for (i = 0; i < num; i++) {
+ writel(ddrc_config->val, (ulong)ddrc_config->reg);
+ ddrc_config++;
+ }
+
+ if (dram_timing->fsp_cfg) {
+ ddrc_config = dram_timing->fsp_cfg[0].ddrc_cfg;
+ while (ddrc_config->reg != 0) {
+ writel(ddrc_config->val, (ulong)ddrc_config->reg);
+ ddrc_config++;
+ }
+ }
+}
+
+static unsigned int look_for_max(unsigned int data[], unsigned int addr_start,
+ unsigned int addr_end)
+{
+ unsigned int i, imax = 0;
+
+ for (i = addr_start; i <= addr_end; i++) {
+ if (((data[i] >> 7) == 0) && data[i] > imax)
+ imax = data[i];
+ }
+
+ return imax;
+}
+
+void get_trained_CDD(u32 fsp)
+{
+ unsigned int i, tmp;
+ unsigned int cdd_cha[12], cdd_chb[12];
+ unsigned int cdd_cha_rr_max, cdd_cha_rw_max, cdd_cha_wr_max, cdd_cha_ww_max;
+ unsigned int cdd_chb_rr_max, cdd_chb_rw_max, cdd_chb_wr_max, cdd_chb_ww_max;
+
+ for (i = 0; i < 6; i++) {
+ tmp = dwc_ddrphy_apb_rd(0x54013 + i);
+ cdd_cha[i * 2] = tmp & 0xff;
+ cdd_cha[i * 2 + 1] = (tmp >> 8) & 0xff;
+ }
+
+ for (i = 0; i < 7; i++) {
+ tmp = dwc_ddrphy_apb_rd(0x5402c + i);
+
+ if (i == 0) {
+ cdd_chb[0] = (tmp >> 8) & 0xff;
+ } else if (i == 6) {
+ cdd_chb[11] = tmp & 0xff;
+ } else {
+ cdd_chb[i * 2 - 1] = tmp & 0xff;
+ cdd_chb[i * 2] = (tmp >> 8) & 0xff;
+ }
+ }
+
+ cdd_cha_rr_max = look_for_max(cdd_cha, 0, 1);
+ cdd_cha_rw_max = look_for_max(cdd_cha, 2, 5);
+ cdd_cha_wr_max = look_for_max(cdd_cha, 6, 9);
+ cdd_cha_ww_max = look_for_max(cdd_cha, 10, 11);
+ cdd_chb_rr_max = look_for_max(cdd_chb, 0, 1);
+ cdd_chb_rw_max = look_for_max(cdd_chb, 2, 5);
+ cdd_chb_wr_max = look_for_max(cdd_chb, 6, 9);
+ cdd_chb_ww_max = look_for_max(cdd_chb, 10, 11);
+ g_cdd_rr_max[fsp] = cdd_cha_rr_max > cdd_chb_rr_max ? cdd_cha_rr_max : cdd_chb_rr_max;
+ g_cdd_rw_max[fsp] = cdd_cha_rw_max > cdd_chb_rw_max ? cdd_cha_rw_max : cdd_chb_rw_max;
+ g_cdd_wr_max[fsp] = cdd_cha_wr_max > cdd_chb_wr_max ? cdd_cha_wr_max : cdd_chb_wr_max;
+ g_cdd_ww_max[fsp] = cdd_cha_ww_max > cdd_chb_ww_max ? cdd_cha_ww_max : cdd_chb_ww_max;
+}
+
+static u32 ddrc_get_fsp_reg_setting(struct dram_cfg_param *ddrc_cfg, unsigned int cfg_num, u32 reg)
+{
+ unsigned int i;
+
+ for (i = 0; i < cfg_num; i++) {
+ if (reg == ddrc_cfg[i].reg)
+ return ddrc_cfg[i].val;
+ }
+
+ return 0;
+}
+
+static void ddrc_update_fsp_reg_setting(struct dram_cfg_param *ddrc_cfg, int cfg_num,
+ u32 reg, u32 val)
+{
+ unsigned int i;
+
+ for (i = 0; i < cfg_num; i++) {
+ if (reg == ddrc_cfg[i].reg) {
+ ddrc_cfg[i].val = val;
+ return;
+ }
+ }
+}
+
+void update_umctl2_rank_space_setting(struct dram_timing_info *dram_timing, unsigned int pstat_num)
+{
+ u32 tmp, tmp_t;
+ u32 wwt, rrt, wrt, rwt;
+ u32 ext_wwt, ext_rrt, ext_wrt, ext_rwt;
+ u32 max_wwt, max_rrt, max_wrt, max_rwt;
+ u32 i;
+
+ for (i = 0; i < pstat_num; i++) {
+ /* read wwt, rrt, wrt, rwt fields from timing_cfg_0 */
+ if (!dram_timing->fsp_cfg_num) {
+ tmp = ddrc_get_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_0);
+ } else {
+ tmp = ddrc_get_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_0);
+ }
+ wwt = (tmp >> 24) & 0x3;
+ rrt = (tmp >> 26) & 0x3;
+ wrt = (tmp >> 28) & 0x3;
+ rwt = (tmp >> 30) & 0x3;
+
+ /* read rxt_wwt, ext_rrt, ext_wrt, ext_rwt fields from timing_cfg_4 */
+ if (!dram_timing->fsp_cfg_num) {
+ tmp_t = ddrc_get_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_4);
+ } else {
+ tmp_t = ddrc_get_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_4);
+ }
+ ext_wwt = (tmp_t >> 8) & 0x3;
+ ext_rrt = (tmp_t >> 10) & 0x3;
+ ext_wrt = (tmp_t >> 12) & 0x3;
+ ext_rwt = (tmp_t >> 14) & 0x3;
+
+ wwt = (ext_wwt << 2) | wwt;
+ rrt = (ext_rrt << 2) | rrt;
+ wrt = (ext_wrt << 2) | wrt;
+ rwt = (ext_rwt << 2) | rwt;
+
+ max_wwt = MAX(g_cdd_ww_max[0], wwt);
+ max_rrt = MAX(g_cdd_rr_max[0], rrt);
+ max_wrt = MAX(g_cdd_wr_max[0], wrt);
+ max_rwt = MAX(g_cdd_rw_max[0], rwt);
+ /* verify values to see if are bigger then 15 (4 bits) */
+ if (max_wwt > 15)
+ max_wwt = 15;
+ if (max_rrt > 15)
+ max_rrt = 15;
+ if (max_wrt > 15)
+ max_wrt = 15;
+ if (max_rwt > 15)
+ max_rwt = 15;
+
+ /* recalculate timings for controller registers */
+ wwt = max_wwt & 0x3;
+ rrt = max_rrt & 0x3;
+ wrt = max_wrt & 0x3;
+ rwt = max_rwt & 0x3;
+
+ ext_wwt = (max_wwt & 0xC) >> 2;
+ ext_rrt = (max_rrt & 0xC) >> 2;
+ ext_wrt = (max_wrt & 0xC) >> 2;
+ ext_rwt = (max_rwt & 0xC) >> 2;
+
+ /* update timing_cfg_0 and timing_cfg_4 */
+ tmp = (tmp & 0x00ffffff) | (rwt << 30) | (wrt << 28) |
+ (rrt << 26) | (wwt << 24);
+ tmp_t = (tmp_t & 0xFFFF00FF) | (ext_rwt << 14) |
+ (ext_wrt << 12) | (ext_rrt << 10) | (ext_wwt << 8);
+
+ if (!dram_timing->fsp_cfg_num) {
+ ddrc_update_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_0, tmp);
+ ddrc_update_fsp_reg_setting(dram_timing->ddrc_cfg,
+ dram_timing->ddrc_cfg_num,
+ REG_DDR_TIMING_CFG_4, tmp_t);
+ } else {
+ ddrc_update_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_0, tmp);
+ ddrc_update_fsp_reg_setting(dram_timing->fsp_cfg[i].ddrc_cfg,
+ ARRAY_SIZE(dram_timing->fsp_cfg[i].ddrc_cfg),
+ REG_DDR_TIMING_CFG_4, tmp_t);
+ }
+ }
+}
+
+u32 ddrc_mrr(u32 chip_select, u32 mode_reg_num, u32 *mode_reg_val)
+{
+ u32 temp;
+
+ writel(0x80000000, REG_DDR_SDRAM_MD_CNTL_2);
+ temp = 0x80000000 | (chip_select << 28) | (mode_reg_num << 0);
+ writel(temp, REG_DDR_SDRAM_MD_CNTL);
+ while ((readl(REG_DDR_SDRAM_MD_CNTL) & 0x80000000) == 0x80000000)
+ ;
+ while (!(readl(REG_DDR_SDRAM_MPR5)))
+ ;
+ *mode_reg_val = (readl(REG_DDR_SDRAM_MPR4) & 0xFF0000) >> 16;
+ writel(0x0, REG_DDR_SDRAM_MPR5);
+ while ((readl(REG_DDR_SDRAM_MPR5)))
+ ;
+ writel(0x0, REG_DDR_SDRAM_MPR4);
+ writel(0x0, REG_DDR_SDRAM_MD_CNTL_2);
+
+ return 0;
+}
+
+void ddrc_mrs(u32 cs_sel, u32 opcode, u32 mr)
+{
+ u32 regval;
+
+ regval = (cs_sel << 28) | (opcode << 6) | (mr);
+ writel(regval, REG_DDR_SDRAM_MD_CNTL);
+ setbits_le32(REG_DDR_SDRAM_MD_CNTL, BIT(31));
+ check_ddrc_idle();
+}
+
+u32 lpddr4_mr_read(u32 mr_rank, u32 mr_addr)
+{
+ u32 chip_select, regval;
+
+ if (mr_rank == 1)
+ chip_select = 0; /* CS0 */
+ else if (mr_rank == 2)
+ chip_select = 1; /* CS1 */
+ else
+ chip_select = 4; /* CS0 & CS1 */
+
+ ddrc_mrr(chip_select, mr_addr, &regval);
+
+ return regval;
+}
+
+void update_mr_fsp_op0(struct dram_cfg_param *cfg, unsigned int num)
+{
+ int i;
+
+ ddrc_mrs(0x4, 0x88, 13); /* FSP-OP->1, FSP-WR->0, VRCG=1, DMD=0 */
+ for (i = 0; i < num; i++) {
+ if (cfg[i].reg)
+ ddrc_mrs(0x4, cfg[i].val, cfg[i].reg);
+ }
+ ddrc_mrs(0x4, 0xc0, 13); /* FSP-OP->1, FSP-WR->1, VRCG=0, DMD=0 */
+}
+
+void save_trained_mr12_14(struct dram_cfg_param *cfg, u32 cfg_num, u32 mr12, u32 mr14)
+{
+ int i;
+
+ for (i = 0; i < cfg_num; i++) {
+ if (cfg->reg == 12)
+ cfg->val = mr12;
+ else if (cfg->reg == 14)
+ cfg->val = mr14;
+ cfg++;
+ }
+}
+
+int ddr_init(struct dram_timing_info *dram_timing)
+{
+ unsigned int initial_drate;
+ struct dram_timing_info *saved_timing;
+ void *fsp;
+ int ret;
+ u32 mr12, mr14;
+ u32 regval;
+
+ debug("DDRINFO: start DRAM init\n");
+
+ /* reset ddrphy */
+ ddrphy_coldreset();
+
+ debug("DDRINFO: cfg clk\n");
+
+ initial_drate = dram_timing->fsp_msg[0].drate;
+ /* default to the frequency point 0 clock */
+ ddrphy_init_set_dfi_clk(initial_drate);
+
+ /*
+ * Start PHY initialization and training by
+ * accessing relevant PUB registers
+ */
+ debug("DDRINFO:ddrphy config start\n");
+
+ ret = ddr_cfg_phy(dram_timing);
+ if (ret)
+ return ret;
+
+ debug("DDRINFO: ddrphy config done\n");
+
+ update_umctl2_rank_space_setting(dram_timing, dram_timing->fsp_msg_num - 1);
+
+ /* rogram the ddrc registers */
+ debug("DDRINFO: ddrc config start\n");
+ ddrc_config(dram_timing);
+ debug("DDRINFO: ddrc config done\n");
+
+#ifdef CONFIG_IMX9_DRAM_PM_COUNTER
+ writel(0x200000, REG_DDR_DEBUG_19);
+#endif
+
+ check_dfi_init_complete();
+
+ regval = readl(REG_DDR_SDRAM_CFG);
+ writel((regval | 0x80000000), REG_DDR_SDRAM_CFG);
+
+ check_ddrc_idle();
+
+ mr12 = lpddr4_mr_read(1, 12);
+ mr14 = lpddr4_mr_read(1, 14);
+
+ /* save the dram timing config into memory */
+ fsp = dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
+
+ saved_timing = (struct dram_timing_info *)CONFIG_SAVED_DRAM_TIMING_BASE;
+ saved_timing->fsp_cfg = fsp;
+ saved_timing->fsp_cfg_num = dram_timing->fsp_cfg_num;
+ if (saved_timing->fsp_cfg_num) {
+ memcpy(saved_timing->fsp_cfg, dram_timing->fsp_cfg,
+ dram_timing->fsp_cfg_num * sizeof(struct dram_fsp_cfg));
+
+ save_trained_mr12_14(saved_timing->fsp_cfg[0].mr_cfg,
+ ARRAY_SIZE(saved_timing->fsp_cfg[0].mr_cfg), mr12, mr14);
+ /*
+ * Configure mode registers in fsp1 to mode register 0 because DDRC
+ * doesn't automatically set.
+ */
+ if (saved_timing->fsp_cfg_num > 1)
+ update_mr_fsp_op0(saved_timing->fsp_cfg[1].mr_cfg,
+ ARRAY_SIZE(saved_timing->fsp_cfg[1].mr_cfg));
+ }
+
+ return 0;
+}
+
+ulong ddrphy_addr_remap(u32 paddr_apb_from_ctlr)
+{
+ u32 paddr_apb_qual;
+ u32 paddr_apb_unqual_dec_22_13;
+ u32 paddr_apb_unqual_dec_19_13;
+ u32 paddr_apb_unqual_dec_12_1;
+ u32 paddr_apb_unqual;
+ u32 paddr_apb_phy;
+
+ paddr_apb_qual = (paddr_apb_from_ctlr << 1);
+ paddr_apb_unqual_dec_22_13 = ((paddr_apb_qual & 0x7fe000) >> 13);
+ paddr_apb_unqual_dec_12_1 = ((paddr_apb_qual & 0x1ffe) >> 1);
+
+ switch (paddr_apb_unqual_dec_22_13) {
+ case 0x000:
+ paddr_apb_unqual_dec_19_13 = 0x00;
+ break;
+ case 0x001:
+ paddr_apb_unqual_dec_19_13 = 0x01;
+ break;
+ case 0x002:
+ paddr_apb_unqual_dec_19_13 = 0x02;
+ break;
+ case 0x003:
+ paddr_apb_unqual_dec_19_13 = 0x03;
+ break;
+ case 0x004:
+ paddr_apb_unqual_dec_19_13 = 0x04;
+ break;
+ case 0x005:
+ paddr_apb_unqual_dec_19_13 = 0x05;
+ break;
+ case 0x006:
+ paddr_apb_unqual_dec_19_13 = 0x06;
+ break;
+ case 0x007:
+ paddr_apb_unqual_dec_19_13 = 0x07;
+ break;
+ case 0x008:
+ paddr_apb_unqual_dec_19_13 = 0x08;
+ break;
+ case 0x009:
+ paddr_apb_unqual_dec_19_13 = 0x09;
+ break;
+ case 0x00a:
+ paddr_apb_unqual_dec_19_13 = 0x0a;
+ break;
+ case 0x00b:
+ paddr_apb_unqual_dec_19_13 = 0x0b;
+ break;
+ case 0x100:
+ paddr_apb_unqual_dec_19_13 = 0x0c;
+ break;
+ case 0x101:
+ paddr_apb_unqual_dec_19_13 = 0x0d;
+ break;
+ case 0x102:
+ paddr_apb_unqual_dec_19_13 = 0x0e;
+ break;
+ case 0x103:
+ paddr_apb_unqual_dec_19_13 = 0x0f;
+ break;
+ case 0x104:
+ paddr_apb_unqual_dec_19_13 = 0x10;
+ break;
+ case 0x105:
+ paddr_apb_unqual_dec_19_13 = 0x11;
+ break;
+ case 0x106:
+ paddr_apb_unqual_dec_19_13 = 0x12;
+ break;
+ case 0x107:
+ paddr_apb_unqual_dec_19_13 = 0x13;
+ break;
+ case 0x108:
+ paddr_apb_unqual_dec_19_13 = 0x14;
+ break;
+ case 0x109:
+ paddr_apb_unqual_dec_19_13 = 0x15;
+ break;
+ case 0x10a:
+ paddr_apb_unqual_dec_19_13 = 0x16;
+ break;
+ case 0x10b:
+ paddr_apb_unqual_dec_19_13 = 0x17;
+ break;
+ case 0x200:
+ paddr_apb_unqual_dec_19_13 = 0x18;
+ break;
+ case 0x201:
+ paddr_apb_unqual_dec_19_13 = 0x19;
+ break;
+ case 0x202:
+ paddr_apb_unqual_dec_19_13 = 0x1a;
+ break;
+ case 0x203:
+ paddr_apb_unqual_dec_19_13 = 0x1b;
+ break;
+ case 0x204:
+ paddr_apb_unqual_dec_19_13 = 0x1c;
+ break;
+ case 0x205:
+ paddr_apb_unqual_dec_19_13 = 0x1d;
+ break;
+ case 0x206:
+ paddr_apb_unqual_dec_19_13 = 0x1e;
+ break;
+ case 0x207:
+ paddr_apb_unqual_dec_19_13 = 0x1f;
+ break;
+ case 0x208:
+ paddr_apb_unqual_dec_19_13 = 0x20;
+ break;
+ case 0x209:
+ paddr_apb_unqual_dec_19_13 = 0x21;
+ break;
+ case 0x20a:
+ paddr_apb_unqual_dec_19_13 = 0x22;
+ break;
+ case 0x20b:
+ paddr_apb_unqual_dec_19_13 = 0x23;
+ break;
+ case 0x300:
+ paddr_apb_unqual_dec_19_13 = 0x24;
+ break;
+ case 0x301:
+ paddr_apb_unqual_dec_19_13 = 0x25;
+ break;
+ case 0x302:
+ paddr_apb_unqual_dec_19_13 = 0x26;
+ break;
+ case 0x303:
+ paddr_apb_unqual_dec_19_13 = 0x27;
+ break;
+ case 0x304:
+ paddr_apb_unqual_dec_19_13 = 0x28;
+ break;
+ case 0x305:
+ paddr_apb_unqual_dec_19_13 = 0x29;
+ break;
+ case 0x306:
+ paddr_apb_unqual_dec_19_13 = 0x2a;
+ break;
+ case 0x307:
+ paddr_apb_unqual_dec_19_13 = 0x2b;
+ break;
+ case 0x308:
+ paddr_apb_unqual_dec_19_13 = 0x2c;
+ break;
+ case 0x309:
+ paddr_apb_unqual_dec_19_13 = 0x2d;
+ break;
+ case 0x30a:
+ paddr_apb_unqual_dec_19_13 = 0x2e;
+ break;
+ case 0x30b:
+ paddr_apb_unqual_dec_19_13 = 0x2f;
+ break;
+ case 0x010:
+ paddr_apb_unqual_dec_19_13 = 0x30;
+ break;
+ case 0x011:
+ paddr_apb_unqual_dec_19_13 = 0x31;
+ break;
+ case 0x012:
+ paddr_apb_unqual_dec_19_13 = 0x32;
+ break;
+ case 0x013:
+ paddr_apb_unqual_dec_19_13 = 0x33;
+ break;
+ case 0x014:
+ paddr_apb_unqual_dec_19_13 = 0x34;
+ break;
+ case 0x015:
+ paddr_apb_unqual_dec_19_13 = 0x35;
+ break;
+ case 0x016:
+ paddr_apb_unqual_dec_19_13 = 0x36;
+ break;
+ case 0x017:
+ paddr_apb_unqual_dec_19_13 = 0x37;
+ break;
+ case 0x018:
+ paddr_apb_unqual_dec_19_13 = 0x38;
+ break;
+ case 0x019:
+ paddr_apb_unqual_dec_19_13 = 0x39;
+ break;
+ case 0x110:
+ paddr_apb_unqual_dec_19_13 = 0x3a;
+ break;
+ case 0x111:
+ paddr_apb_unqual_dec_19_13 = 0x3b;
+ break;
+ case 0x112:
+ paddr_apb_unqual_dec_19_13 = 0x3c;
+ break;
+ case 0x113:
+ paddr_apb_unqual_dec_19_13 = 0x3d;
+ break;
+ case 0x114:
+ paddr_apb_unqual_dec_19_13 = 0x3e;
+ break;
+ case 0x115:
+ paddr_apb_unqual_dec_19_13 = 0x3f;
+ break;
+ case 0x116:
+ paddr_apb_unqual_dec_19_13 = 0x40;
+ break;
+ case 0x117:
+ paddr_apb_unqual_dec_19_13 = 0x41;
+ break;
+ case 0x118:
+ paddr_apb_unqual_dec_19_13 = 0x42;
+ break;
+ case 0x119:
+ paddr_apb_unqual_dec_19_13 = 0x43;
+ break;
+ case 0x210:
+ paddr_apb_unqual_dec_19_13 = 0x44;
+ break;
+ case 0x211:
+ paddr_apb_unqual_dec_19_13 = 0x45;
+ break;
+ case 0x212:
+ paddr_apb_unqual_dec_19_13 = 0x46;
+ break;
+ case 0x213:
+ paddr_apb_unqual_dec_19_13 = 0x47;
+ break;
+ case 0x214:
+ paddr_apb_unqual_dec_19_13 = 0x48;
+ break;
+ case 0x215:
+ paddr_apb_unqual_dec_19_13 = 0x49;
+ break;
+ case 0x216:
+ paddr_apb_unqual_dec_19_13 = 0x4a;
+ break;
+ case 0x217:
+ paddr_apb_unqual_dec_19_13 = 0x4b;
+ break;
+ case 0x218:
+ paddr_apb_unqual_dec_19_13 = 0x4c;
+ break;
+ case 0x219:
+ paddr_apb_unqual_dec_19_13 = 0x4d;
+ break;
+ case 0x310:
+ paddr_apb_unqual_dec_19_13 = 0x4e;
+ break;
+ case 0x311:
+ paddr_apb_unqual_dec_19_13 = 0x4f;
+ break;
+ case 0x312:
+ paddr_apb_unqual_dec_19_13 = 0x50;
+ break;
+ case 0x313:
+ paddr_apb_unqual_dec_19_13 = 0x51;
+ break;
+ case 0x314:
+ paddr_apb_unqual_dec_19_13 = 0x52;
+ break;
+ case 0x315:
+ paddr_apb_unqual_dec_19_13 = 0x53;
+ break;
+ case 0x316:
+ paddr_apb_unqual_dec_19_13 = 0x54;
+ break;
+ case 0x317:
+ paddr_apb_unqual_dec_19_13 = 0x55;
+ break;
+ case 0x318:
+ paddr_apb_unqual_dec_19_13 = 0x56;
+ break;
+ case 0x319:
+ paddr_apb_unqual_dec_19_13 = 0x57;
+ break;
+ case 0x020:
+ paddr_apb_unqual_dec_19_13 = 0x58;
+ break;
+ case 0x120:
+ paddr_apb_unqual_dec_19_13 = 0x59;
+ break;
+ case 0x220:
+ paddr_apb_unqual_dec_19_13 = 0x5a;
+ break;
+ case 0x320:
+ paddr_apb_unqual_dec_19_13 = 0x5b;
+ break;
+ case 0x040:
+ paddr_apb_unqual_dec_19_13 = 0x5c;
+ break;
+ case 0x140:
+ paddr_apb_unqual_dec_19_13 = 0x5d;
+ break;
+ case 0x240:
+ paddr_apb_unqual_dec_19_13 = 0x5e;
+ break;
+ case 0x340:
+ paddr_apb_unqual_dec_19_13 = 0x5f;
+ break;
+ case 0x050:
+ paddr_apb_unqual_dec_19_13 = 0x60;
+ break;
+ case 0x051:
+ paddr_apb_unqual_dec_19_13 = 0x61;
+ break;
+ case 0x052:
+ paddr_apb_unqual_dec_19_13 = 0x62;
+ break;
+ case 0x053:
+ paddr_apb_unqual_dec_19_13 = 0x63;
+ break;
+ case 0x054:
+ paddr_apb_unqual_dec_19_13 = 0x64;
+ break;
+ case 0x055:
+ paddr_apb_unqual_dec_19_13 = 0x65;
+ break;
+ case 0x056:
+ paddr_apb_unqual_dec_19_13 = 0x66;
+ break;
+ case 0x057:
+ paddr_apb_unqual_dec_19_13 = 0x67;
+ break;
+ case 0x070:
+ paddr_apb_unqual_dec_19_13 = 0x68;
+ break;
+ case 0x090:
+ paddr_apb_unqual_dec_19_13 = 0x69;
+ break;
+ case 0x190:
+ paddr_apb_unqual_dec_19_13 = 0x6a;
+ break;
+ case 0x290:
+ paddr_apb_unqual_dec_19_13 = 0x6b;
+ break;
+ case 0x390:
+ paddr_apb_unqual_dec_19_13 = 0x6c;
+ break;
+ case 0x0c0:
+ paddr_apb_unqual_dec_19_13 = 0x6d;
+ break;
+ case 0x0d0:
+ paddr_apb_unqual_dec_19_13 = 0x6e;
+ break;
+ default:
+ paddr_apb_unqual_dec_19_13 = 0x00;
+ break;
+ }
+
+ paddr_apb_unqual = ((paddr_apb_unqual_dec_19_13 << 13) | (paddr_apb_unqual_dec_12_1 << 1));
+
+ paddr_apb_phy = (paddr_apb_unqual << 1);
+
+ return paddr_apb_phy;
+}
diff --git a/drivers/ddr/imx/phy/Kconfig b/drivers/ddr/imx/phy/Kconfig
new file mode 100644
index 00000000000..d3e589b23c4
--- /dev/null
+++ b/drivers/ddr/imx/phy/Kconfig
@@ -0,0 +1,4 @@
+config IMX_SNPS_DDR_PHY
+ bool "i.MX Snopsys DDR PHY"
+ help
+ Select the DDR PHY driver support on i.MX8M and i.MX9 SOC.
diff --git a/drivers/ddr/imx/phy/Makefile b/drivers/ddr/imx/phy/Makefile
new file mode 100644
index 00000000000..bb3d4ee5b74
--- /dev/null
+++ b/drivers/ddr/imx/phy/Makefile
@@ -0,0 +1,9 @@
+#
+# Copyright 2018 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_IMX_SNPS_DDR_PHY) += helper.o ddrphy_utils.o ddrphy_train.o ddrphy_csr.o
+endif
diff --git a/drivers/ddr/imx/phy/ddrphy_csr.c b/drivers/ddr/imx/phy/ddrphy_csr.c
new file mode 100644
index 00000000000..67dd4e7059f
--- /dev/null
+++ b/drivers/ddr/imx/phy/ddrphy_csr.c
@@ -0,0 +1,732 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+#include <linux/kernel.h>
+#include <asm/arch/ddr.h>
+
+/* ddr phy trained csr */
+struct dram_cfg_param ddrphy_trained_csr[] = {
+ { 0x200b2, 0x0 },
+ { 0x1200b2, 0x0 },
+ { 0x2200b2, 0x0 },
+ { 0x200cb, 0x0 },
+ { 0x10043, 0x0 },
+ { 0x110043, 0x0 },
+ { 0x210043, 0x0 },
+ { 0x10143, 0x0 },
+ { 0x110143, 0x0 },
+ { 0x210143, 0x0 },
+ { 0x11043, 0x0 },
+ { 0x111043, 0x0 },
+ { 0x211043, 0x0 },
+ { 0x11143, 0x0 },
+ { 0x111143, 0x0 },
+ { 0x211143, 0x0 },
+ { 0x12043, 0x0 },
+ { 0x112043, 0x0 },
+ { 0x212043, 0x0 },
+ { 0x12143, 0x0 },
+ { 0x112143, 0x0 },
+ { 0x212143, 0x0 },
+ { 0x13043, 0x0 },
+ { 0x113043, 0x0 },
+ { 0x213043, 0x0 },
+ { 0x13143, 0x0 },
+ { 0x113143, 0x0 },
+ { 0x213143, 0x0 },
+ { 0x80, 0x0 },
+ { 0x100080, 0x0 },
+ { 0x200080, 0x0 },
+ { 0x1080, 0x0 },
+ { 0x101080, 0x0 },
+ { 0x201080, 0x0 },
+ { 0x2080, 0x0 },
+ { 0x102080, 0x0 },
+ { 0x202080, 0x0 },
+ { 0x3080, 0x0 },
+ { 0x103080, 0x0 },
+ { 0x203080, 0x0 },
+ { 0x4080, 0x0 },
+ { 0x104080, 0x0 },
+ { 0x204080, 0x0 },
+ { 0x5080, 0x0 },
+ { 0x105080, 0x0 },
+ { 0x205080, 0x0 },
+ { 0x6080, 0x0 },
+ { 0x106080, 0x0 },
+ { 0x206080, 0x0 },
+ { 0x7080, 0x0 },
+ { 0x107080, 0x0 },
+ { 0x207080, 0x0 },
+ { 0x8080, 0x0 },
+ { 0x108080, 0x0 },
+ { 0x208080, 0x0 },
+ { 0x9080, 0x0 },
+ { 0x109080, 0x0 },
+ { 0x209080, 0x0 },
+ { 0x10080, 0x0 },
+ { 0x110080, 0x0 },
+ { 0x210080, 0x0 },
+ { 0x10180, 0x0 },
+ { 0x110180, 0x0 },
+ { 0x210180, 0x0 },
+ { 0x11080, 0x0 },
+ { 0x111080, 0x0 },
+ { 0x211080, 0x0 },
+ { 0x11180, 0x0 },
+ { 0x111180, 0x0 },
+ { 0x211180, 0x0 },
+ { 0x12080, 0x0 },
+ { 0x112080, 0x0 },
+ { 0x212080, 0x0 },
+ { 0x12180, 0x0 },
+ { 0x112180, 0x0 },
+ { 0x212180, 0x0 },
+ { 0x13080, 0x0 },
+ { 0x113080, 0x0 },
+ { 0x213080, 0x0 },
+ { 0x13180, 0x0 },
+ { 0x113180, 0x0 },
+ { 0x213180, 0x0 },
+ { 0x10081, 0x0 },
+ { 0x110081, 0x0 },
+ { 0x210081, 0x0 },
+ { 0x10181, 0x0 },
+ { 0x110181, 0x0 },
+ { 0x210181, 0x0 },
+ { 0x11081, 0x0 },
+ { 0x111081, 0x0 },
+ { 0x211081, 0x0 },
+ { 0x11181, 0x0 },
+ { 0x111181, 0x0 },
+ { 0x211181, 0x0 },
+ { 0x12081, 0x0 },
+ { 0x112081, 0x0 },
+ { 0x212081, 0x0 },
+ { 0x12181, 0x0 },
+ { 0x112181, 0x0 },
+ { 0x212181, 0x0 },
+ { 0x13081, 0x0 },
+ { 0x113081, 0x0 },
+ { 0x213081, 0x0 },
+ { 0x13181, 0x0 },
+ { 0x113181, 0x0 },
+ { 0x213181, 0x0 },
+ { 0x100d0, 0x0 },
+ { 0x1100d0, 0x0 },
+ { 0x2100d0, 0x0 },
+ { 0x101d0, 0x0 },
+ { 0x1101d0, 0x0 },
+ { 0x2101d0, 0x0 },
+ { 0x110d0, 0x0 },
+ { 0x1110d0, 0x0 },
+ { 0x2110d0, 0x0 },
+ { 0x111d0, 0x0 },
+ { 0x1111d0, 0x0 },
+ { 0x2111d0, 0x0 },
+ { 0x120d0, 0x0 },
+ { 0x1120d0, 0x0 },
+ { 0x2120d0, 0x0 },
+ { 0x121d0, 0x0 },
+ { 0x1121d0, 0x0 },
+ { 0x2121d0, 0x0 },
+ { 0x130d0, 0x0 },
+ { 0x1130d0, 0x0 },
+ { 0x2130d0, 0x0 },
+ { 0x131d0, 0x0 },
+ { 0x1131d0, 0x0 },
+ { 0x2131d0, 0x0 },
+ { 0x100d1, 0x0 },
+ { 0x1100d1, 0x0 },
+ { 0x2100d1, 0x0 },
+ { 0x101d1, 0x0 },
+ { 0x1101d1, 0x0 },
+ { 0x2101d1, 0x0 },
+ { 0x110d1, 0x0 },
+ { 0x1110d1, 0x0 },
+ { 0x2110d1, 0x0 },
+ { 0x111d1, 0x0 },
+ { 0x1111d1, 0x0 },
+ { 0x2111d1, 0x0 },
+ { 0x120d1, 0x0 },
+ { 0x1120d1, 0x0 },
+ { 0x2120d1, 0x0 },
+ { 0x121d1, 0x0 },
+ { 0x1121d1, 0x0 },
+ { 0x2121d1, 0x0 },
+ { 0x130d1, 0x0 },
+ { 0x1130d1, 0x0 },
+ { 0x2130d1, 0x0 },
+ { 0x131d1, 0x0 },
+ { 0x1131d1, 0x0 },
+ { 0x2131d1, 0x0 },
+ { 0x10068, 0x0 },
+ { 0x10168, 0x0 },
+ { 0x10268, 0x0 },
+ { 0x10368, 0x0 },
+ { 0x10468, 0x0 },
+ { 0x10568, 0x0 },
+ { 0x10668, 0x0 },
+ { 0x10768, 0x0 },
+ { 0x10868, 0x0 },
+ { 0x11068, 0x0 },
+ { 0x11168, 0x0 },
+ { 0x11268, 0x0 },
+ { 0x11368, 0x0 },
+ { 0x11468, 0x0 },
+ { 0x11568, 0x0 },
+ { 0x11668, 0x0 },
+ { 0x11768, 0x0 },
+ { 0x11868, 0x0 },
+ { 0x12068, 0x0 },
+ { 0x12168, 0x0 },
+ { 0x12268, 0x0 },
+ { 0x12368, 0x0 },
+ { 0x12468, 0x0 },
+ { 0x12568, 0x0 },
+ { 0x12668, 0x0 },
+ { 0x12768, 0x0 },
+ { 0x12868, 0x0 },
+ { 0x13068, 0x0 },
+ { 0x13168, 0x0 },
+ { 0x13268, 0x0 },
+ { 0x13368, 0x0 },
+ { 0x13468, 0x0 },
+ { 0x13568, 0x0 },
+ { 0x13668, 0x0 },
+ { 0x13768, 0x0 },
+ { 0x13868, 0x0 },
+ { 0x10069, 0x0 },
+ { 0x10169, 0x0 },
+ { 0x10269, 0x0 },
+ { 0x10369, 0x0 },
+ { 0x10469, 0x0 },
+ { 0x10569, 0x0 },
+ { 0x10669, 0x0 },
+ { 0x10769, 0x0 },
+ { 0x10869, 0x0 },
+ { 0x11069, 0x0 },
+ { 0x11169, 0x0 },
+ { 0x11269, 0x0 },
+ { 0x11369, 0x0 },
+ { 0x11469, 0x0 },
+ { 0x11569, 0x0 },
+ { 0x11669, 0x0 },
+ { 0x11769, 0x0 },
+ { 0x11869, 0x0 },
+ { 0x12069, 0x0 },
+ { 0x12169, 0x0 },
+ { 0x12269, 0x0 },
+ { 0x12369, 0x0 },
+ { 0x12469, 0x0 },
+ { 0x12569, 0x0 },
+ { 0x12669, 0x0 },
+ { 0x12769, 0x0 },
+ { 0x12869, 0x0 },
+ { 0x13069, 0x0 },
+ { 0x13169, 0x0 },
+ { 0x13269, 0x0 },
+ { 0x13369, 0x0 },
+ { 0x13469, 0x0 },
+ { 0x13569, 0x0 },
+ { 0x13669, 0x0 },
+ { 0x13769, 0x0 },
+ { 0x13869, 0x0 },
+ { 0x1008c, 0x0 },
+ { 0x11008c, 0x0 },
+ { 0x21008c, 0x0 },
+ { 0x1018c, 0x0 },
+ { 0x11018c, 0x0 },
+ { 0x21018c, 0x0 },
+ { 0x1108c, 0x0 },
+ { 0x11108c, 0x0 },
+ { 0x21108c, 0x0 },
+ { 0x1118c, 0x0 },
+ { 0x11118c, 0x0 },
+ { 0x21118c, 0x0 },
+ { 0x1208c, 0x0 },
+ { 0x11208c, 0x0 },
+ { 0x21208c, 0x0 },
+ { 0x1218c, 0x0 },
+ { 0x11218c, 0x0 },
+ { 0x21218c, 0x0 },
+ { 0x1308c, 0x0 },
+ { 0x11308c, 0x0 },
+ { 0x21308c, 0x0 },
+ { 0x1318c, 0x0 },
+ { 0x11318c, 0x0 },
+ { 0x21318c, 0x0 },
+ { 0x1008d, 0x0 },
+ { 0x11008d, 0x0 },
+ { 0x21008d, 0x0 },
+ { 0x1018d, 0x0 },
+ { 0x11018d, 0x0 },
+ { 0x21018d, 0x0 },
+ { 0x1108d, 0x0 },
+ { 0x11108d, 0x0 },
+ { 0x21108d, 0x0 },
+ { 0x1118d, 0x0 },
+ { 0x11118d, 0x0 },
+ { 0x21118d, 0x0 },
+ { 0x1208d, 0x0 },
+ { 0x11208d, 0x0 },
+ { 0x21208d, 0x0 },
+ { 0x1218d, 0x0 },
+ { 0x11218d, 0x0 },
+ { 0x21218d, 0x0 },
+ { 0x1308d, 0x0 },
+ { 0x11308d, 0x0 },
+ { 0x21308d, 0x0 },
+ { 0x1318d, 0x0 },
+ { 0x11318d, 0x0 },
+ { 0x21318d, 0x0 },
+ { 0x100c0, 0x0 },
+ { 0x1100c0, 0x0 },
+ { 0x2100c0, 0x0 },
+ { 0x101c0, 0x0 },
+ { 0x1101c0, 0x0 },
+ { 0x2101c0, 0x0 },
+ { 0x102c0, 0x0 },
+ { 0x1102c0, 0x0 },
+ { 0x2102c0, 0x0 },
+ { 0x103c0, 0x0 },
+ { 0x1103c0, 0x0 },
+ { 0x2103c0, 0x0 },
+ { 0x104c0, 0x0 },
+ { 0x1104c0, 0x0 },
+ { 0x2104c0, 0x0 },
+ { 0x105c0, 0x0 },
+ { 0x1105c0, 0x0 },
+ { 0x2105c0, 0x0 },
+ { 0x106c0, 0x0 },
+ { 0x1106c0, 0x0 },
+ { 0x2106c0, 0x0 },
+ { 0x107c0, 0x0 },
+ { 0x1107c0, 0x0 },
+ { 0x2107c0, 0x0 },
+ { 0x108c0, 0x0 },
+ { 0x1108c0, 0x0 },
+ { 0x2108c0, 0x0 },
+ { 0x110c0, 0x0 },
+ { 0x1110c0, 0x0 },
+ { 0x2110c0, 0x0 },
+ { 0x111c0, 0x0 },
+ { 0x1111c0, 0x0 },
+ { 0x2111c0, 0x0 },
+ { 0x112c0, 0x0 },
+ { 0x1112c0, 0x0 },
+ { 0x2112c0, 0x0 },
+ { 0x113c0, 0x0 },
+ { 0x1113c0, 0x0 },
+ { 0x2113c0, 0x0 },
+ { 0x114c0, 0x0 },
+ { 0x1114c0, 0x0 },
+ { 0x2114c0, 0x0 },
+ { 0x115c0, 0x0 },
+ { 0x1115c0, 0x0 },
+ { 0x2115c0, 0x0 },
+ { 0x116c0, 0x0 },
+ { 0x1116c0, 0x0 },
+ { 0x2116c0, 0x0 },
+ { 0x117c0, 0x0 },
+ { 0x1117c0, 0x0 },
+ { 0x2117c0, 0x0 },
+ { 0x118c0, 0x0 },
+ { 0x1118c0, 0x0 },
+ { 0x2118c0, 0x0 },
+ { 0x120c0, 0x0 },
+ { 0x1120c0, 0x0 },
+ { 0x2120c0, 0x0 },
+ { 0x121c0, 0x0 },
+ { 0x1121c0, 0x0 },
+ { 0x2121c0, 0x0 },
+ { 0x122c0, 0x0 },
+ { 0x1122c0, 0x0 },
+ { 0x2122c0, 0x0 },
+ { 0x123c0, 0x0 },
+ { 0x1123c0, 0x0 },
+ { 0x2123c0, 0x0 },
+ { 0x124c0, 0x0 },
+ { 0x1124c0, 0x0 },
+ { 0x2124c0, 0x0 },
+ { 0x125c0, 0x0 },
+ { 0x1125c0, 0x0 },
+ { 0x2125c0, 0x0 },
+ { 0x126c0, 0x0 },
+ { 0x1126c0, 0x0 },
+ { 0x2126c0, 0x0 },
+ { 0x127c0, 0x0 },
+ { 0x1127c0, 0x0 },
+ { 0x2127c0, 0x0 },
+ { 0x128c0, 0x0 },
+ { 0x1128c0, 0x0 },
+ { 0x2128c0, 0x0 },
+ { 0x130c0, 0x0 },
+ { 0x1130c0, 0x0 },
+ { 0x2130c0, 0x0 },
+ { 0x131c0, 0x0 },
+ { 0x1131c0, 0x0 },
+ { 0x2131c0, 0x0 },
+ { 0x132c0, 0x0 },
+ { 0x1132c0, 0x0 },
+ { 0x2132c0, 0x0 },
+ { 0x133c0, 0x0 },
+ { 0x1133c0, 0x0 },
+ { 0x2133c0, 0x0 },
+ { 0x134c0, 0x0 },
+ { 0x1134c0, 0x0 },
+ { 0x2134c0, 0x0 },
+ { 0x135c0, 0x0 },
+ { 0x1135c0, 0x0 },
+ { 0x2135c0, 0x0 },
+ { 0x136c0, 0x0 },
+ { 0x1136c0, 0x0 },
+ { 0x2136c0, 0x0 },
+ { 0x137c0, 0x0 },
+ { 0x1137c0, 0x0 },
+ { 0x2137c0, 0x0 },
+ { 0x138c0, 0x0 },
+ { 0x1138c0, 0x0 },
+ { 0x2138c0, 0x0 },
+ { 0x100c1, 0x0 },
+ { 0x1100c1, 0x0 },
+ { 0x2100c1, 0x0 },
+ { 0x101c1, 0x0 },
+ { 0x1101c1, 0x0 },
+ { 0x2101c1, 0x0 },
+ { 0x102c1, 0x0 },
+ { 0x1102c1, 0x0 },
+ { 0x2102c1, 0x0 },
+ { 0x103c1, 0x0 },
+ { 0x1103c1, 0x0 },
+ { 0x2103c1, 0x0 },
+ { 0x104c1, 0x0 },
+ { 0x1104c1, 0x0 },
+ { 0x2104c1, 0x0 },
+ { 0x105c1, 0x0 },
+ { 0x1105c1, 0x0 },
+ { 0x2105c1, 0x0 },
+ { 0x106c1, 0x0 },
+ { 0x1106c1, 0x0 },
+ { 0x2106c1, 0x0 },
+ { 0x107c1, 0x0 },
+ { 0x1107c1, 0x0 },
+ { 0x2107c1, 0x0 },
+ { 0x108c1, 0x0 },
+ { 0x1108c1, 0x0 },
+ { 0x2108c1, 0x0 },
+ { 0x110c1, 0x0 },
+ { 0x1110c1, 0x0 },
+ { 0x2110c1, 0x0 },
+ { 0x111c1, 0x0 },
+ { 0x1111c1, 0x0 },
+ { 0x2111c1, 0x0 },
+ { 0x112c1, 0x0 },
+ { 0x1112c1, 0x0 },
+ { 0x2112c1, 0x0 },
+ { 0x113c1, 0x0 },
+ { 0x1113c1, 0x0 },
+ { 0x2113c1, 0x0 },
+ { 0x114c1, 0x0 },
+ { 0x1114c1, 0x0 },
+ { 0x2114c1, 0x0 },
+ { 0x115c1, 0x0 },
+ { 0x1115c1, 0x0 },
+ { 0x2115c1, 0x0 },
+ { 0x116c1, 0x0 },
+ { 0x1116c1, 0x0 },
+ { 0x2116c1, 0x0 },
+ { 0x117c1, 0x0 },
+ { 0x1117c1, 0x0 },
+ { 0x2117c1, 0x0 },
+ { 0x118c1, 0x0 },
+ { 0x1118c1, 0x0 },
+ { 0x2118c1, 0x0 },
+ { 0x120c1, 0x0 },
+ { 0x1120c1, 0x0 },
+ { 0x2120c1, 0x0 },
+ { 0x121c1, 0x0 },
+ { 0x1121c1, 0x0 },
+ { 0x2121c1, 0x0 },
+ { 0x122c1, 0x0 },
+ { 0x1122c1, 0x0 },
+ { 0x2122c1, 0x0 },
+ { 0x123c1, 0x0 },
+ { 0x1123c1, 0x0 },
+ { 0x2123c1, 0x0 },
+ { 0x124c1, 0x0 },
+ { 0x1124c1, 0x0 },
+ { 0x2124c1, 0x0 },
+ { 0x125c1, 0x0 },
+ { 0x1125c1, 0x0 },
+ { 0x2125c1, 0x0 },
+ { 0x126c1, 0x0 },
+ { 0x1126c1, 0x0 },
+ { 0x2126c1, 0x0 },
+ { 0x127c1, 0x0 },
+ { 0x1127c1, 0x0 },
+ { 0x2127c1, 0x0 },
+ { 0x128c1, 0x0 },
+ { 0x1128c1, 0x0 },
+ { 0x2128c1, 0x0 },
+ { 0x130c1, 0x0 },
+ { 0x1130c1, 0x0 },
+ { 0x2130c1, 0x0 },
+ { 0x131c1, 0x0 },
+ { 0x1131c1, 0x0 },
+ { 0x2131c1, 0x0 },
+ { 0x132c1, 0x0 },
+ { 0x1132c1, 0x0 },
+ { 0x2132c1, 0x0 },
+ { 0x133c1, 0x0 },
+ { 0x1133c1, 0x0 },
+ { 0x2133c1, 0x0 },
+ { 0x134c1, 0x0 },
+ { 0x1134c1, 0x0 },
+ { 0x2134c1, 0x0 },
+ { 0x135c1, 0x0 },
+ { 0x1135c1, 0x0 },
+ { 0x2135c1, 0x0 },
+ { 0x136c1, 0x0 },
+ { 0x1136c1, 0x0 },
+ { 0x2136c1, 0x0 },
+ { 0x137c1, 0x0 },
+ { 0x1137c1, 0x0 },
+ { 0x2137c1, 0x0 },
+ { 0x138c1, 0x0 },
+ { 0x1138c1, 0x0 },
+ { 0x2138c1, 0x0 },
+ { 0x10020, 0x0 },
+ { 0x110020, 0x0 },
+ { 0x210020, 0x0 },
+ { 0x11020, 0x0 },
+ { 0x111020, 0x0 },
+ { 0x211020, 0x0 },
+ { 0x12020, 0x0 },
+ { 0x112020, 0x0 },
+ { 0x212020, 0x0 },
+ { 0x13020, 0x0 },
+ { 0x113020, 0x0 },
+ { 0x213020, 0x0 },
+ { 0x20072, 0x0 },
+ { 0x20073, 0x0 },
+ { 0x20074, 0x0 },
+ { 0x100aa, 0x0 },
+ { 0x110aa, 0x0 },
+ { 0x120aa, 0x0 },
+ { 0x130aa, 0x0 },
+ { 0x20010, 0x0 },
+ { 0x120010, 0x0 },
+ { 0x220010, 0x0 },
+ { 0x20011, 0x0 },
+ { 0x120011, 0x0 },
+ { 0x220011, 0x0 },
+ { 0x100ae, 0x0 },
+ { 0x1100ae, 0x0 },
+ { 0x2100ae, 0x0 },
+ { 0x100af, 0x0 },
+ { 0x1100af, 0x0 },
+ { 0x2100af, 0x0 },
+ { 0x110ae, 0x0 },
+ { 0x1110ae, 0x0 },
+ { 0x2110ae, 0x0 },
+ { 0x110af, 0x0 },
+ { 0x1110af, 0x0 },
+ { 0x2110af, 0x0 },
+ { 0x120ae, 0x0 },
+ { 0x1120ae, 0x0 },
+ { 0x2120ae, 0x0 },
+ { 0x120af, 0x0 },
+ { 0x1120af, 0x0 },
+ { 0x2120af, 0x0 },
+ { 0x130ae, 0x0 },
+ { 0x1130ae, 0x0 },
+ { 0x2130ae, 0x0 },
+ { 0x130af, 0x0 },
+ { 0x1130af, 0x0 },
+ { 0x2130af, 0x0 },
+ { 0x20020, 0x0 },
+ { 0x120020, 0x0 },
+ { 0x220020, 0x0 },
+ { 0x100a0, 0x0 },
+ { 0x100a1, 0x0 },
+ { 0x100a2, 0x0 },
+ { 0x100a3, 0x0 },
+ { 0x100a4, 0x0 },
+ { 0x100a5, 0x0 },
+ { 0x100a6, 0x0 },
+ { 0x100a7, 0x0 },
+ { 0x110a0, 0x0 },
+ { 0x110a1, 0x0 },
+ { 0x110a2, 0x0 },
+ { 0x110a3, 0x0 },
+ { 0x110a4, 0x0 },
+ { 0x110a5, 0x0 },
+ { 0x110a6, 0x0 },
+ { 0x110a7, 0x0 },
+ { 0x120a0, 0x0 },
+ { 0x120a1, 0x0 },
+ { 0x120a2, 0x0 },
+ { 0x120a3, 0x0 },
+ { 0x120a4, 0x0 },
+ { 0x120a5, 0x0 },
+ { 0x120a6, 0x0 },
+ { 0x120a7, 0x0 },
+ { 0x130a0, 0x0 },
+ { 0x130a1, 0x0 },
+ { 0x130a2, 0x0 },
+ { 0x130a3, 0x0 },
+ { 0x130a4, 0x0 },
+ { 0x130a5, 0x0 },
+ { 0x130a6, 0x0 },
+ { 0x130a7, 0x0 },
+ { 0x2007c, 0x0 },
+ { 0x12007c, 0x0 },
+ { 0x22007c, 0x0 },
+ { 0x2007d, 0x0 },
+ { 0x12007d, 0x0 },
+ { 0x22007d, 0x0 },
+ { 0x400fd, 0x0 },
+ { 0x400c0, 0x0 },
+ { 0x90201, 0x0 },
+ { 0x190201, 0x0 },
+ { 0x290201, 0x0 },
+ { 0x90202, 0x0 },
+ { 0x190202, 0x0 },
+ { 0x290202, 0x0 },
+ { 0x90203, 0x0 },
+ { 0x190203, 0x0 },
+ { 0x290203, 0x0 },
+ { 0x90204, 0x0 },
+ { 0x190204, 0x0 },
+ { 0x290204, 0x0 },
+ { 0x90205, 0x0 },
+ { 0x190205, 0x0 },
+ { 0x290205, 0x0 },
+ { 0x90206, 0x0 },
+ { 0x190206, 0x0 },
+ { 0x290206, 0x0 },
+ { 0x90207, 0x0 },
+ { 0x190207, 0x0 },
+ { 0x290207, 0x0 },
+ { 0x90208, 0x0 },
+ { 0x190208, 0x0 },
+ { 0x290208, 0x0 },
+ { 0x10062, 0x0 },
+ { 0x10162, 0x0 },
+ { 0x10262, 0x0 },
+ { 0x10362, 0x0 },
+ { 0x10462, 0x0 },
+ { 0x10562, 0x0 },
+ { 0x10662, 0x0 },
+ { 0x10762, 0x0 },
+ { 0x10862, 0x0 },
+ { 0x11062, 0x0 },
+ { 0x11162, 0x0 },
+ { 0x11262, 0x0 },
+ { 0x11362, 0x0 },
+ { 0x11462, 0x0 },
+ { 0x11562, 0x0 },
+ { 0x11662, 0x0 },
+ { 0x11762, 0x0 },
+ { 0x11862, 0x0 },
+ { 0x12062, 0x0 },
+ { 0x12162, 0x0 },
+ { 0x12262, 0x0 },
+ { 0x12362, 0x0 },
+ { 0x12462, 0x0 },
+ { 0x12562, 0x0 },
+ { 0x12662, 0x0 },
+ { 0x12762, 0x0 },
+ { 0x12862, 0x0 },
+ { 0x13062, 0x0 },
+ { 0x13162, 0x0 },
+ { 0x13262, 0x0 },
+ { 0x13362, 0x0 },
+ { 0x13462, 0x0 },
+ { 0x13562, 0x0 },
+ { 0x13662, 0x0 },
+ { 0x13762, 0x0 },
+ { 0x13862, 0x0 },
+ { 0x20077, 0x0 },
+ { 0x10001, 0x0 },
+ { 0x11001, 0x0 },
+ { 0x12001, 0x0 },
+ { 0x13001, 0x0 },
+ { 0x10040, 0x0 },
+ { 0x10140, 0x0 },
+ { 0x10240, 0x0 },
+ { 0x10340, 0x0 },
+ { 0x10440, 0x0 },
+ { 0x10540, 0x0 },
+ { 0x10640, 0x0 },
+ { 0x10740, 0x0 },
+ { 0x10840, 0x0 },
+ { 0x10030, 0x0 },
+ { 0x10130, 0x0 },
+ { 0x10230, 0x0 },
+ { 0x10330, 0x0 },
+ { 0x10430, 0x0 },
+ { 0x10530, 0x0 },
+ { 0x10630, 0x0 },
+ { 0x10730, 0x0 },
+ { 0x10830, 0x0 },
+ { 0x11040, 0x0 },
+ { 0x11140, 0x0 },
+ { 0x11240, 0x0 },
+ { 0x11340, 0x0 },
+ { 0x11440, 0x0 },
+ { 0x11540, 0x0 },
+ { 0x11640, 0x0 },
+ { 0x11740, 0x0 },
+ { 0x11840, 0x0 },
+ { 0x11030, 0x0 },
+ { 0x11130, 0x0 },
+ { 0x11230, 0x0 },
+ { 0x11330, 0x0 },
+ { 0x11430, 0x0 },
+ { 0x11530, 0x0 },
+ { 0x11630, 0x0 },
+ { 0x11730, 0x0 },
+ { 0x11830, 0x0 },
+ { 0x12040, 0x0 },
+ { 0x12140, 0x0 },
+ { 0x12240, 0x0 },
+ { 0x12340, 0x0 },
+ { 0x12440, 0x0 },
+ { 0x12540, 0x0 },
+ { 0x12640, 0x0 },
+ { 0x12740, 0x0 },
+ { 0x12840, 0x0 },
+ { 0x12030, 0x0 },
+ { 0x12130, 0x0 },
+ { 0x12230, 0x0 },
+ { 0x12330, 0x0 },
+ { 0x12430, 0x0 },
+ { 0x12530, 0x0 },
+ { 0x12630, 0x0 },
+ { 0x12730, 0x0 },
+ { 0x12830, 0x0 },
+ { 0x13040, 0x0 },
+ { 0x13140, 0x0 },
+ { 0x13240, 0x0 },
+ { 0x13340, 0x0 },
+ { 0x13440, 0x0 },
+ { 0x13540, 0x0 },
+ { 0x13640, 0x0 },
+ { 0x13740, 0x0 },
+ { 0x13840, 0x0 },
+ { 0x13030, 0x0 },
+ { 0x13130, 0x0 },
+ { 0x13230, 0x0 },
+ { 0x13330, 0x0 },
+ { 0x13430, 0x0 },
+ { 0x13530, 0x0 },
+ { 0x13630, 0x0 },
+ { 0x13730, 0x0 },
+ { 0x13830, 0x0 },
+};
+
+uint32_t ddrphy_trained_csr_num = ARRAY_SIZE(ddrphy_trained_csr);
diff --git a/drivers/ddr/imx/phy/ddrphy_train.c b/drivers/ddr/imx/phy/ddrphy_train.c
new file mode 100644
index 00000000000..ccc10df1845
--- /dev/null
+++ b/drivers/ddr/imx/phy/ddrphy_train.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+#include <log.h>
+#include <linux/kernel.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/sys_proto.h>
+
+int ddr_cfg_phy(struct dram_timing_info *dram_timing)
+{
+ struct dram_cfg_param *dram_cfg;
+ struct dram_fsp_msg *fsp_msg;
+ unsigned int num;
+ int i = 0;
+ int j = 0;
+ int ret;
+
+ /* initialize PHY configuration */
+ dram_cfg = dram_timing->ddrphy_cfg;
+ num = dram_timing->ddrphy_cfg_num;
+ for (i = 0; i < num; i++) {
+ /* config phy reg */
+ dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ dram_cfg++;
+ }
+
+ /* load the frequency setpoint message block config */
+ fsp_msg = dram_timing->fsp_msg;
+ for (i = 0; i < dram_timing->fsp_msg_num; i++) {
+ debug("DRAM PHY training for %dMTS\n", fsp_msg->drate);
+ /* set dram PHY input clocks to desired frequency */
+ ddrphy_init_set_dfi_clk(fsp_msg->drate);
+
+ /* load the dram training firmware image */
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+ ddr_load_train_firmware(fsp_msg->fw_type);
+
+ /* load the frequency set point message block parameter */
+ dram_cfg = fsp_msg->fsp_cfg;
+ num = fsp_msg->fsp_cfg_num;
+ for (j = 0; j < num; j++) {
+ dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ dram_cfg++;
+ }
+
+ /*
+ * -------------------- excute the firmware --------------------
+ * Running the firmware is a simply process to taking the
+ * PMU out of reset and stall, then the firwmare will be run
+ * 1. reset the PMU;
+ * 2. begin the excution;
+ * 3. wait for the training done;
+ * 4. read the message block result.
+ * -------------------------------------------------------------
+ */
+ dwc_ddrphy_apb_wr(0xd0000, 0x1);
+ dwc_ddrphy_apb_wr(0xd0099, 0x9);
+ dwc_ddrphy_apb_wr(0xd0099, 0x1);
+ dwc_ddrphy_apb_wr(0xd0099, 0x0);
+
+ /* Wait for the training firmware to complete */
+ ret = wait_ddrphy_training_complete();
+ if (ret)
+ return ret;
+
+ /* Halt the microcontroller. */
+ dwc_ddrphy_apb_wr(0xd0099, 0x1);
+
+ /* Read the Message Block results */
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+
+ ddrphy_init_read_msg_block(fsp_msg->fw_type);
+
+ if(fsp_msg->fw_type != FW_2D_IMAGE)
+ get_trained_CDD(i);
+
+ dwc_ddrphy_apb_wr(0xd0000, 0x1);
+
+
+ fsp_msg++;
+ }
+
+ /* Load PHY Init Engine Image */
+ dram_cfg = dram_timing->ddrphy_pie;
+ num = dram_timing->ddrphy_pie_num;
+ for (i = 0; i < num; i++) {
+ dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val);
+ dram_cfg++;
+ }
+
+ /* save the ddr PHY trained CSR in memory for low power use */
+ ddrphy_trained_csr_save(ddrphy_trained_csr, ddrphy_trained_csr_num);
+
+ return 0;
+}
diff --git a/drivers/ddr/imx/phy/ddrphy_utils.c b/drivers/ddr/imx/phy/ddrphy_utils.c
new file mode 100644
index 00000000000..cf5bdad7abe
--- /dev/null
+++ b/drivers/ddr/imx/phy/ddrphy_utils.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+#include <errno.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/sys_proto.h>
+
+static inline void poll_pmu_message_ready(void)
+{
+ unsigned int reg;
+
+ do {
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0004));
+ } while (reg & 0x1);
+}
+
+static inline void ack_pmu_message_receive(void)
+{
+ unsigned int reg;
+
+ reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0031), 0x0);
+
+ do {
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0004));
+ } while (!(reg & 0x1));
+
+ reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0031), 0x1);
+}
+
+static inline unsigned int get_mail(void)
+{
+ unsigned int reg;
+
+ poll_pmu_message_ready();
+
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0032));
+
+ ack_pmu_message_receive();
+
+ return reg;
+}
+
+static inline unsigned int get_stream_message(void)
+{
+ unsigned int reg, reg2;
+
+ poll_pmu_message_ready();
+
+ reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0032));
+
+ reg2 = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + ddrphy_addr_remap(0xd0034));
+
+ reg2 = (reg2 << 16) | reg;
+
+ ack_pmu_message_receive();
+
+ return reg2;
+}
+
+static inline void decode_major_message(unsigned int mail)
+{
+ debug("[PMU Major message = 0x%08x]\n", mail);
+}
+
+static inline void decode_streaming_message(void)
+{
+ unsigned int string_index, arg __maybe_unused;
+ int i = 0;
+
+ string_index = get_stream_message();
+ debug("PMU String index = 0x%08x\n", string_index);
+ while (i < (string_index & 0xffff)) {
+ arg = get_stream_message();
+ debug("arg[%d] = 0x%08x\n", i, arg);
+ i++;
+ }
+
+ debug("\n");
+}
+
+int wait_ddrphy_training_complete(void)
+{
+ unsigned int mail;
+
+ while (1) {
+ mail = get_mail();
+ decode_major_message(mail);
+ if (mail == 0x08) {
+ decode_streaming_message();
+ } else if (mail == 0x07) {
+ debug("Training PASS\n");
+ return 0;
+ } else if (mail == 0xff) {
+ printf("Training FAILED\n");
+ return -1;
+ }
+ }
+}
+
+void ddrphy_init_set_dfi_clk(unsigned int drate)
+{
+ switch (drate) {
+ case 4000:
+ dram_pll_init(MHZ(1000));
+ dram_disable_bypass();
+ break;
+ case 3734:
+ case 3733:
+ case 3732:
+ dram_pll_init(MHZ(933));
+ dram_disable_bypass();
+ break;
+ case 3600:
+ dram_pll_init(MHZ(900));
+ dram_disable_bypass();
+ break;
+ case 3200:
+ dram_pll_init(MHZ(800));
+ dram_disable_bypass();
+ break;
+ case 3000:
+ dram_pll_init(MHZ(750));
+ dram_disable_bypass();
+ break;
+ case 2800:
+ dram_pll_init(MHZ(700));
+ dram_disable_bypass();
+ break;
+ case 2400:
+ dram_pll_init(MHZ(600));
+ dram_disable_bypass();
+ break;
+ case 1866:
+ dram_pll_init(MHZ(466));
+ dram_disable_bypass();
+ break;
+ case 1600:
+ dram_pll_init(MHZ(400));
+ dram_disable_bypass();
+ break;
+ case 1066:
+ dram_pll_init(MHZ(266));
+ dram_disable_bypass();
+ break;
+ case 667:
+ dram_pll_init(MHZ(167));
+ dram_disable_bypass();
+ break;
+ case 625:
+ dram_enable_bypass(MHZ(625));
+ break;
+ case 400:
+ dram_enable_bypass(MHZ(400));
+ break;
+ case 333:
+ dram_enable_bypass(MHZ(333));
+ break;
+ case 200:
+ dram_enable_bypass(MHZ(200));
+ break;
+ case 100:
+ dram_enable_bypass(MHZ(100));
+ break;
+ default:
+ return;
+ }
+}
+
+void ddrphy_init_read_msg_block(enum fw_type type)
+{
+}
diff --git a/drivers/ddr/imx/phy/helper.c b/drivers/ddr/imx/phy/helper.c
new file mode 100644
index 00000000000..c1fc800f191
--- /dev/null
+++ b/drivers/ddr/imx/phy/helper.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+#include <binman_sym.h>
+#include <log.h>
+#include <spl.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/ddr.h>
+#include <asm/sections.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define IMEM_LEN 32768 /* byte */
+#define DMEM_LEN 16384 /* byte */
+#define IMEM_2D_OFFSET 49152
+
+#define IMEM_OFFSET_ADDR 0x00050000
+#define DMEM_OFFSET_ADDR 0x00054000
+#define DDR_TRAIN_CODE_BASE_ADDR IP2APB_DDRPHY_IPS_BASE_ADDR(0)
+
+binman_sym_declare(ulong, ddr_1d_imem_fw, image_pos);
+binman_sym_declare(ulong, ddr_1d_imem_fw, size);
+
+binman_sym_declare(ulong, ddr_1d_dmem_fw, image_pos);
+binman_sym_declare(ulong, ddr_1d_dmem_fw, size);
+
+#if !IS_ENABLED(CONFIG_IMX8M_DDR3L)
+binman_sym_declare(ulong, ddr_2d_imem_fw, image_pos);
+binman_sym_declare(ulong, ddr_2d_imem_fw, size);
+
+binman_sym_declare(ulong, ddr_2d_dmem_fw, image_pos);
+binman_sym_declare(ulong, ddr_2d_dmem_fw, size);
+#endif
+
+/* We need PHY iMEM PHY is 32KB padded */
+void ddr_load_train_firmware(enum fw_type type)
+{
+ u32 tmp32, i;
+ u32 error = 0;
+ unsigned long pr_to32, pr_from32;
+ uint32_t fw_offset = type ? IMEM_2D_OFFSET : 0;
+ unsigned long imem_start = (unsigned long)_end + fw_offset;
+ unsigned long dmem_start;
+ unsigned long imem_len = IMEM_LEN, dmem_len = DMEM_LEN;
+ static enum fw_type last_type = -1;
+
+ /* If FW doesn't change, we can save the loading. */
+ if (last_type == type)
+ return;
+
+ last_type = type;
+
+#ifdef CONFIG_SPL_OF_CONTROL
+ if (gd->fdt_blob && !fdt_check_header(gd->fdt_blob)) {
+ imem_start = roundup((unsigned long)_end +
+ fdt_totalsize(gd->fdt_blob), 4) +
+ fw_offset;
+ }
+#endif
+
+ dmem_start = imem_start + imem_len;
+
+ if (BINMAN_SYMS_OK) {
+ switch (type) {
+ case FW_1D_IMAGE:
+ imem_start = binman_sym(ulong, ddr_1d_imem_fw, image_pos);
+ imem_len = binman_sym(ulong, ddr_1d_imem_fw, size);
+ dmem_start = binman_sym(ulong, ddr_1d_dmem_fw, image_pos);
+ dmem_len = binman_sym(ulong, ddr_1d_dmem_fw, size);
+ break;
+ case FW_2D_IMAGE:
+#if !IS_ENABLED(CONFIG_IMX8M_DDR3L)
+ imem_start = binman_sym(ulong, ddr_2d_imem_fw, image_pos);
+ imem_len = binman_sym(ulong, ddr_2d_imem_fw, size);
+ dmem_start = binman_sym(ulong, ddr_2d_dmem_fw, image_pos);
+ dmem_len = binman_sym(ulong, ddr_2d_dmem_fw, size);
+#endif
+ break;
+ }
+ }
+
+ pr_from32 = imem_start;
+ pr_to32 = IMEM_OFFSET_ADDR;
+ for (i = 0x0; i < imem_len; ) {
+ tmp32 = readl(pr_from32);
+ writew(tmp32 & 0x0000ffff, DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
+ writew((tmp32 >> 16) & 0x0000ffff,
+ DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
+ pr_from32 += 4;
+ i += 4;
+ }
+
+ pr_from32 = dmem_start;
+ pr_to32 = DMEM_OFFSET_ADDR;
+ for (i = 0x0; i < dmem_len; ) {
+ tmp32 = readl(pr_from32);
+ writew(tmp32 & 0x0000ffff, DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
+ writew((tmp32 >> 16) & 0x0000ffff,
+ DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32));
+ pr_to32 += 1;
+ pr_from32 += 4;
+ i += 4;
+ }
+
+ debug("check ddr_pmu_train_imem code\n");
+ pr_from32 = imem_start;
+ pr_to32 = IMEM_OFFSET_ADDR;
+ for (i = 0x0; i < imem_len; ) {
+ tmp32 = (readw(DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32)) & 0x0000ffff);
+ pr_to32 += 1;
+ tmp32 += ((readw(DDR_TRAIN_CODE_BASE_ADDR +
+ ddrphy_addr_remap(pr_to32)) & 0x0000ffff) << 16);
+
+ if (tmp32 != readl(pr_from32)) {
+ debug("%lx %lx\n", pr_from32, pr_to32);
+ error++;
+ }
+ pr_from32 += 4;
+ pr_to32 += 1;
+ i += 4;
+ }
+ if (error)
+ printf("check ddr_pmu_train_imem code fail=%d\n", error);
+ else
+ debug("check ddr_pmu_train_imem code pass\n");
+
+ debug("check ddr4_pmu_train_dmem code\n");
+ pr_from32 = dmem_start;
+ pr_to32 = DMEM_OFFSET_ADDR;
+ for (i = 0x0; i < dmem_len;) {
+ tmp32 = (readw(DDR_TRAIN_CODE_BASE_ADDR + ddrphy_addr_remap(pr_to32)) & 0x0000ffff);
+ pr_to32 += 1;
+ tmp32 += ((readw(DDR_TRAIN_CODE_BASE_ADDR +
+ ddrphy_addr_remap(pr_to32)) & 0x0000ffff) << 16);
+ if (tmp32 != readl(pr_from32)) {
+ debug("%lx %lx\n", pr_from32, pr_to32);
+ error++;
+ }
+ pr_from32 += 4;
+ pr_to32 += 1;
+ i += 4;
+ }
+
+ if (error)
+ printf("check ddr_pmu_train_dmem code fail=%d", error);
+ else
+ debug("check ddr_pmu_train_dmem code pass\n");
+}
+
+void ddrphy_trained_csr_save(struct dram_cfg_param *ddrphy_csr,
+ unsigned int num)
+{
+ int i = 0;
+
+ /* enable the ddrphy apb */
+ dwc_ddrphy_apb_wr(0xd0000, 0x0);
+ dwc_ddrphy_apb_wr(0xc0080, 0x3);
+ for (i = 0; i < num; i++) {
+ ddrphy_csr->val = dwc_ddrphy_apb_rd(ddrphy_csr->reg);
+ ddrphy_csr++;
+ }
+ /* disable the ddrphy apb */
+ dwc_ddrphy_apb_wr(0xc0080, 0x2);
+ dwc_ddrphy_apb_wr(0xd0000, 0x1);
+}
+
+void *dram_config_save(struct dram_timing_info *timing_info, unsigned long saved_timing_base)
+{
+ int i = 0;
+ struct dram_timing_info *saved_timing = (struct dram_timing_info *)saved_timing_base;
+ struct dram_cfg_param *cfg;
+
+ saved_timing->ddrc_cfg_num = timing_info->ddrc_cfg_num;
+ saved_timing->ddrphy_cfg_num = timing_info->ddrphy_cfg_num;
+ saved_timing->ddrphy_trained_csr_num = ddrphy_trained_csr_num;
+ saved_timing->ddrphy_pie_num = timing_info->ddrphy_pie_num;
+
+ /* save the fsp table */
+ for (i = 0; i < 4; i++)
+ saved_timing->fsp_table[i] = timing_info->fsp_table[i];
+
+ cfg = (struct dram_cfg_param *)(saved_timing_base +
+ sizeof(*timing_info));
+
+ /* save ddrc config */
+ saved_timing->ddrc_cfg = cfg;
+ for (i = 0; i < timing_info->ddrc_cfg_num; i++) {
+ cfg->reg = timing_info->ddrc_cfg[i].reg;
+ cfg->val = timing_info->ddrc_cfg[i].val;
+ cfg++;
+ }
+
+ /* save ddrphy config */
+ saved_timing->ddrphy_cfg = cfg;
+ for (i = 0; i < timing_info->ddrphy_cfg_num; i++) {
+ cfg->reg = timing_info->ddrphy_cfg[i].reg;
+ cfg->val = timing_info->ddrphy_cfg[i].val;
+ cfg++;
+ }
+
+ /* save the ddrphy csr */
+ saved_timing->ddrphy_trained_csr = cfg;
+ for (i = 0; i < ddrphy_trained_csr_num; i++) {
+ cfg->reg = ddrphy_trained_csr[i].reg;
+ cfg->val = ddrphy_trained_csr[i].val;
+ cfg++;
+ }
+
+ /* save the ddrphy pie */
+ saved_timing->ddrphy_pie = cfg;
+ for (i = 0; i < timing_info->ddrphy_pie_num; i++) {
+ cfg->reg = timing_info->ddrphy_pie[i].reg;
+ cfg->val = timing_info->ddrphy_pie[i].val;
+ cfg++;
+ }
+
+ return (void *)cfg;
+}