]>
Commit | Line | Data |
---|---|---|
531e3e8b PK |
1 | /* |
2 | * (C) Copyright 2007 | |
3 | * Developed for DENX Software Engineering GmbH. | |
4 | * | |
5 | * Author: Pavel Kolesnikov <concord@emcraft.com> | |
6 | * | |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
531e3e8b PK |
8 | */ |
9 | ||
10 | /* define DEBUG for debugging output (obviously ;-)) */ | |
11 | #if 0 | |
12 | #define DEBUG | |
13 | #endif | |
14 | ||
15 | #include <common.h> | |
16 | #include <watchdog.h> | |
17 | ||
0a51e924 | 18 | #if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) |
531e3e8b PK |
19 | |
20 | #include <post.h> | |
21 | ||
6d0f6bcf | 22 | #if CONFIG_POST & CONFIG_SYS_POST_ECC |
531e3e8b PK |
23 | |
24 | /* | |
25 | * MEMORY ECC test | |
26 | * | |
27 | * This test performs the checks ECC facility of memory. | |
28 | */ | |
29 | #include <asm/processor.h> | |
30 | #include <asm/mmu.h> | |
31 | #include <asm/io.h> | |
b36df561 | 32 | #include <asm/ppc440.h> |
531e3e8b | 33 | |
531e3e8b PK |
34 | DECLARE_GLOBAL_DATA_PTR; |
35 | ||
d62b247d | 36 | #if defined(DEBUG) |
4b3cc6ec | 37 | const static uint8_t syndrome_codes[] = { |
0d9cdeac | 38 | 0xF4, 0XF1, 0XEC, 0XEA, 0XE9, 0XE6, 0XE5, 0XE3, |
531e3e8b PK |
39 | 0XDC, 0XDA, 0XD9, 0XD6, 0XD5, 0XD3, 0XCE, 0XCB, |
40 | 0xB5, 0XB0, 0XAD, 0XAB, 0XA8, 0XA7, 0XA4, 0XA2, | |
41 | 0X9D, 0X9B, 0X98, 0X97, 0X94, 0X92, 0X8F, 0X8A, | |
42 | 0x75, 0x70, 0X6D, 0X6B, 0X68, 0X67, 0X64, 0X62, | |
43 | 0X5E, 0X5B, 0X58, 0X57, 0X54, 0X52, 0X4F, 0X4A, | |
44 | 0x34, 0x31, 0X2C, 0X2A, 0X29, 0X26, 0X25, 0X23, | |
45 | 0X1C, 0X1A, 0X19, 0X16, 0X15, 0X13, 0X0E, 0X0B, | |
46 | 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 | |
47 | }; | |
d62b247d | 48 | #endif |
531e3e8b PK |
49 | |
50 | #define ECC_START_ADDR 0x10 | |
51 | #define ECC_STOP_ADDR 0x2000 | |
a724a9b4 LJ |
52 | #define ECC_PATTERN 0x01010101 |
53 | #define ECC_PATTERN_CORR 0x11010101 | |
4b3cc6ec | 54 | #define ECC_PATTERN_UNCORR 0x61010101 |
531e3e8b | 55 | |
4b3cc6ec | 56 | inline static void disable_ecc(void) |
531e3e8b | 57 | { |
4b3cc6ec | 58 | uint32_t value; |
531e3e8b | 59 | |
4b3cc6ec LJ |
60 | sync(); /* Wait for any pending memory accesses to complete. */ |
61 | mfsdram(DDR0_22, value); | |
62 | mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) | |
63 | | DDR0_22_CTRL_RAW_ECC_DISABLE); | |
64 | } | |
531e3e8b | 65 | |
4b3cc6ec LJ |
66 | inline static void clear_and_enable_ecc(void) |
67 | { | |
68 | uint32_t value; | |
531e3e8b | 69 | |
4b3cc6ec | 70 | sync(); /* Wait for any pending memory accesses to complete. */ |
531e3e8b | 71 | mfsdram(DDR0_00, value); |
4b3cc6ec LJ |
72 | mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL); |
73 | mfsdram(DDR0_22, value); | |
74 | mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) | |
75 | | DDR0_22_CTRL_RAW_ECC_ENABLE); | |
76 | } | |
77 | ||
78 | static uint32_t get_ecc_status(void) | |
79 | { | |
80 | uint32_t int_status; | |
81 | #if defined(DEBUG) | |
82 | uint8_t syndrome; | |
83 | uint32_t hdata, ldata, haddr, laddr; | |
84 | uint32_t value; | |
85 | #endif | |
86 | ||
87 | mfsdram(DDR0_00, int_status); | |
88 | int_status &= DDR0_00_INT_STATUS_MASK; | |
531e3e8b | 89 | |
4b3cc6ec LJ |
90 | #if defined(DEBUG) |
91 | if (int_status & (DDR0_00_INT_STATUS_BIT0 | DDR0_00_INT_STATUS_BIT1)) { | |
531e3e8b PK |
92 | mfsdram(DDR0_32, laddr); |
93 | mfsdram(DDR0_33, haddr); | |
4b3cc6ec LJ |
94 | haddr &= 0x00000001; |
95 | if (int_status & DDR0_00_INT_STATUS_BIT1) | |
96 | debug("Multiple accesses"); | |
97 | else | |
98 | debug("A single access"); | |
99 | ||
100 | debug(" outside the defined physical memory space detected\n" | |
101 | " addr = 0x%01x%08x\n", haddr, laddr); | |
531e3e8b | 102 | } |
4b3cc6ec LJ |
103 | if (int_status & (DDR0_00_INT_STATUS_BIT2 | DDR0_00_INT_STATUS_BIT3)) { |
104 | unsigned int bit; | |
105 | ||
106 | mfsdram(DDR0_23, value); | |
107 | syndrome = (value >> 16) & 0xff; | |
108 | for (bit = 0; bit < sizeof(syndrome_codes); bit++) | |
109 | if (syndrome_codes[bit] == syndrome) | |
110 | break; | |
111 | ||
531e3e8b PK |
112 | mfsdram(DDR0_38, laddr); |
113 | mfsdram(DDR0_39, haddr); | |
4b3cc6ec | 114 | haddr &= 0x00000001; |
531e3e8b PK |
115 | mfsdram(DDR0_40, ldata); |
116 | mfsdram(DDR0_41, hdata); | |
4b3cc6ec LJ |
117 | if (int_status & DDR0_00_INT_STATUS_BIT3) |
118 | debug("Multiple correctable ECC events"); | |
119 | else | |
120 | debug("Single correctable ECC event"); | |
121 | ||
122 | debug(" detected\n 0x%01x%08x - 0x%08x%08x, bit - %d\n", | |
123 | haddr, laddr, hdata, ldata, bit); | |
531e3e8b | 124 | } |
4b3cc6ec LJ |
125 | if (int_status & (DDR0_00_INT_STATUS_BIT4 | DDR0_00_INT_STATUS_BIT5)) { |
126 | mfsdram(DDR0_23, value); | |
127 | syndrome = (value >> 8) & 0xff; | |
531e3e8b PK |
128 | mfsdram(DDR0_34, laddr); |
129 | mfsdram(DDR0_35, haddr); | |
4b3cc6ec | 130 | haddr &= 0x00000001; |
531e3e8b PK |
131 | mfsdram(DDR0_36, ldata); |
132 | mfsdram(DDR0_37, hdata); | |
4b3cc6ec LJ |
133 | if (int_status & DDR0_00_INT_STATUS_BIT5) |
134 | debug("Multiple uncorrectable ECC events"); | |
135 | else | |
136 | debug("Single uncorrectable ECC event"); | |
137 | ||
138 | debug(" detected\n 0x%01x%08x - 0x%08x%08x, " | |
139 | "syndrome - 0x%02x\n", | |
140 | haddr, laddr, hdata, ldata, syndrome); | |
531e3e8b | 141 | } |
4b3cc6ec LJ |
142 | if (int_status & DDR0_00_INT_STATUS_BIT6) |
143 | debug("DRAM initialization complete\n"); | |
144 | #endif /* defined(DEBUG) */ | |
531e3e8b | 145 | |
4b3cc6ec | 146 | return int_status; |
531e3e8b PK |
147 | } |
148 | ||
4b3cc6ec | 149 | static int test_ecc(uint32_t ecc_addr) |
531e3e8b | 150 | { |
4b3cc6ec LJ |
151 | uint32_t value; |
152 | volatile uint32_t *const ecc_mem = (volatile uint32_t *)ecc_addr; | |
a724a9b4 | 153 | int ret = 0; |
531e3e8b | 154 | |
531e3e8b PK |
155 | WATCHDOG_RESET(); |
156 | ||
4b3cc6ec LJ |
157 | debug("Entering test_ecc(0x%08x)\n", ecc_addr); |
158 | /* Set up correct ECC in memory */ | |
159 | disable_ecc(); | |
160 | clear_and_enable_ecc(); | |
a724a9b4 LJ |
161 | out_be32(ecc_mem, ECC_PATTERN); |
162 | out_be32(ecc_mem + 1, ECC_PATTERN); | |
28e94bb2 | 163 | ppcDcbf((u32)ecc_mem); |
4b3cc6ec LJ |
164 | |
165 | /* Verify no ECC error reading back */ | |
166 | value = in_be32(ecc_mem); | |
167 | disable_ecc(); | |
168 | if (ECC_PATTERN != value) { | |
169 | debug("Data read error (no-error case): " | |
170 | "expected 0x%08x, read 0x%08x\n", ECC_PATTERN, value); | |
171 | ret = 1; | |
172 | } | |
173 | value = get_ecc_status(); | |
174 | if (0x00000000 != value) { | |
175 | /* Expected no ECC status reported */ | |
176 | debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n", | |
177 | 0x00000000, value); | |
531e3e8b | 178 | ret = 1; |
a724a9b4 | 179 | } |
531e3e8b | 180 | |
4b3cc6ec | 181 | /* Test for correctable error by creating a one-bit error */ |
a724a9b4 | 182 | out_be32(ecc_mem, ECC_PATTERN_CORR); |
28e94bb2 | 183 | ppcDcbf((u32)ecc_mem); |
4b3cc6ec LJ |
184 | clear_and_enable_ecc(); |
185 | value = in_be32(ecc_mem); | |
186 | disable_ecc(); | |
187 | /* Test that the corrected data was read */ | |
188 | if (ECC_PATTERN != value) { | |
189 | debug("Data read error (correctable-error case): " | |
190 | "expected 0x%08x, read 0x%08x\n", ECC_PATTERN, value); | |
191 | ret = 1; | |
192 | } | |
193 | value = get_ecc_status(); | |
194 | if ((DDR0_00_INT_STATUS_BIT2 | DDR0_00_INT_STATUS_BIT7) != value) { | |
195 | /* Expected a single correctable error reported */ | |
196 | debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n", | |
197 | DDR0_00_INT_STATUS_BIT2, value); | |
531e3e8b | 198 | ret = 1; |
a724a9b4 | 199 | } |
531e3e8b | 200 | |
4b3cc6ec | 201 | /* Test for uncorrectable error by creating a two-bit error */ |
a724a9b4 | 202 | out_be32(ecc_mem, ECC_PATTERN_UNCORR); |
28e94bb2 | 203 | ppcDcbf((u32)ecc_mem); |
4b3cc6ec LJ |
204 | clear_and_enable_ecc(); |
205 | value = in_be32(ecc_mem); | |
206 | disable_ecc(); | |
207 | /* Test that the corrected data was read */ | |
208 | if (ECC_PATTERN_UNCORR != value) { | |
209 | debug("Data read error (uncorrectable-error case): " | |
210 | "expected 0x%08x, read 0x%08x\n", ECC_PATTERN_UNCORR, | |
211 | value); | |
212 | ret = 1; | |
213 | } | |
214 | value = get_ecc_status(); | |
215 | if ((DDR0_00_INT_STATUS_BIT4 | DDR0_00_INT_STATUS_BIT7) != value) { | |
216 | /* Expected a single uncorrectable error reported */ | |
217 | debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n", | |
218 | DDR0_00_INT_STATUS_BIT4, value); | |
531e3e8b | 219 | ret = 1; |
a724a9b4 | 220 | } |
4b3cc6ec LJ |
221 | |
222 | /* Remove error from SDRAM and enable ECC. */ | |
a724a9b4 | 223 | out_be32(ecc_mem, ECC_PATTERN); |
28e94bb2 | 224 | ppcDcbf((u32)ecc_mem); |
4b3cc6ec | 225 | clear_and_enable_ecc(); |
531e3e8b | 226 | |
531e3e8b PK |
227 | return ret; |
228 | } | |
229 | ||
4b3cc6ec | 230 | int ecc_post_test(int flags) |
531e3e8b PK |
231 | { |
232 | int ret = 0; | |
4b3cc6ec LJ |
233 | uint32_t value; |
234 | uint32_t iaddr; | |
531e3e8b | 235 | |
a724a9b4 LJ |
236 | mfsdram(DDR0_22, value); |
237 | if (0x3 != DDR0_22_CTRL_RAW_DECODE(value)) { | |
238 | debug("SDRAM ECC not enabled, skipping ECC POST.\n"); | |
239 | return 0; | |
240 | } | |
241 | ||
4b3cc6ec | 242 | /* Mask all interrupts. */ |
531e3e8b | 243 | mfsdram(DDR0_01, value); |
0d9cdeac | 244 | mtsdram(DDR0_01, (value & ~DDR0_01_INT_MASK_MASK) |
531e3e8b PK |
245 | | DDR0_01_INT_MASK_ALL_OFF); |
246 | ||
a724a9b4 | 247 | for (iaddr = ECC_START_ADDR; iaddr <= ECC_STOP_ADDR; iaddr += iaddr) { |
531e3e8b PK |
248 | ret = test_ecc(iaddr); |
249 | if (ret) | |
250 | break; | |
251 | } | |
ea9f6bce | 252 | /* |
4b3cc6ec LJ |
253 | * Clear possible errors resulting from ECC testing. (If not done, we |
254 | * we could get an interrupt later on when exceptions are enabled.) | |
ea9f6bce SR |
255 | */ |
256 | set_mcsr(get_mcsr()); | |
4b3cc6ec | 257 | debug("ecc_post_test() returning %d\n", ret); |
531e3e8b | 258 | return ret; |
531e3e8b | 259 | } |
6d0f6bcf | 260 | #endif /* CONFIG_POST & CONFIG_SYS_POST_ECC */ |
0a51e924 | 261 | #endif /* defined(CONFIG_440EPX) || defined(CONFIG_440GRX) */ |