summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-uniphier/ddrphy_training.c
blob: e99c5e65f2a51ed7a67b774aab319d29d05c2b63 (plain)
1
2
3
4
5
6
7
8
9
10
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
36
37
38
39
40
41
42
43
44
45
46
47
48
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * 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) {
#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;
}
OpenPOWER on IntegriCloud