ddrphy-training.c 2.67 KB
Newer Older
1
/*
2
 * Copyright (C) 2011-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3 4 5 6 7
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
8
#include <linux/err.h>
9
#include <linux/io.h>
10
#include <mach/ddrphy-regs.h>
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

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 */
36 37
	tmp &= ~DTCR_RANKEN_MASK;
	tmp |= (1 << (DTCR_RANKEN_SHIFT + rank)) & DTCR_RANKEN_MASK;
38 39 40 41 42 43 44 45 46 47
	writel(tmp, p);
}

struct ddrphy_init_sequence {
	char *description;
	u32 init_flag;
	u32 done_flag;
	u32 err_flag;
};

48
static const struct ddrphy_init_sequence init_sequence[] = {
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
	{
		"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__);
121
			return -ETIMEDOUT;
122 123 124 125 126 127 128 129 130
		}
		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);
131
			return -EIO;
132 133 134 135 136 137 138 139 140
		}
	}

#ifdef DISPLAY_ELAPSED_TIME
	printf("%s: info: elapsed time %ld msec\n", get_timer(start));
#endif

	return 0;
}