aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-zynqmp/ecc_spl_init.c
blob: f547d8e3a5bfe076b6d72154fb83a9482bd850cc (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// SPDX-License-Identifier: MIT
/*
 *  Copyright(c) 2015 - 2020 Xilinx, Inc.
 *
 *  Jorge Ramirez-Ortiz <jorge@foundries.io>
 */

#include <common.h>
#include <cpu_func.h>
#include <asm/arch/hardware.h>
#include <asm/arch/ecc_spl_init.h>
#include <asm/io.h>
#include <linux/delay.h>

#define ZDMA_TRANSFER_MAX_LEN		(0x3FFFFFFFU - 7U)
#define ZDMA_CH_STATUS			((ADMA_CH0_BASEADDR) + 0x0000011CU)
#define ZDMA_CH_STATUS_STATE_MASK	0x00000003U
#define ZDMA_CH_STATUS_STATE_DONE	0x00000000U
#define ZDMA_CH_STATUS_STATE_ERR	0x00000003U
#define ZDMA_CH_CTRL0			((ADMA_CH0_BASEADDR) + 0x00000110U)
#define ZDMA_CH_CTRL0_POINT_TYPE_MASK	(u32)0x00000040U
#define ZDMA_CH_CTRL0_POINT_TYPE_NORMAL	(u32)0x00000000U
#define ZDMA_CH_CTRL0_MODE_MASK		(u32)0x00000030U
#define ZDMA_CH_CTRL0_MODE_WR_ONLY	(u32)0x00000010U
#define ZDMA_CH_CTRL0_TOTAL_BYTE_COUNT	((ADMA_CH0_BASEADDR) + 0x00000188U)
#define ZDMA_CH_WR_ONLY_WORD0		((ADMA_CH0_BASEADDR) + 0x00000148U)
#define ZDMA_CH_WR_ONLY_WORD1		((ADMA_CH0_BASEADDR) + 0x0000014CU)
#define ZDMA_CH_WR_ONLY_WORD2		((ADMA_CH0_BASEADDR) + 0x00000150U)
#define ZDMA_CH_WR_ONLY_WORD3		((ADMA_CH0_BASEADDR) + 0x00000154U)
#define ZDMA_CH_DST_DSCR_WORD0		((ADMA_CH0_BASEADDR) + 0x00000138U)
#define ZDMA_CH_DST_DSCR_WORD0_LSB_MASK	0xFFFFFFFFU
#define ZDMA_CH_DST_DSCR_WORD1		((ADMA_CH0_BASEADDR) + 0x0000013CU)
#define ZDMA_CH_DST_DSCR_WORD1_MSB_MASK	0x0001FFFFU
#define ZDMA_CH_SRC_DSCR_WORD2		((ADMA_CH0_BASEADDR) + 0x00000130U)
#define ZDMA_CH_DST_DSCR_WORD2		((ADMA_CH0_BASEADDR) + 0x00000140U)
#define ZDMA_CH_CTRL2			((ADMA_CH0_BASEADDR) + 0x00000200U)
#define ZDMA_CH_CTRL2_EN_MASK		0x00000001U
#define ZDMA_CH_ISR			((ADMA_CH0_BASEADDR) + 0x00000100U)
#define ZDMA_CH_ISR_DMA_DONE_MASK	0x00000400U
#define ECC_INIT_VAL_WORD		0xDEADBEEFU

#define ZDMA_IDLE_TIMEOUT_USEC		1000000
#define ZDMA_DONE_TIMEOUT_USEC		5000000

static void ecc_zdma_restore(void)
{
	/* Restore reset values for the DMA registers used */
	writel(ZDMA_CH_CTRL0, 0x00000080U);
	writel(ZDMA_CH_WR_ONLY_WORD0, 0x00000000U);
	writel(ZDMA_CH_WR_ONLY_WORD1, 0x00000000U);
	writel(ZDMA_CH_WR_ONLY_WORD2, 0x00000000U);
	writel(ZDMA_CH_WR_ONLY_WORD3, 0x00000000U);
	writel(ZDMA_CH_DST_DSCR_WORD0, 0x00000000U);
	writel(ZDMA_CH_DST_DSCR_WORD1, 0x00000000U);
	writel(ZDMA_CH_SRC_DSCR_WORD2, 0x00000000U);
	writel(ZDMA_CH_DST_DSCR_WORD2, 0x00000000U);
	writel(ZDMA_CH_CTRL0_TOTAL_BYTE_COUNT, 0x00000000U);
}

static void ecc_dram_bank_init(u64 addr, u64 len)
{
	bool retry = true;
	u32 timeout;
	u64 bytes;
	u32 size;
	u64 src;
	u32 reg;

	if (!len)
		return;
retry:
	bytes = len;
	src = addr;
	ecc_zdma_restore();
	while (bytes > 0) {
		size = bytes > ZDMA_TRANSFER_MAX_LEN ?
			ZDMA_TRANSFER_MAX_LEN : (u32)bytes;

		/* Wait until the DMA is in idle state */
		timeout = ZDMA_IDLE_TIMEOUT_USEC;
		do {
			udelay(1);
			reg = readl(ZDMA_CH_STATUS);
			reg &= ZDMA_CH_STATUS_STATE_MASK;
			if (!timeout--) {
				puts("error, ECC DMA failed to idle\n");
				goto done;
			}

		} while ((reg != ZDMA_CH_STATUS_STATE_DONE) &&
			(reg != ZDMA_CH_STATUS_STATE_ERR));

		/* Enable Simple (Write Only) Mode */
		reg = readl(ZDMA_CH_CTRL0);
		reg &= (ZDMA_CH_CTRL0_POINT_TYPE_MASK |
			ZDMA_CH_CTRL0_MODE_MASK);
		reg |= (ZDMA_CH_CTRL0_POINT_TYPE_NORMAL |
			ZDMA_CH_CTRL0_MODE_WR_ONLY);
		writel(reg, ZDMA_CH_CTRL0);

		/* Fill in the data to be written */
		writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD0);
		writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD1);
		writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD2);
		writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD3);

		/* Write Destination Address */
		writel((u32)(src & ZDMA_CH_DST_DSCR_WORD0_LSB_MASK),
		       ZDMA_CH_DST_DSCR_WORD0);
		writel((u32)((src >> 32) & ZDMA_CH_DST_DSCR_WORD1_MSB_MASK),
		       ZDMA_CH_DST_DSCR_WORD1);

		/* Size to be Transferred. Recommended to set both src and dest sizes */
		writel(size, ZDMA_CH_SRC_DSCR_WORD2);
		writel(size, ZDMA_CH_DST_DSCR_WORD2);

		/* DMA Enable */
		reg = readl(ZDMA_CH_CTRL2);
		reg |= ZDMA_CH_CTRL2_EN_MASK;
		writel(reg, ZDMA_CH_CTRL2);

		/* Check the status of the transfer by polling on DMA Done */
		timeout = ZDMA_DONE_TIMEOUT_USEC;
		do {
			udelay(1);
			reg = readl(ZDMA_CH_ISR);
			reg &= ZDMA_CH_ISR_DMA_DONE_MASK;
			if (!timeout--) {
				puts("error, ECC DMA timeout\n");
				goto done;
			}
		} while (reg != ZDMA_CH_ISR_DMA_DONE_MASK);

		/* Clear DMA status */
		reg = readl(ZDMA_CH_ISR);
		reg |= ZDMA_CH_ISR_DMA_DONE_MASK;
		writel(ZDMA_CH_ISR_DMA_DONE_MASK, ZDMA_CH_ISR);

		/* Read the channel status for errors */
		reg = readl(ZDMA_CH_STATUS);
		if (reg == ZDMA_CH_STATUS_STATE_ERR) {
			if (retry) {
				retry = false;
				goto retry;
			}
			puts("error, ECC DMA error\n");
			break;
		}

		bytes -= size;
		src += size;
	}
done:
	ecc_zdma_restore();
}

void zynqmp_ecc_init(void)
{
	ecc_dram_bank_init(CONFIG_SPL_ZYNQMP_DRAM_BANK1_BASE,
			   CONFIG_SPL_ZYNQMP_DRAM_BANK1_LEN);
	ecc_dram_bank_init(CONFIG_SPL_ZYNQMP_DRAM_BANK2_BASE,
			   CONFIG_SPL_ZYNQMP_DRAM_BANK2_LEN);
}