Commit 220d0cc7 authored by Bai Ping's avatar Bai Ping

MLK-18431-02: add a more generic dram init flow for imx8m

the dram init is board related. But there is still some common
part can be reused on different board. The basic flow is common
for all the board. only the DDRC and DDR PHY config register setting
is different on different board. So extract the LPDDR4 init common
flow to make it more generic. baord level only need to provide
the DDRC and PHY config register parameter to the common code to finish
the dram init.

the same method can be use for DDR4. will be added later.
Signed-off-by: default avatarBai Ping <ping.bai@nxp.com>
parent 2b2cd1ad
......@@ -2112,7 +2112,9 @@
#define DDRPHY_PrbsChkStateHi(X) (IP2APB_DDRPHY_IPS_BASE_ADDR(X) + 4*0x070064)
#define DDRPHY_PrbsGenCtl1(X) (IP2APB_DDRPHY_IPS_BASE_ADDR(X) + 4*0x070065)
#define DDRPHY_PrbsGenCtl2(X) (IP2APB_DDRPHY_IPS_BASE_ADDR(X) + 4*0x070066)
#endif
#define DDRPHY_CalBusy(X) (IP2APB_DDRPHY_IPS_BASE_ADDR(X) + 4*0x020097)
#define DRC_PERF_MON_BASE_ADDR(X) (0x3d800000 + (X * 0x2000000))
#define DRC_PERF_MON_CNT0_CTL(X) (DRC_PERF_MON_BASE_ADDR(X) + 0x0)
......@@ -2139,4 +2141,3 @@
#define DRC_PERF_MON_MRR13_DAT(X) (DRC_PERF_MON_BASE_ADDR(X) + 0x74)
#define DRC_PERF_MON_MRR14_DAT(X) (DRC_PERF_MON_BASE_ADDR(X) + 0x78)
#define DRC_PERF_MON_MRR15_DAT(X) (DRC_PERF_MON_BASE_ADDR(X) + 0x7C)
#endif
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
* Common file for ddr code
*/
#ifndef __IMX8M_DDR_H__
#define __IMX8M_DDR_H__
#include <asm/io.h>
#include <asm/types.h>
#include <asm/arch/ddr_memory_map.h>
/* user data type */
enum fw_type {
FW_1D_IMAGE,
FW_2D_IMAGE,
};
struct dram_cfg_param {
unsigned int reg;
unsigned int val;
};
struct dram_fsp_msg {
unsigned int drate;
enum fw_type fw_type;
struct dram_cfg_param *fsp_cfg;
unsigned int fsp_cfg_num;
};
struct dram_timing_info {
/* umctl2 config */
struct dram_cfg_param *ddrc_cfg;
unsigned int ddrc_cfg_num;
/* ddrphy config */
struct dram_cfg_param *ddrphy_cfg;
unsigned int ddrphy_cfg_num;
/* ddr fsp train info */
struct dram_fsp_msg *fsp_msg;
unsigned int fsp_msg_num;
/* ddr phy trained CSR */
struct dram_cfg_param *ddrphy_trained_csr;
unsigned int ddrphy_trained_csr_num;
/* ddr phy PIE */
struct dram_cfg_param *ddrphy_pie;
unsigned int ddrphy_pie_num;
};
extern struct dram_timing_info lpddr4_timing;
void ddr_load_train_firmware(enum fw_type type);
void ddr_init(struct dram_timing_info *timing_info);
void lpddr4_cfg_phy(struct dram_timing_info *timing_info);
void load_lpddr4_phy_pie(void);
void ddrphy_trained_csr_save(struct dram_cfg_param *, unsigned int);
void dram_config_save(struct dram_timing_info *, unsigned long);
/* utils function for ddr phy training */
void wait_ddrphy_training_complete(void);
void ddrphy_init_set_dfi_clk(unsigned int drate);
void ddrphy_init_read_msg_block(enum fw_type type);
static inline void reg32_write(unsigned long addr, u32 val)
{
writel(val, addr);
}
static inline u32 reg32_read(unsigned long addr)
{
return readl(addr);
}
static inline void reg32setbit(unsigned long addr, u32 bit)
{
setbits_le32(addr, (1 << bit));
}
#define dwc_ddrphy_apb_wr(addr, data) reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*(addr), data)
#define dwc_ddrphy_apb_rd(addr) reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*(addr))
#endif /* __IMX8M_DDR_H__ */
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __LPDDR4_DEFINE_H_
#define __LPDDR4_DEFINE_H_
#define LPDDR4_DVFS_DBI
#define DDR_ONE_RANK
/* #define LPDDR4_DBI_ON */
#define DFI_BUG_WR
#define M845S_4GBx2
#define PRETRAIN
/* DRAM MR setting */
#ifdef LPDDR4_DBI_ON
#define LPDDR4_MR3 0xf1
#define LPDDR4_PHY_DMIPinPresent 0x1
#else
#define LPDDR4_MR3 0x31
#define LPDDR4_PHY_DMIPinPresent 0x0
#endif
#ifdef DDR_ONE_RANK
#define LPDDR4_CS 0x1
#else
#define LPDDR4_CS 0x3
#endif
/* PHY training feature */
#define LPDDR4_HDT_CTL_2D 0xC8
#define LPDDR4_HDT_CTL_3200_1D 0xC8
#define LPDDR4_HDT_CTL_400_1D 0xC8
#define LPDDR4_HDT_CTL_100_1D 0xC8
#define LPDDR4_HDT_CTL_2D 0xC8
#define LPDDR4_HDT_CTL_3200_1D 0xC8
#define LPDDR4_HDT_CTL_400_1D 0xC8
#define LPDDR4_HDT_CTL_100_1D 0xC8
/* 400/100 training seq */
#define LPDDR4_TRAIN_SEQ_P2 0x121f
#define LPDDR4_TRAIN_SEQ_P1 0x121f
#define LPDDR4_TRAIN_SEQ_P0 0x121f
/* 2D share & weight */
#define LPDDR4_2D_WEIGHT 0x1f7f
#define LPDDR4_2D_SHARE 1
#define LPDDR4_CATRAIN_3200_1d 0
#define LPDDR4_CATRAIN_400 0
#define LPDDR4_CATRAIN_100 0
#define LPDDR4_CATRAIN_3200_2d 0
/* MRS parameter */
/* for LPDDR4 Rtt */
#define LPDDR4_RTT40 6
#define LPDDR4_RTT48 5
#define LPDDR4_RTT60 4
#define LPDDR4_RTT80 3
#define LPDDR4_RTT120 2
#define LPDDR4_RTT240 1
#define LPDDR4_RTT_DIS 0
/* for LPDDR4 Ron */
#define LPDDR4_RON34 7
#define LPDDR4_RON40 6
#define LPDDR4_RON48 5
#define LPDDR4_RON60 4
#define LPDDR4_RON80 3
#define LPDDR4_PHY_ADDR_RON60 0x1
#define LPDDR4_PHY_ADDR_RON40 0x3
#define LPDDR4_PHY_ADDR_RON30 0x7
#define LPDDR4_PHY_ADDR_RON24 0xf
#define LPDDR4_PHY_ADDR_RON20 0x1f
/* for read channel */
#define LPDDR4_RON LPDDR4_RON40
#define LPDDR4_PHY_RTT 30
#define LPDDR4_PHY_VREF_VALUE 17
/* for write channel */
#define LPDDR4_PHY_RON 30
#define LPDDR4_PHY_ADDR_RON LPDDR4_PHY_ADDR_RON40
#define LPDDR4_RTT_DQ LPDDR4_RTT40
#define LPDDR4_RTT_CA LPDDR4_RTT40
#define LPDDR4_RTT_CA_BANK0 LPDDR4_RTT40
#define LPDDR4_RTT_CA_BANK1 LPDDR4_RTT40
#define LPDDR4_VREF_VALUE_CA ((1 << 6) | (0xd))
#define LPDDR4_VREF_VALUE_DQ_RANK0 ((1 << 6) | (0xd))
#define LPDDR4_VREF_VALUE_DQ_RANK1 ((1 << 6) | (0xd))
#define LPDDR4_MR22_RANK0 ((0 << 5) | (0 << 4) | (0 << 3) | (LPDDR4_RTT40))
#define LPDDR4_MR22_RANK1 ((1 << 5) | (0 << 4) | (1 << 3) | (LPDDR4_RTT40))
#define LPDDR4_MR3_PU_CAL 1
#endif /* __LPDDR4_DEFINE_H__ */
......@@ -18,6 +18,8 @@ source "drivers/demo/Kconfig"
source "drivers/ddr/fsl/Kconfig"
source "drivers/ddr/imx8m/Kconfig"
source "drivers/dfu/Kconfig"
source "drivers/dma/Kconfig"
......
......@@ -19,6 +19,7 @@ obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/
obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/
obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/
obj-$(CONFIG_ALTERA_SDRAM) += ddr/altera/
obj-$(CONFIG_ARCH_IMX8M) += ddr/imx8m/
obj-$(CONFIG_SPL_SERIAL_SUPPORT) += serial/
obj-$(CONFIG_SPL_SPI_SUPPORT) += spi/
obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/
......
config IMX8M_DRAM
bool "imx8m dram"
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 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
#
# Copyright 2018 NXP
#
# SPDX-License-Identifier: GPL-2.0+
#
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_IMX8M_DRAM) += helper.o ddrphy_utils.o
obj-$(CONFIG_IMX8M_LPDDR4) += lpddr4/
endif
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/ddr_memory_map.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx8m_ddr.h>
#include <asm/arch/lpddr4_define.h>
#define ddr_printf(args...) debug(args)
static inline void poll_pmu_message_ready(void)
{
unsigned int reg;
do {
reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0)+ 4*0xd0004);
} while (reg & 0x1);
}
static inline void ack_pmu_message_recieve(void)
{
unsigned int reg;
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0)+ 4*0xd0031, 0x0);
do {
reg = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0)+ 4*0xd0004);
} while (!(reg & 0x1));
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0)+ 4*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)+ 4*0xd0032);
ack_pmu_message_recieve();
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)+ 4*0xd0032);
reg2 = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0)+ 4*0xd0034);
reg2 = (reg2 << 16) | reg;
ack_pmu_message_recieve();
return reg2;
}
static inline void decode_major_message(unsigned int mail)
{
ddr_printf("[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();
ddr_printf("PMU String index = 0x%08x\n", string_index);
while (i < (string_index & 0xffff)){
arg = get_stream_message();
ddr_printf("arg[%d] = 0x%08x\n", i, arg);
i++;
}
ddr_printf("\n");
}
void 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) {
printf("Training PASS\n");
break;
} else if (mail == 0xff) {
printf("Training FAILED\n");
break;
}
}
}
void ddrphy_init_set_dfi_clk(unsigned int drate)
{
switch (drate) {
case 3000:
dram_pll_init(DRAM_PLL_OUT_750M);
dram_disable_bypass();
break;
case 2400:
dram_pll_init(DRAM_PLL_OUT_600M);
dram_disable_bypass();
break;
case 1600:
dram_pll_init(DRAM_PLL_OUT_400M);
dram_disable_bypass();
break;
case 400:
dram_enable_bypass(DRAM_BYPASSCLK_400M);
break;
case 100:
dram_enable_bypass(DRAM_BYPASSCLK_100M);
break;
default:
return;
}
}
void ddrphy_init_read_msg_block(enum fw_type type)
{
}
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));
tmp = tmp & 0xff;
reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4);
return tmp;
}
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spl.h>
#include <asm/io.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/ddr_memory_map.h>
#include <asm/arch/imx8m_ddr.h>
#include <asm/arch/lpddr4_define.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)
/* 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;
unsigned long fw_offset = type ? IMEM_2D_OFFSET : 0;
unsigned long imem_start = (unsigned long)&_end + fw_offset;
unsigned long dmem_start = imem_start + IMEM_LEN;
pr_from32 = imem_start;
pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * IMEM_OFFSET_ADDR;
for (i = 0x0; i < IMEM_LEN; ) {
tmp32 = readl(pr_from32);
writew(tmp32 & 0x0000ffff, pr_to32);
pr_to32 += 4;
writew((tmp32 >> 16) & 0x0000ffff, pr_to32);
pr_to32 += 4;
pr_from32 += 4;
i += 4;
}
pr_from32 = dmem_start;
pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * DMEM_OFFSET_ADDR;
for (i = 0x0; i < DMEM_LEN; ) {
tmp32 = readl(pr_from32);
writew(tmp32 & 0x0000ffff, pr_to32);
pr_to32 += 4;
writew((tmp32 >> 16) & 0x0000ffff, pr_to32);
pr_to32 += 4;
pr_from32 += 4;
i += 4;
}
printf("check ddr4_pmu_train_imem code\n");
pr_from32 = imem_start;
pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * IMEM_OFFSET_ADDR;
for (i = 0x0; i < IMEM_LEN; ) {
tmp32 = (readw(pr_to32) & 0x0000ffff);
pr_to32 += 4;
tmp32 += ((readw(pr_to32) & 0x0000ffff) << 16);
if(tmp32 != readl(pr_from32)){
printf("%lx %lx\n", pr_from32, pr_to32);
error++;
}
pr_from32 += 4;
pr_to32 += 4;
i += 4;
}
if (error) {
printf("check ddr4_pmu_train_imem code fail=%d\n",error);
} else {
printf("check ddr4_pmu_train_imem code pass\n");
}
printf("check ddr4_pmu_train_dmem code\n");
pr_from32 = dmem_start;
pr_to32 = DDR_TRAIN_CODE_BASE_ADDR + 4 * DMEM_OFFSET_ADDR;
for (i = 0x0; i < DMEM_LEN;) {
tmp32 = (readw(pr_to32) & 0x0000ffff);
pr_to32 += 4;
tmp32 += ((readw(pr_to32) & 0x0000ffff) << 16);
if (tmp32 != readl(pr_from32)) {
printf("%lx %lx\n", pr_from32, pr_to32);
error++;
}
pr_from32 += 4;
pr_to32 += 4;
i += 4;
}
if (error) {
printf("check ddr4_pmu_train_dmem code fail=%d",error);
} else {
printf("check ddr4_pmu_train_dmem code pass\n");
}
}
void ddrphy_trained_csr_save(struct dram_cfg_param *ddrphy_csr, unsigned int num)
{
/* enable the ddrphy apb */
dwc_ddrphy_apb_wr(0xd0000, 0x0);
dwc_ddrphy_apb_wr(0xc0080, 0x3);
for (int 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)
{
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 = timing_info->ddrphy_trained_csr_num;
saved_timing->ddrphy_pie_num = timing_info->ddrphy_pie_num;
cfg = (struct dram_cfg_param *)(saved_timing_base + sizeof(*timing_info));
/* save ddrc config */
saved_timing->ddrc_cfg = cfg;
for (int 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 (int 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 (int i = 0; i < timing_info->ddrphy_trained_csr_num; i++) {
cfg->reg = timing_info->ddrphy_trained_csr[i].reg;
cfg->val = timing_info->ddrphy_trained_csr[i].val;
cfg++;
}
/* save the ddrphy pie */
saved_timing->ddrphy_pie = cfg;
for (int 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++;
}
}
#
# Copyright 2018 NXP
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_SPL_BUILD) += lpddr4_init.o lpddr4_ddrphy_train.o
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <linux/kernel.h>
#include <asm/arch/imx8m_ddr.h>
#include <asm/arch/lpddr4_define.h>
void lpddr4_cfg_phy(struct dram_timing_info *dram_timing)
{
struct dram_cfg_param *dram_cfg;
struct dram_fsp_msg *fsp_msg;
unsigned int num;
/* initialize PHY configuration */
dram_cfg = dram_timing->ddrphy_cfg;
num = dram_timing->ddrphy_cfg_num;
for (int 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 (int i = 0; i < dram_timing->fsp_msg_num; i++) {
printf("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 (int 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 */
wait_ddrphy_training_complete();
/* 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);
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 (int 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(dram_timing->ddrphy_trained_csr,
dram_timing->ddrphy_trained_csr_num);
}
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/ddr_memory_map.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx8m_ddr.h>
#include <asm/arch/lpddr4_define.h>
#include <asm/arch/sys_proto.h>
void lpddr4_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num)
{
for (int i = 0; i < num; i++) {
reg32_write(ddrc_cfg->reg, ddrc_cfg->val);
ddrc_cfg++;
}
}
void ddr_init(struct dram_timing_info *dram_timing)
{
unsigned int tmp;
printf("DDRINFO: start lpddr4 ddr init\n");
/* step 1: reset */
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);