Commit 6ecf6bc1 authored by Thomas Schäfer's avatar Thomas Schäfer
Browse files

pitx-imx8m: port lpddr4 initialization



- use the lpddr4_timing.c file generated by DDR stress test
  tool for board specific lpddr4 timing settings.
- use NXP LPDDR4 driver located in drivers/ddr/imx/imx8m by
  enabling the IMX8M_LPDDR4 configuration setting.
Signed-off-by: Thomas Schäfer's avatarThomas Schaefer <thomas.schaefer@kontron.com>
parent b8d4ff33
......@@ -8,5 +8,5 @@ obj-y += pitx-imx8m.o
ifdef CONFIG_SPL_BUILD
obj-y += spl.o
obj-y += ddr/ddr_init.o ddr/ddrphy_train.o ddr/helper.o
obj-$(CONFIG_IMX8M_LPDDR4) += lpddr4_timing.o
endif
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
enum fw_type {
FW_1D_IMAGE,
FW_2D_IMAGE,
};
void imx8mq_ddr_init(void);
void ddr_load_train_code(enum fw_type type);
void lpddr4_800M_cfg_phy(void);
static inline void reg32_write(unsigned long addr, u32 val)
{
writel(val, addr);
}
static inline uint32_t reg32_read(unsigned long addr)
{
return readl(addr);
}
static void inline dwc_ddrphy_apb_wr(unsigned long addr, u32 val)
{
writel(val, addr);
}
static inline void reg32setbit(unsigned long addr, u32 bit)
{
setbits_le32(addr, (1 << bit));
}
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*
* Generated code from MX8M_DDR_tool
* Align with uboot-imx_v2017.03_4.9.51_imx8m_ga
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
// #include <asm/arch/ddr_memory_map.h>
#include <asm/arch/clock.h>
#include "imx8m_ddr.h"
#include "ddr.h"
#ifdef CONFIG_ENABLE_DDR_TRAINING_DEBUG
#define ddr_printf(args...) printf(args)
#else
#define ddr_printf(args...)
#endif
#include "wait_ddrphy_training_complete.c"
#ifndef SRC_DDRC_RCR_ADDR
#define SRC_DDRC_RCR_ADDR SRC_IPS_BASE_ADDR +0x1000
#endif
#ifndef DDR_CSD1_BASE_ADDR
#define DDR_CSD1_BASE_ADDR 0x40000000
#endif
#define SILICON_TRAIN
void imx8mq_ddr_cfg_phy(void);
volatile unsigned int tmp, tmp_t, i;
void imx8mq_ddr_init(void)
{
/** Initialize DDR clock and DDRC registers **/
reg32_write(0x30330244,0x0);
reg32_write(0x30330248,0x0);
reg32_write(0x303304ac,0xe);
reg32_write(0x303304b0,0xe);
reg32_write(0x30330504,0x2);
reg32_write(0x3038a088,0x7070000);
reg32_write(0x3038a084,0x4030000);
reg32_write(0x303a00ec,0xffff);
tmp=reg32_read(0x303a00f8);
tmp |= 0x20;
reg32_write(0x303a00f8,tmp);
reg32_write(0x30391000,0x8f000000);
reg32_write(0x30391004,0x8f000000);
reg32_write(0x30360068,0xece580);
tmp=reg32_read(0x30360060);
tmp &= ~0x80;
reg32_write(0x30360060,tmp);
tmp=reg32_read(0x30360060);
tmp |= 0x200;
reg32_write(0x30360060,tmp);
tmp=reg32_read(0x30360060);
tmp &= ~0x20;
reg32_write(0x30360060,tmp);
tmp=reg32_read(0x30360060);
tmp &= ~0x10;
reg32_write(0x30360060,tmp);
do{
tmp=reg32_read(0x30360060);
if(tmp&0x80000000) break;
}while(1);
reg32_write(0x30391000,0x8f000006);
reg32_write(0x3d400304,0x1);
reg32_write(0x3d400030,0x1);
reg32_write(0x3d400000,0xa3080020);
reg32_write(0x3d400028,0x0);
reg32_write(0x3d400020,0x203);
reg32_write(0x3d400024,0x3e800);
reg32_write(0x3d400064,0x6100e0);
reg32_write(0x3d4000d0,0xc003061c);
reg32_write(0x3d4000d4,0x9e0000);
reg32_write(0x3d4000dc,0xd4002d);
reg32_write(0x3d4000e0,0x310008);
reg32_write(0x3d4000e8,0x66004a);
reg32_write(0x3d4000ec,0x16004a);
reg32_write(0x3d400100,0x1a201b22);
reg32_write(0x3d400104,0x60633);
reg32_write(0x3d40010c,0xc0c000);
reg32_write(0x3d400110,0xf04080f);
reg32_write(0x3d400114,0x2040c0c);
reg32_write(0x3d400118,0x1010007);
reg32_write(0x3d40011c,0x401);
reg32_write(0x3d400130,0x20600);
reg32_write(0x3d400134,0xc100002);
reg32_write(0x3d400138,0xe6);
reg32_write(0x3d400144,0xa00050);
reg32_write(0x3d400180,0xc3200018);
reg32_write(0x3d400184,0x28061a8);
reg32_write(0x3d400188,0x0);
reg32_write(0x3d400190,0x497820a);
reg32_write(0x3d400194,0x80303);
reg32_write(0x3d4001a0,0xe0400018);
reg32_write(0x3d4001a4,0xdf00e4);
reg32_write(0x3d4001a8,0x80000000);
reg32_write(0x3d4001b0,0x11);
reg32_write(0x3d4001b4,0x170a);
reg32_write(0x3d4001c0,0x1);
reg32_write(0x3d4001c4,0x1);
reg32_write(0x3d4000f4,0x639);
reg32_write(0x3d400108,0x70e1617);
reg32_write(0x3d400200,0x17);
reg32_write(0x3d40020c,0x0);
reg32_write(0x3d400210,0x1f1f);
reg32_write(0x3d400204,0x80808);
reg32_write(0x3d400214,0x7070707);
reg32_write(0x3d400218,0x7070707);
reg32_write(0x3d402020,0x1);
reg32_write(0x3d402024,0xd0c0);
reg32_write(0x3d402050,0x20d040);
reg32_write(0x3d402064,0x14002f);
reg32_write(0x3d4020dc,0x940009);
reg32_write(0x3d4020e0,0x310000);
reg32_write(0x3d4020e8,0x66004a);
reg32_write(0x3d4020ec,0x16004a);
reg32_write(0x3d402100,0xb070508);
reg32_write(0x3d402104,0x3040b);
reg32_write(0x3d402108,0x305090c);
reg32_write(0x3d40210c,0x505000);
reg32_write(0x3d402110,0x4040204);
reg32_write(0x3d402114,0x2030303);
reg32_write(0x3d402118,0x1010004);
reg32_write(0x3d40211c,0x301);
reg32_write(0x3d402130,0x20300);
reg32_write(0x3d402134,0xa100002);
reg32_write(0x3d402138,0x31);
reg32_write(0x3d402144,0x220011);
reg32_write(0x3d402180,0xc0a70006);
reg32_write(0x3d402190,0x3858202);
reg32_write(0x3d402194,0x80303);
reg32_write(0x3d4021b4,0x502);
reg32_write(0x3d400244,0x0);
reg32_write(0x3d400250,0x29001505);
reg32_write(0x3d400254,0x2c);
reg32_write(0x3d40025c,0x5900575b);
reg32_write(0x3d400264,0x90000096);
reg32_write(0x3d40026c,0x1000012c);
reg32_write(0x3d400300,0x16);
reg32_write(0x3d400304,0x0);
reg32_write(0x3d40030c,0x0);
reg32_write(0x3d400320,0x1);
reg32_write(0x3d40036c,0x11);
reg32_write(0x3d400400,0x111);
reg32_write(0x3d400404,0x10f3);
reg32_write(0x3d400408,0x72ff);
reg32_write(0x3d400490,0x1);
reg32_write(0x3d400494,0xe00);
reg32_write(0x3d400498,0x62ffff);
reg32_write(0x3d40049c,0xe00);
reg32_write(0x3d4004a0,0xffff);
reg32_write(0x30391000,0x8f000004);
reg32_write(0x30391000,0x8f000000);
reg32_write(0x3d400030,0xa8);
do{
tmp=reg32_read(0x3d400004);
if(tmp&0x223) break;
}while(1);
reg32_write(0x3d400320,0x0);
reg32_write(0x3d000000,0x1);
reg32_write(0x3d4001b0,0x10);
reg32_write(0x3c040280,0x2);
reg32_write(0x3c040284,0x3);
reg32_write(0x3c040288,0x0);
reg32_write(0x3c04028c,0x1);
reg32_write(0x3c040290,0x5);
reg32_write(0x3c040294,0x7);
reg32_write(0x3c040298,0x6);
reg32_write(0x3c04029c,0x4);
reg32_write(0x3c044280,0x2);
reg32_write(0x3c044284,0x1);
reg32_write(0x3c044288,0x3);
reg32_write(0x3c04428c,0x0);
reg32_write(0x3c044290,0x6);
reg32_write(0x3c044294,0x7);
reg32_write(0x3c044298,0x4);
reg32_write(0x3c04429c,0x5);
reg32_write(0x3c048280,0x4);
reg32_write(0x3c048284,0x5);
reg32_write(0x3c048288,0x7);
reg32_write(0x3c04828c,0x6);
reg32_write(0x3c048290,0x1);
reg32_write(0x3c048294,0x3);
reg32_write(0x3c048298,0x2);
reg32_write(0x3c04829c,0x0);
reg32_write(0x3c04c280,0x4);
reg32_write(0x3c04c284,0x5);
reg32_write(0x3c04c288,0x7);
reg32_write(0x3c04c28c,0x6);
reg32_write(0x3c04c290,0x2);
reg32_write(0x3c04c294,0x1);
reg32_write(0x3c04c298,0x0);
reg32_write(0x3c04c29c,0x3);
/* Configure DDR PHY's registers */
imx8mq_ddr_cfg_phy();
reg32_write(DDRC_RFSHCTL3(0), 0x00000000);
reg32_write(DDRC_SWCTL(0), 0x0000);
/*
* ------------------- 9 -------------------
* Set DFIMISC.dfi_init_start to 1
* -----------------------------------------
*/
reg32_write(DDRC_DFIMISC(0), 0x00000030);
reg32_write(DDRC_SWCTL(0), 0x0001);
/* wait DFISTAT.dfi_init_complete to 1 */
tmp_t = 0;
while(tmp_t==0){
tmp = reg32_read(DDRC_DFISTAT(0));
tmp_t = tmp & 0x01;
tmp = reg32_read(DDRC_MRSTAT(0));
}
reg32_write(DDRC_SWCTL(0), 0x0000);
/* clear DFIMISC.dfi_init_complete_en */
reg32_write(DDRC_DFIMISC(0), 0x00000010);
reg32_write(DDRC_DFIMISC(0), 0x00000011);
reg32_write(DDRC_PWRCTL(0), 0x00000088);
tmp = reg32_read(DDRC_CRCPARSTAT(0));
/*
* set SWCTL.sw_done to enable quasi-dynamic register
* programming outside reset.
*/
reg32_write(DDRC_SWCTL(0), 0x00000001);
/* wait SWSTAT.sw_done_ack to 1 */
while((reg32_read(DDRC_SWSTAT(0)) & 0x1) == 0)
;
/* wait STAT.operating_mode([1:0] for ddr3) to normal state */
while ((reg32_read(DDRC_STAT(0)) & 0x3) != 0x1)
;
reg32_write(DDRC_PWRCTL(0), 0x00000088);
/* reg32_write(DDRC_PWRCTL(0), 0x018a); */
tmp = reg32_read(DDRC_CRCPARSTAT(0));
/* enable port 0 */
reg32_write(DDRC_PCTRL_0(0), 0x00000001);
/* enable DDR auto-refresh mode */
tmp = reg32_read(DDRC_RFSHCTL3(0)) & ~0x1;
reg32_write(DDRC_RFSHCTL3(0), tmp);
}
This diff is collapsed.
/*
* Copyright 2017 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/sections.h>
#include "imx8m_ddr.h"
#include "ddr.h"
DECLARE_GLOBAL_DATA_PTR;
#define IMEM_LEN 32768//23400 //byte
#define DMEM_LEN 16384//1720 //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_code(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");
}
}
This diff is collapsed.
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __LPDDR4_DVFS_H__
#define __LPDDR4_DVFS_H__
#include <asm/arch/ddr.h>
#define DFILP_SPT
#define ANAMIX_PLL_BASE_ADDR 0x30360000
#define HW_DRAM_PLL_CFG0_ADDR (ANAMIX_PLL_BASE_ADDR + 0x60)
#define HW_DRAM_PLL_CFG1_ADDR (ANAMIX_PLL_BASE_ADDR + 0x64)
#define HW_DRAM_PLL_CFG2_ADDR (ANAMIX_PLL_BASE_ADDR + 0x68)
#define LPDDR4_HDT_CTL_2D 0xC8 /* stage completion */
#define LPDDR4_HDT_CTL_3200_1D 0xC8 /* stage completion */
#define LPDDR4_HDT_CTL_400_1D 0xC8 /* stage completion */
#define LPDDR4_HDT_CTL_100_1D 0xC8 /* stage completion */
/* 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
#define WR_POST_EXT_3200 /* recommened to define */
/* lpddr4 phy training config */
/* 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 /* MR3[5:3] */
#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 /* MR11[2:0] */
#define LPDDR4_RTT_CA LPDDR4_RTT40 /* MR11[6:4] */
#define LPDDR4_RTT_CA_BANK0 LPDDR4_RTT40 /* MR11[6:4] */
#define LPDDR4_RTT_CA_BANK1 LPDDR4_RTT40 /* LPDDR4_RTT_DIS//MR11[6:4] */
#define LPDDR4_VREF_VALUE_CA ((1<<6)|(0xd)) /*((0<<6)|(0xe)) MR12 */
#define LPDDR4_VREF_VALUE_DQ_RANK0 ((1<<6)|(0xd)) /* MR14 */
#define LPDDR4_VREF_VALUE_DQ_RANK1 ((1<<6)|(0xd)) /* MR14 */
#define LPDDR4_MR22_RANK0 ((0<<5)|(1<<4)|(0<<3)|(LPDDR4_RTT40)) /* MR22: OP[5:3]ODTD-CA,CS,CK */
#define LPDDR4_MR22_RANK1 ((0<<5)|(1<<4)|(0<<3)|(LPDDR4_RTT40)) /* MR22: OP[5:3]ODTD-CA,CS,CK */
#define LPDDR4_MR3_PU_CAL 1 /* MR3[0] */
#define LPDDR4_2D_WEIGHT 0x1f7f
#define LPDDR4_2D_SHARE 1
#endif /*__LPDDR4_DVFS_H__ */
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
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 imx8mq_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;
}
}
}
This diff is collapsed.
......@@ -5,11 +5,11 @@
*/
#include <common.h>
#include <spl.h>
#include <asm/io.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/arch/ddr.h>
#include <asm/arch/imx8mq_pins.h>
#include <asm/arch/sys_proto.h>
#include <power/pmic.h>
......@@ -20,14 +20,13 @@
#include <asm/mach-imx/mxc_i2c.h>
#include <fsl_esdhc.h>
#include <mmc.h>
#include "ddr/ddr.h"
#include <spl.h>
DECLARE_GLOBAL_DATA_PTR;
void spl_dram_init(void)
{
/* ddr init, use old function from v2018.03 */
imx8mq_ddr_init();
ddr_init(&dram_timing);
}