u-boot/arch/arm/mach-uniphier/ddrphy/ddrphy-training.c
Masahiro Yamada 323d1f9d5b ARM: uniphier: allow to enable multiple SoCs
Before this commit, the Kconfig menu in mach-uniphier only allowed us
to choose one SoC to be compiled.  Each SoC has its own defconfig file
for the build-test coverage.  Consequently, some defconfig files are
duplicated with only the difference in CONFIG_DEFAULT_DEVICE_TREE and
CONFIG_{SOC_NAME}=y.

Now, most of board-specific parameters have been moved to device trees,
so it makes sense to include init code of multiple SoCs into a single
image as long as the SoCs have similar architecture.  In fact, some
SoCs of UniPhier family are very similar:
 - PH1-LD4 and PH1-sLD8
 - PH1-LD6b and ProXstream2 (will be added in the upcoming commit)

This commit will be helpful to merge some defconfig files for better
maintainability.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
2015-09-25 00:58:38 +09:00

139 lines
2.6 KiB
C

/*
* Copyright (C) 2011-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <linux/io.h>
#include <mach/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) {
printf("%s: error: timeout during DDR training\n",
__func__);
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) {
printf("%s: error: %s failed\n", __func__,
init_sequence[i].description);
return -1;
}
}
#ifdef DISPLAY_ELAPSED_TIME
printf("%s: info: elapsed time %ld msec\n", get_timer(start));
#endif
return 0;
}