]>
Commit | Line | Data |
---|---|---|
58eb869f SR |
1 | /* |
2 | * (C) Copyright 2010 | |
3 | * Stefan Roese, DENX Software Engineering, sr@denx.de. | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
58eb869f SR |
6 | */ |
7 | ||
8 | #include <common.h> | |
b36df561 | 9 | #include <asm/ppc4xx.h> |
58eb869f SR |
10 | #include <asm/processor.h> |
11 | #include <asm/io.h> | |
12 | #include <asm/cache.h> | |
13 | ||
14 | #if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) || \ | |
15 | defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) | |
16 | #if defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) | |
17 | ||
18 | #if defined(CONFIG_405EX) | |
19 | /* | |
20 | * Currently only 405EX uses 16bit data bus width as an alternative | |
21 | * option to 32bit data width (SDRAM0_MCOPT1_WDTH) | |
22 | */ | |
23 | #define SDRAM_DATA_ALT_WIDTH 2 | |
24 | #else | |
25 | #define SDRAM_DATA_ALT_WIDTH 8 | |
26 | #endif | |
27 | ||
28 | #if defined(CONFIG_SYS_OCM_BASE) | |
29 | #define CONFIG_FUNC_ISRAM_ADDR CONFIG_SYS_OCM_BASE | |
30 | #endif | |
31 | ||
32 | #if defined(CONFIG_SYS_ISRAM_BASE) | |
33 | #define CONFIG_FUNC_ISRAM_ADDR CONFIG_SYS_ISRAM_BASE | |
34 | #endif | |
35 | ||
36 | #if !defined(CONFIG_FUNC_ISRAM_ADDR) | |
37 | #error "No internal SRAM/OCM provided!" | |
38 | #endif | |
39 | ||
40 | #define force_inline inline __attribute__ ((always_inline)) | |
41 | ||
42 | static inline void machine_check_disable(void) | |
43 | { | |
44 | mtmsr(mfmsr() & ~MSR_ME); | |
45 | } | |
46 | ||
47 | static inline void machine_check_enable(void) | |
48 | { | |
49 | mtmsr(mfmsr() | MSR_ME); | |
50 | } | |
51 | ||
52 | /* | |
53 | * These helper functions need to be inlined, since they | |
54 | * are called from the functions running from internal SRAM. | |
55 | * SDRAM operation is forbidden at that time, so calling | |
56 | * functions in SDRAM has to be avoided. | |
57 | */ | |
58 | static force_inline void wait_ddr_idle(void) | |
59 | { | |
60 | u32 val; | |
61 | ||
62 | do { | |
63 | mfsdram(SDRAM_MCSTAT, val); | |
64 | } while ((val & SDRAM_MCSTAT_IDLE_MASK) == SDRAM_MCSTAT_IDLE_NOT); | |
65 | } | |
66 | ||
67 | static force_inline void recalibrate_ddr(void) | |
68 | { | |
69 | u32 val; | |
70 | ||
71 | /* | |
72 | * Rewrite RQDC & RFDC to calibrate again. If this is not | |
73 | * done, the SDRAM controller is working correctly after | |
74 | * changing the MCOPT1_MCHK bits. | |
75 | */ | |
76 | mfsdram(SDRAM_RQDC, val); | |
77 | mtsdram(SDRAM_RQDC, val); | |
78 | mfsdram(SDRAM_RFDC, val); | |
79 | mtsdram(SDRAM_RFDC, val); | |
80 | } | |
81 | ||
82 | static force_inline void set_mcopt1_mchk(u32 bits) | |
83 | { | |
84 | u32 val; | |
85 | ||
86 | wait_ddr_idle(); | |
87 | mfsdram(SDRAM_MCOPT1, val); | |
88 | mtsdram(SDRAM_MCOPT1, (val & ~SDRAM_MCOPT1_MCHK_MASK) | bits); | |
89 | recalibrate_ddr(); | |
90 | } | |
91 | ||
92 | /* | |
93 | * The next 2 functions are copied to internal SRAM/OCM and run | |
94 | * there. No function calls allowed here. No SDRAM acitivity should | |
95 | * be done here. | |
96 | */ | |
97 | static void inject_ecc_error(void *ptr, int par) | |
98 | { | |
58eb869f SR |
99 | /* |
100 | * Taken from PPC460EX/EXr/GT users manual (Rev 1.21) | |
101 | * 22.2.17.13 ECC Diagnostics | |
102 | * | |
103 | * Items 1 ... 5 are already done by now, running from RAM | |
104 | * with ECC enabled | |
105 | */ | |
106 | ||
107 | out_be32(ptr, 0x00000000); | |
a6370da1 | 108 | in_be32(ptr); |
58eb869f SR |
109 | |
110 | /* 6. Set memory controller to no error checking */ | |
111 | set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_NON); | |
112 | ||
113 | /* 7. Modify one or two bits for error simulation */ | |
114 | if (par == 1) | |
115 | out_be32(ptr, in_be32(ptr) ^ 0x00000001); | |
116 | else | |
117 | out_be32(ptr, in_be32(ptr) ^ 0x00000003); | |
118 | ||
119 | /* 8. Wait for SDRAM idle */ | |
a6370da1 | 120 | in_be32(ptr); |
58eb869f SR |
121 | set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_CHK_REP); |
122 | ||
123 | /* Wait for SDRAM idle */ | |
124 | wait_ddr_idle(); | |
125 | ||
126 | /* Continue with 9. in calling function... */ | |
127 | } | |
128 | ||
129 | static void rewrite_ecc_parity(void *ptr, int par) | |
130 | { | |
131 | u32 current_address = (u32)ptr; | |
132 | u32 end_address; | |
133 | u32 address_increment; | |
134 | u32 mcopt1; | |
58eb869f SR |
135 | |
136 | /* | |
137 | * Fill ECC parity byte again. Otherwise further accesses to | |
138 | * the failure address will result in exceptions. | |
139 | */ | |
140 | ||
141 | /* Wait for SDRAM idle */ | |
a6370da1 | 142 | in_be32(0x00000000); |
58eb869f SR |
143 | set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_GEN); |
144 | ||
145 | /* ECC bit set method for non-cached memory */ | |
146 | mfsdram(SDRAM_MCOPT1, mcopt1); | |
147 | if ((mcopt1 & SDRAM_MCOPT1_DMWD_MASK) == SDRAM_MCOPT1_DMWD_32) | |
148 | address_increment = 4; | |
149 | else | |
150 | address_increment = SDRAM_DATA_ALT_WIDTH; | |
151 | end_address = current_address + CONFIG_SYS_CACHELINE_SIZE; | |
152 | ||
153 | while (current_address < end_address) { | |
154 | *((unsigned long *)current_address) = 0; | |
155 | current_address += address_increment; | |
156 | } | |
157 | ||
158 | set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_CHK_REP); | |
159 | ||
160 | /* Wait for SDRAM idle */ | |
161 | wait_ddr_idle(); | |
162 | } | |
163 | ||
164 | static int do_ecctest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
165 | { | |
166 | u32 old_val; | |
167 | u32 val; | |
168 | u32 *ptr; | |
169 | void (*sram_func)(u32 *, int); | |
170 | int error; | |
171 | ||
172 | if (argc < 3) { | |
e03dabe8 | 173 | return cmd_usage(cmdtp); |
58eb869f SR |
174 | } |
175 | ||
176 | ptr = (u32 *)simple_strtoul(argv[1], NULL, 16); | |
177 | error = simple_strtoul(argv[2], NULL, 16); | |
178 | if ((error < 1) || (error > 2)) { | |
e03dabe8 | 179 | return cmd_usage(cmdtp); |
58eb869f SR |
180 | } |
181 | ||
182 | printf("Using address %p for %d bit ECC error injection\n", | |
183 | ptr, error); | |
184 | ||
185 | /* | |
186 | * Save value to restore it later on | |
187 | */ | |
188 | old_val = in_be32(ptr); | |
189 | ||
190 | /* | |
191 | * Copy ECC injection function into internal SRAM/OCM | |
192 | */ | |
193 | sram_func = (void *)CONFIG_FUNC_ISRAM_ADDR; | |
194 | memcpy((void *)CONFIG_FUNC_ISRAM_ADDR, inject_ecc_error, 0x10000); | |
195 | ||
196 | /* | |
197 | * Disable interrupts and exceptions before calling this | |
198 | * function in internal SRAM/OCM | |
199 | */ | |
200 | disable_interrupts(); | |
201 | machine_check_disable(); | |
202 | eieio(); | |
203 | ||
204 | /* | |
205 | * Jump to ECC simulation function in internal SRAM/OCM | |
206 | */ | |
207 | (*sram_func)(ptr, error); | |
208 | ||
209 | /* 10. Read the corresponding address */ | |
210 | val = in_be32(ptr); | |
211 | ||
212 | /* | |
213 | * Read and print ECC status register/info: | |
214 | * The faulting address is only known upon uncorrectable ECC | |
215 | * errors. | |
216 | */ | |
217 | mfsdram(SDRAM_ECCES, val); | |
218 | if (val & SDRAM_ECCES_CE) | |
219 | printf("ECC: Correctable error\n"); | |
220 | if (val & SDRAM_ECCES_UE) { | |
221 | printf("ECC: Uncorrectable error at 0x%02x%08x\n", | |
222 | mfdcr(SDRAM_ERRADDULL), mfdcr(SDRAM_ERRADDLLL)); | |
223 | } | |
224 | ||
225 | /* | |
226 | * Clear pending interrupts/exceptions | |
227 | */ | |
228 | mtsdram(SDRAM_ECCES, 0xffffffff); | |
229 | mtdcr(SDRAM_ERRSTATLL, 0xff000000); | |
230 | set_mcsr(get_mcsr()); | |
231 | ||
232 | /* Now enable interrupts and exceptions again */ | |
233 | eieio(); | |
234 | machine_check_enable(); | |
235 | enable_interrupts(); | |
236 | ||
237 | /* | |
238 | * The ECC parity byte need to be re-written for the | |
239 | * corresponding address. Otherwise future accesses to it | |
240 | * will result in exceptions. | |
241 | * | |
242 | * Jump to ECC parity generation function | |
243 | */ | |
244 | memcpy((void *)CONFIG_FUNC_ISRAM_ADDR, rewrite_ecc_parity, 0x10000); | |
245 | (*sram_func)(ptr, 0); | |
246 | ||
247 | /* | |
248 | * Restore value in corresponding address | |
249 | */ | |
250 | out_be32(ptr, old_val); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | U_BOOT_CMD( | |
256 | ecctest, 3, 0, do_ecctest, | |
257 | "Test ECC by single and double error bit injection", | |
258 | "address 1/2" | |
259 | ); | |
260 | ||
261 | #endif /* defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) */ | |
262 | #endif /* defined(CONFIG_SDRAM_PPC4xx_IBM_DDR)... */ |