ARM: UniPhier: add dump command of DDR PHY parameters

This commit adds a dump command of DDR PHY parameters of UniPhier
SoC family.  It might not be used very often for the regular operation
but it would be useful when something goes wrong with DDR memories.

Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
diff --git a/arch/arm/cpu/armv7/uniphier/Kconfig b/arch/arm/cpu/armv7/uniphier/Kconfig
index 9760299..0556e4b 100644
--- a/arch/arm/cpu/armv7/uniphier/Kconfig
+++ b/arch/arm/cpu/armv7/uniphier/Kconfig
@@ -65,6 +65,13 @@
 	bool
 	default SPL_BUILD
 
+config CMD_DDRPHY_DUMP
+	bool "Enable dump command of DDR PHY parameters"
+	depends on !SPL_BUILD
+	help
+	  The command "ddrphy" shows the resulting parameters of DDR PHY
+	  training; it is useful for the evaluation of DDR PHY training.
+
 choice
 	prompt "DDR3 Frequency select"
 	depends on DRAM_INIT
diff --git a/arch/arm/cpu/armv7/uniphier/Makefile b/arch/arm/cpu/armv7/uniphier/Makefile
index 1d84696..0546232 100644
--- a/arch/arm/cpu/armv7/uniphier/Makefile
+++ b/arch/arm/cpu/armv7/uniphier/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_BOARD_LATE_INIT) += board_late_init.o
 obj-$(CONFIG_UNIPHIER_SMP) += smp.o
 obj-$(CONFIG_CMD_PINMON) += cmd_pinmon.o
+obj-$(CONFIG_CMD_DDRPHY_DUMP) += cmd_ddrphy.o
 
 obj-y += board_common.o
 obj-$(CONFIG_PFC_MICRO_SUPPORT_CARD) += support_card.o
diff --git a/arch/arm/cpu/armv7/uniphier/cmd_ddrphy.c b/arch/arm/cpu/armv7/uniphier/cmd_ddrphy.c
new file mode 100644
index 0000000..431d901
--- /dev/null
+++ b/arch/arm/cpu/armv7/uniphier/cmd_ddrphy.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 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>
+
+/* Select either decimal or hexadecimal */
+#if 1
+#define PRINTF_FORMAT "%2d"
+#else
+#define PRINTF_FORMAT "%02x"
+#endif
+/* field separator */
+#define FS "   "
+
+static u32 read_bdl(struct ddrphy_datx8 __iomem *dx, int index)
+{
+	return (readl(&dx->bdlr[index / 5]) >> (index % 5 * 6)) & 0x3f;
+}
+
+static void dump_loop(void (*callback)(struct ddrphy_datx8 __iomem *))
+{
+	int ch, p, dx;
+	struct ddrphy __iomem *phy;
+
+	for (ch = 0; ch < NR_DDRCH; ch++) {
+		for (p = 0; p < NR_DDRPHY_PER_CH; p++) {
+			phy = (struct ddrphy __iomem *)DDRPHY_BASE(ch, p);
+
+			for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
+				printf("CH%dP%dDX%d:", ch, p, dx);
+				(*callback)(&phy->dx[dx]);
+				printf("\n");
+			}
+		}
+	}
+}
+
+static void __wbdl_dump(struct ddrphy_datx8 __iomem *dx)
+{
+	int i;
+
+	for (i = 0; i < 10; i++)
+		printf(FS PRINTF_FORMAT, read_bdl(dx, i));
+
+	printf(FS "(+" PRINTF_FORMAT ")", readl(&dx->lcdlr[1]) & 0xff);
+}
+
+void wbdl_dump(void)
+{
+	printf("\n--- Write Bit Delay Line ---\n");
+	printf("           DQ0  DQ1  DQ2  DQ3  DQ4  DQ5  DQ6  DQ7   DM  DQS  (WDQD)\n");
+
+	dump_loop(&__wbdl_dump);
+}
+
+static void __rbdl_dump(struct ddrphy_datx8 __iomem *dx)
+{
+	int i;
+
+	for (i = 15; i < 24; i++)
+		printf(FS PRINTF_FORMAT, read_bdl(dx, i));
+
+	printf(FS "(+" PRINTF_FORMAT ")", (readl(&dx->lcdlr[1]) >> 8) & 0xff);
+}
+
+void rbdl_dump(void)
+{
+	printf("\n--- Read Bit Delay Line ---\n");
+	printf("           DQ0  DQ1  DQ2  DQ3  DQ4  DQ5  DQ6  DQ7   DM  (RDQSD)\n");
+
+	dump_loop(&__rbdl_dump);
+}
+
+static void __wld_dump(struct ddrphy_datx8 __iomem *dx)
+{
+	int rank;
+	u32 lcdlr0 = readl(&dx->lcdlr[0]);
+	u32 gtr = readl(&dx->gtr);
+
+	for (rank = 0; rank < 4; rank++) {
+		u32 wld = (lcdlr0 >> (8 * rank)) & 0xff; /* Delay */
+		u32 wlsl = (gtr >> (12 + 2 * rank)) & 0x3; /* System Latency */
+
+		printf(FS PRINTF_FORMAT "%sT", wld,
+		       wlsl == 0 ? "-1" : wlsl == 1 ? "+0" : "+1");
+	}
+}
+
+void wld_dump(void)
+{
+	printf("\n--- Write Leveling Delay ---\n");
+	printf("            Rank0   Rank1   Rank2   Rank3\n");
+
+	dump_loop(&__wld_dump);
+}
+
+static void __dqsgd_dump(struct ddrphy_datx8 __iomem *dx)
+{
+	int rank;
+	u32 lcdlr2 = readl(&dx->lcdlr[2]);
+	u32 gtr = readl(&dx->gtr);
+
+	for (rank = 0; rank < 4; rank++) {
+		u32 dqsgd = (lcdlr2 >> (8 * rank)) & 0xff; /* Delay */
+		u32 dgsl = (gtr >> (3 * rank)) & 0x7; /* System Latency */
+
+		printf(FS PRINTF_FORMAT "+%dT", dqsgd, dgsl);
+	}
+}
+
+void dqsgd_dump(void)
+{
+	printf("\n--- DQS Gating Delay ---\n");
+	printf("            Rank0   Rank1   Rank2   Rank3\n");
+
+	dump_loop(&__dqsgd_dump);
+}
+
+static void __mdl_dump(struct ddrphy_datx8 __iomem *dx)
+{
+	int i;
+	u32 mdl = readl(&dx->mdlr);
+	for (i = 0; i < 3; i++)
+		printf(FS PRINTF_FORMAT, (mdl >> (8 * i)) & 0xff);
+}
+
+void mdl_dump(void)
+{
+	printf("\n--- Master Delay Line ---\n");
+	printf("          IPRD TPRD MDLD\n");
+
+	dump_loop(&__mdl_dump);
+}
+
+#define REG_DUMP(x) \
+	{ u32 __iomem *p = &phy->x; printf("%3d: %-10s: %p : %08x\n", \
+					p - (u32 *)phy, #x, p, readl(p)); }
+
+void reg_dump(void)
+{
+	int ch, p;
+	struct ddrphy __iomem *phy;
+
+	printf("\n--- DDR PHY registers ---\n");
+
+	for (ch = 0; ch < NR_DDRCH; ch++) {
+		for (p = 0; p < NR_DDRPHY_PER_CH; p++) {
+			printf("== Ch%d, PHY%d ==\n", ch, p);
+			printf(" No: Name      : Address  : Data\n");
+
+			phy = (struct ddrphy __iomem *)DDRPHY_BASE(ch, p);
+
+			REG_DUMP(ridr);
+			REG_DUMP(pir);
+			REG_DUMP(pgcr[0]);
+			REG_DUMP(pgcr[1]);
+			REG_DUMP(pgsr[0]);
+			REG_DUMP(pgsr[1]);
+			REG_DUMP(pllcr);
+			REG_DUMP(ptr[0]);
+			REG_DUMP(ptr[1]);
+			REG_DUMP(ptr[2]);
+			REG_DUMP(ptr[3]);
+			REG_DUMP(ptr[4]);
+			REG_DUMP(acmdlr);
+			REG_DUMP(acbdlr);
+			REG_DUMP(dxccr);
+			REG_DUMP(dsgcr);
+			REG_DUMP(dcr);
+			REG_DUMP(dtpr[0]);
+			REG_DUMP(dtpr[1]);
+			REG_DUMP(dtpr[2]);
+			REG_DUMP(mr0);
+			REG_DUMP(mr1);
+			REG_DUMP(mr2);
+			REG_DUMP(mr3);
+			REG_DUMP(dx[0].gcr);
+			REG_DUMP(dx[0].gtr);
+			REG_DUMP(dx[1].gcr);
+			REG_DUMP(dx[1].gtr);
+		}
+	}
+}
+
+static int do_ddr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	char *cmd = argv[1];
+
+	if (argc == 1)
+		cmd = "all";
+
+	if (!strcmp(cmd, "wbdl") || !strcmp(cmd, "all"))
+		wbdl_dump();
+
+	if (!strcmp(cmd, "rbdl") || !strcmp(cmd, "all"))
+		rbdl_dump();
+
+	if (!strcmp(cmd, "wld") || !strcmp(cmd, "all"))
+		wld_dump();
+
+	if (!strcmp(cmd, "dqsgd") || !strcmp(cmd, "all"))
+		dqsgd_dump();
+
+	if (!strcmp(cmd, "mdl") || !strcmp(cmd, "all"))
+		mdl_dump();
+
+	if (!strcmp(cmd, "reg") || !strcmp(cmd, "all"))
+		reg_dump();
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	ddr,	2,	1,	do_ddr,
+	"UniPhier DDR PHY parameters dumper",
+	"- dump all of the followings\n"
+	"ddr wbdl - dump Write Bit Delay\n"
+	"ddr rbdl - dump Read Bit Delay\n"
+	"ddr wld - dump Write Leveling\n"
+	"ddr dqsgd - dump DQS Gating Delay\n"
+	"ddr mdl - dump Master Delay Line\n"
+	"ddr reg - dump registers\n"
+);