Commit b614e16c authored by Masahiro Yamada's avatar Masahiro Yamada
Browse files

ARM: UniPhier: add DDR PHY training code



This training code provides run-time adjustment of DDR PHY parameters
for stable DDR operation.
Signed-off-by: default avatarMasahiro Yamada <yamada.m@jp.panasonic.com>
parent 125738e8
......@@ -10,6 +10,7 @@ obj-y += reset.o
obj-y += cache_uniphier.o
obj-$(CONFIG_BOARD_POSTCLK_INIT) += board_postclk_init.o
obj-y += dram_init.o
obj-$(CONFIG_DRAM_INIT) += ddrphy_training.o
obj-$(CONFIG_DISPLAY_CPUINFO) += cpu_info.o
obj-$(CONFIG_BOARD_EARLY_INIT_R) += board_early_init_r.o
obj-$(CONFIG_BOARD_LATE_INIT) += board_late_init.o
......
/*
* Copyright (C) 2011-2014 Panasonic Corporation
* Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/ddrphy-regs.h>
void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank)
{
int dx;
u32 __iomem tmp, *p;
for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
p = &phy->dx[dx].gcr;
tmp = readl(p);
/* Specify the rank that should be write leveled */
tmp &= ~DXGCR_WLRKEN_MASK;
tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK;
writel(tmp, p);
}
p = &phy->dtcr;
tmp = readl(p);
/* Specify the rank used during data bit deskew and eye centering */
tmp &= ~DTCR_DTRANK_MASK;
tmp |= (rank << DTCR_DTRANK_SHIFT) & DTCR_DTRANK_MASK;
/* Use Multi-Purpose Register for DQS gate training */
tmp |= DTCR_DTMPR;
/* Specify the rank enabled for data-training */
tmp &= ~DTCR_RNKEN_MASK;
tmp |= (1 << (DTCR_RNKEN_SHIFT + rank)) & DTCR_RNKEN_MASK;
writel(tmp, p);
}
struct ddrphy_init_sequence {
char *description;
u32 init_flag;
u32 done_flag;
u32 err_flag;
};
static struct ddrphy_init_sequence init_sequence[] = {
{
"DRAM Initialization",
PIR_DRAMRST | PIR_DRAMINIT,
PGSR0_DIDONE,
PGSR0_DIERR
},
{
"Write Leveling",
PIR_WL,
PGSR0_WLDONE,
PGSR0_WLERR
},
{
"Read DQS Gate Training",
PIR_QSGATE,
PGSR0_QSGDONE,
PGSR0_QSGERR
},
{
"Write Leveling Adjustment",
PIR_WLADJ,
PGSR0_WLADONE,
PGSR0_WLAERR
},
{
"Read Bit Deskew",
PIR_RDDSKW,
PGSR0_RDDONE,
PGSR0_RDERR
},
{
"Write Bit Deskew",
PIR_WRDSKW,
PGSR0_WDDONE,
PGSR0_WDERR
},
{
"Read Eye Training",
PIR_RDEYE,
PGSR0_REDONE,
PGSR0_REERR
},
{
"Write Eye Training",
PIR_WREYE,
PGSR0_WEDONE,
PGSR0_WEERR
}
};
int ddrphy_training(struct ddrphy __iomem *phy)
{
int i;
u32 pgsr0;
u32 init_flag = PIR_INIT;
u32 done_flag = PGSR0_IDONE;
int timeout = 50000; /* 50 msec is long enough */
#ifdef DISPLAY_ELAPSED_TIME
ulong start = get_timer(0);
#endif
for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
init_flag |= init_sequence[i].init_flag;
done_flag |= init_sequence[i].done_flag;
}
writel(init_flag, &phy->pir);
do {
if (--timeout < 0) {
#ifndef CONFIG_SPL_BUILD
printf("%s: error: timeout during DDR training\n",
__func__);
#endif
return -1;
}
udelay(1);
pgsr0 = readl(&phy->pgsr[0]);
} while ((pgsr0 & done_flag) != done_flag);
for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
if (pgsr0 & init_sequence[i].err_flag) {
#ifndef CONFIG_SPL_BUILD
printf("%s: error: %s failed\n", __func__,
init_sequence[i].description);
#endif
return -1;
}
}
#ifdef DISPLAY_ELAPSED_TIME
printf("%s: info: elapsed time %ld msec\n", get_timer(start));
#endif
return 0;
}
......@@ -8,4 +8,4 @@ obj-y += boot-mode.o
obj-$(CONFIG_SOC_INIT) += bcu_init.o sbc_init.o sg_init.o pll_init.o \
clkrst_init.o
obj-$(CONFIG_BOARD_POSTCLK_INIT) += pinctrl.o
obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o
obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o ddrphy_init.o
/*
* Copyright (C) 2014 Panasonic Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/types.h>
#include <asm/io.h>
#include <asm/arch/ddrphy-regs.h>
void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size)
{
u32 tmp;
writel(0x0300c473, &phy->pgcr[1]);
if (freq == 1333) {
writel(0x0a806844, &phy->ptr[0]);
writel(0x208e0124, &phy->ptr[1]);
} else {
writel(0x0c807d04, &phy->ptr[0]);
writel(0x2710015E, &phy->ptr[1]);
}
writel(0x00083DEF, &phy->ptr[2]);
if (freq == 1333) {
writel(0x0f051616, &phy->ptr[3]);
writel(0x06ae08d6, &phy->ptr[4]);
} else {
writel(0x12061A80, &phy->ptr[3]);
writel(0x08027100, &phy->ptr[4]);
}
writel(0xF004001A, &phy->dsgcr);
/* change the value of the on-die pull-up/pull-down registors */
tmp = readl(&phy->dxccr);
tmp &= ~0x0ee0;
tmp |= DXCCR_DQSNRES_688_OHM | DXCCR_DQSRES_688_OHM;
writel(tmp, &phy->dxccr);
writel(0x0000040B, &phy->dcr);
if (freq == 1333) {
writel(0x85589955, &phy->dtpr[0]);
if (size == 1)
writel(0x1a8253c0, &phy->dtpr[1]);
else
writel(0x1a8363c0, &phy->dtpr[1]);
writel(0x5002c200, &phy->dtpr[2]);
writel(0x00000b51, &phy->mr0);
} else {
writel(0x999cbb66, &phy->dtpr[0]);
if (size == 1)
writel(0x1a82dbc0, &phy->dtpr[1]);
else
writel(0x1a878400, &phy->dtpr[1]);
writel(0xa00214f8, &phy->dtpr[2]);
writel(0x00000d71, &phy->mr0);
}
writel(0x00000006, &phy->mr1);
if (freq == 1333)
writel(0x00000290, &phy->mr2);
else
writel(0x00000298, &phy->mr2);
writel(0x00000800, &phy->mr3);
while (!(readl(&phy->pgsr[0]) & PGSR0_IDONE))
;
writel(0x0300C473, &phy->pgcr[1]);
writel(0x0000005D, &phy->zq[0].cr[1]);
}
......@@ -7,6 +7,7 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch/umc-regs.h>
#include <asm/arch/ddrphy-regs.h>
static inline void umc_start_ssif(void __iomem *ssif_base)
{
......@@ -125,6 +126,8 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
void __iomem *ca_base1 = (void __iomem *)UMC_CA_BASE(1);
void __iomem *dramcont0 = (void __iomem *)UMC_DRAMCONT_BASE(0);
void __iomem *dramcont1 = (void __iomem *)UMC_DRAMCONT_BASE(1);
void __iomem *phy0_0 = (void __iomem *)DDRPHY_BASE(0, 0);
void __iomem *phy1_0 = (void __iomem *)DDRPHY_BASE(1, 0);
umc_dram_init_start(dramcont0);
umc_dram_init_start(dramcont1);
......@@ -133,8 +136,18 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
writel(0x00000101, dramcont0 + UMC_DIOCTLA);
ddrphy_init(phy0_0, freq, size_ch0);
ddrphy_prepare_training(phy0_0, 0);
ddrphy_training(phy0_0);
writel(0x00000101, dramcont1 + UMC_DIOCTLA);
ddrphy_init(phy1_0, freq, size_ch1);
ddrphy_prepare_training(phy1_0, 1);
ddrphy_training(phy1_0);
umc_dramcont_init(dramcont0, ca_base0, size_ch0, freq);
umc_dramcont_init(dramcont1, ca_base1, size_ch1, freq);
......
......@@ -7,4 +7,4 @@ obj-$(if $(CONFIG_OF_CONTROL),,y) += platdevice.o
obj-y += boot-mode.o
obj-$(CONFIG_SOC_INIT) += sbc_init.o sg_init.o pll_init.o clkrst_init.o
obj-$(CONFIG_BOARD_POSTCLK_INIT) += pinctrl.o
obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o
obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o ddrphy_init.o
/*
* Copyright (C) 2014 Panasonic Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/types.h>
#include <asm/io.h>
#include <asm/arch/ddrphy-regs.h>
void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size)
{
u32 tmp;
writel(0x0300c473, &phy->pgcr[1]);
if (freq == 1333) {
writel(0x0a806844, &phy->ptr[0]);
writel(0x208e0124, &phy->ptr[1]);
} else {
writel(0x0c807d04, &phy->ptr[0]);
writel(0x2710015E, &phy->ptr[1]);
}
writel(0x00083DEF, &phy->ptr[2]);
if (freq == 1333) {
writel(0x0f051616, &phy->ptr[3]);
writel(0x06ae08d6, &phy->ptr[4]);
} else {
writel(0x12061A80, &phy->ptr[3]);
writel(0x08027100, &phy->ptr[4]);
}
writel(0xF004001A, &phy->dsgcr);
/* change the value of the on-die pull-up/pull-down registors */
tmp = readl(&phy->dxccr);
tmp &= ~0x0ee0;
tmp |= DXCCR_DQSNRES_688_OHM | DXCCR_DQSRES_688_OHM;
writel(tmp, &phy->dxccr);
writel(0x0000040B, &phy->dcr);
if (freq == 1333) {
writel(0x85589955, &phy->dtpr[0]);
if (size == 1)
writel(0x1a8363c0, &phy->dtpr[1]);
else
writel(0x1a8363c0, &phy->dtpr[1]);
writel(0x5002c200, &phy->dtpr[2]);
writel(0x00000b51, &phy->mr0);
} else {
writel(0x999cbb66, &phy->dtpr[0]);
if (size == 1)
writel(0x1a878400, &phy->dtpr[1]);
else
writel(0x1a878400, &phy->dtpr[1]);
writel(0xa00214f8, &phy->dtpr[2]);
writel(0x00000d71, &phy->mr0);
}
writel(0x00000006, &phy->mr1);
if (freq == 1333)
writel(0x00000290, &phy->mr2);
else
writel(0x00000298, &phy->mr2);
writel(0x00000000, &phy->mr3);
while (!(readl(&phy->pgsr[0]) & PGSR0_IDONE))
;
writel(0x0300C473, &phy->pgcr[1]);
writel(0x0000005D, &phy->zq[0].cr[1]);
}
......@@ -7,6 +7,7 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch/umc-regs.h>
#include <asm/arch/ddrphy-regs.h>
static inline void umc_start_ssif(void __iomem *ssif_base)
{
......@@ -94,6 +95,10 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
void __iomem *ca_base1 = (void __iomem *)UMC_CA_BASE(1);
void __iomem *dramcont0 = (void __iomem *)UMC_DRAMCONT_BASE(0);
void __iomem *dramcont1 = (void __iomem *)UMC_DRAMCONT_BASE(1);
void __iomem *phy0_0 = (void __iomem *)DDRPHY_BASE(0, 0);
void __iomem *phy0_1 = (void __iomem *)DDRPHY_BASE(0, 1);
void __iomem *phy1_0 = (void __iomem *)DDRPHY_BASE(1, 0);
void __iomem *phy1_1 = (void __iomem *)DDRPHY_BASE(1, 1);
umc_dram_init_start(dramcont0);
umc_dram_init_start(dramcont1);
......@@ -102,12 +107,32 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
writel(0x00000101, dramcont0 + UMC_DIOCTLA);
ddrphy_init(phy0_0, freq, size_ch0);
ddrphy_prepare_training(phy0_0, 0);
ddrphy_training(phy0_0);
writel(0x00000103, dramcont0 + UMC_DIOCTLA);
ddrphy_init(phy0_1, freq, size_ch0);
ddrphy_prepare_training(phy0_1, 1);
ddrphy_training(phy0_1);
writel(0x00000101, dramcont1 + UMC_DIOCTLA);
ddrphy_init(phy1_0, freq, size_ch1);
ddrphy_prepare_training(phy1_0, 0);
ddrphy_training(phy1_0);
writel(0x00000103, dramcont1 + UMC_DIOCTLA);
ddrphy_init(phy1_1, freq, size_ch1);
ddrphy_prepare_training(phy1_1, 1);
ddrphy_training(phy1_1);
umc_dramcont_init(dramcont0, ca_base0, size_ch0, freq);
umc_dramcont_init(dramcont1, ca_base1, size_ch1, freq);
......
......@@ -8,4 +8,4 @@ obj-y += boot-mode.o
obj-$(CONFIG_SOC_INIT) += bcu_init.o sbc_init.o sg_init.o pll_init.o \
clkrst_init.o
obj-$(CONFIG_BOARD_POSTCLK_INIT) += pinctrl.o
obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o
obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o ddrphy_init.o
/*
* Copyright (C) 2014 Panasonic Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/arch/ddrphy-regs.h>
void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size)
{
u32 tmp;
writel(0x0300c473, &phy->pgcr[1]);
if (freq == 1333) {
writel(0x0a806844, &phy->ptr[0]);
writel(0x208e0124, &phy->ptr[1]);
} else {
writel(0x0c807d04, &phy->ptr[0]);
writel(0x2710015E, &phy->ptr[1]);
}
writel(0x00083DEF, &phy->ptr[2]);
if (freq == 1333) {
writel(0x0f051616, &phy->ptr[3]);
writel(0x06ae08d6, &phy->ptr[4]);
} else {
writel(0x12061A80, &phy->ptr[3]);
writel(0x08027100, &phy->ptr[4]);
}
writel(0xF004001A, &phy->dsgcr);
/* change the value of the on-die pull-up/pull-down registors */
tmp = readl(&phy->dxccr);
tmp &= ~0x0ee0;
tmp |= DXCCR_DQSNRES_688_OHM | DXCCR_DQSRES_688_OHM;
writel(tmp, &phy->dxccr);
writel(0x0000040B, &phy->dcr);
if (freq == 1333) {
writel(0x85589955, &phy->dtpr[0]);
if (size == 1)
writel(0x1a8363c0, &phy->dtpr[1]);
else
writel(0x1a8363c0, &phy->dtpr[1]);
writel(0x5002c200, &phy->dtpr[2]);
writel(0x00000b51, &phy->mr0);
} else {
writel(0x999cbb66, &phy->dtpr[0]);
if (size == 1)
writel(0x1a878400, &phy->dtpr[1]);
else
writel(0x1a878400, &phy->dtpr[1]);
writel(0xa00214f8, &phy->dtpr[2]);
writel(0x00000d71, &phy->mr0);
}
writel(0x00000006, &phy->mr1);
if (freq == 1333)
writel(0x00000290, &phy->mr2);
else
writel(0x00000298, &phy->mr2);
#ifdef CONFIG_DDR_STANDARD
writel(0x00000000, &phy->mr3);
#else
writel(0x00000800, &phy->mr3);
#endif
while (!(readl(&phy->pgsr[0]) & PGSR0_IDONE))
;
writel(0x0300C473, &phy->pgcr[1]);
writel(0x0000005D, &phy->zq[0].cr[1]);
}
......@@ -7,6 +7,7 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch/umc-regs.h>
#include <asm/arch/ddrphy-regs.h>
static inline void umc_start_ssif(void __iomem *ssif_base)
{
......@@ -105,6 +106,8 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
void __iomem *ca_base1 = (void __iomem *)UMC_CA_BASE(1);
void __iomem *dramcont0 = (void __iomem *)UMC_DRAMCONT_BASE(0);
void __iomem *dramcont1 = (void __iomem *)UMC_DRAMCONT_BASE(1);
void __iomem *phy0_0 = (void __iomem *)DDRPHY_BASE(0, 0);
void __iomem *phy1_0 = (void __iomem *)DDRPHY_BASE(1, 0);
umc_dram_init_start(dramcont0);
umc_dram_init_start(dramcont1);
......@@ -113,8 +116,18 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
writel(0x00000101, dramcont0 + UMC_DIOCTLA);
ddrphy_init(phy0_0, freq, size_ch0);
ddrphy_prepare_training(phy0_0, 0);
ddrphy_training(phy0_0);
writel(0x00000101, dramcont1 + UMC_DIOCTLA);
ddrphy_init(phy1_0, freq, size_ch1);
ddrphy_prepare_training(phy1_0, 1);
ddrphy_training(phy1_0);
umc_dramcont_init(dramcont0, ca_base0, size_ch0, freq);
umc_dramcont_init(dramcont1, ca_base1, size_ch1, freq);
......
/*
* UniPhier DDR PHY registers
*
* Copyright (C) 2014 Panasonic Corporation
* Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef ARCH_DDRPHY_REGS_H
#define ARCH_DDRPHY_REGS_H
#include <linux/compiler.h>
#ifndef __ASSEMBLY__
struct ddrphy {
u32 ridr; /* Revision Identification Register */
u32 pir; /* PHY Initialixation Register */
u32 pgcr[2]; /* PHY General Configuration Register */
u32 pgsr[2]; /* PHY General Status Register */
u32 pllcr; /* PLL Control Register */
u32 ptr[5]; /* PHY Timing Register */
u32 acmdlr; /* AC Master Delay Line Register */
u32 acbdlr; /* AC Bit Delay Line Register */
u32 aciocr; /* AC I/O Configuration Register */
u32 dxccr; /* DATX8 Common Configuration Register */
u32 dsgcr; /* DDR System General Configuration Register */
u32 dcr; /* DRAM Configuration Register */
u32 dtpr[3]; /* DRAM Timing Parameters Register */
u32 mr0; /* Mode Register 0 */
u32 mr1; /* Mode Register 1 */
u32 mr2; /* Mode Register 2 */
u32 mr3; /* Mode Register 3 */
u32 odtcr; /* ODT Configuration Register */
u32 dtcr; /* Data Training Configuration Register */
u32 dtar[4]; /* Data Training Address Register */
u32 dtdr[2]; /* Data Training Data Register */
u32 dtedr[2]; /* Data Training Eye Data Register */
u32 rsv0[13]; /* Reserved */
u32 dcuar; /* DCU Address Register */
u32 dcudr; /* DCU Data Register */
u32 dcurr; /* DCU Run Register */
u32 dculr; /* DCU Loop Register */
u32 dcugcr; /* DCU General Configuration Register */
u32 dcutpr; /* DCU Timing Parameters Register */
u32 dcusr[2]; /* DCU Status Register */
u32 rsv1[8]; /* Reserved */
u32 bistrr; /* BIST Run Register */
u32 bistwcr; /* BIST Word Count Register */
u32 bistmskr[3]; /* BIST Mask Register */
u32 bistlsr; /* BIST LFSR Sed Register */
u32 bistar[3]; /* BIST Address Register */
u32 bistudpr; /* BIST User Data Pattern Register */
u32 bistgsr; /* BIST General Status Register */
u32 bistwer; /* BIST Word Error Register */
u32 bistber[4]; /* BIST Bit Error Register */
u32 bistwcsr; /* BIST Word Count Status Register */
u32 bistfwr[3]; /* BIST Fail Word Register */
u32 rsv2[10]; /* Reserved */
u32 gpr[2]; /* General Purpose Register */
struct ddrphy_zq { /* ZQ */
u32 cr[2]; /* Impedance Control Register */
u32 sr[2]; /* Impedance Status Register */
} zq[4];
struct ddrphy_datx8 { /* DATX8 */
u32 gcr; /* General Configuration Register */
u32 gsr[2]; /* General Status Register */
u32 bdlr[5]; /* Bit Delay Line Register */
u32 lcdlr[3]; /* Local Calibrated Delay Line Register */
u32 mdlr; /* Master Delay Line Register */
u32 gtr; /* General Timing Register */
u32 rsv[3]; /* Reserved */
} dx[9];
} __packed;
#endif /* __ASSEMBLY__ */
#define PIR_INIT (1 << 0) /* Initialization Trigger */
#define PIR_ZCAL (1 << 1) /* Impedance Calibration */
#define PIR_PLLINIT (1 << 4) /* PLL Initialization */