diff options
-rw-r--r-- | arch/xtensa/Kconfig.debug | 11 | ||||
-rw-r--r-- | arch/xtensa/include/asm/regs.h | 4 | ||||
-rw-r--r-- | arch/xtensa/kernel/setup.c | 120 |
3 files changed, 135 insertions, 0 deletions
diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug index be5fb4c6243f..a34010e0e51c 100644 --- a/arch/xtensa/Kconfig.debug +++ b/arch/xtensa/Kconfig.debug | |||
@@ -13,4 +13,15 @@ config LD_NO_RELAX | |||
13 | Enabling this option improves the link time but increases the | 13 | Enabling this option improves the link time but increases the |
14 | code size, and possibly execution time. | 14 | code size, and possibly execution time. |
15 | 15 | ||
16 | config S32C1I_SELFTEST | ||
17 | bool "Perform S32C1I instruction self-test at boot" | ||
18 | default y | ||
19 | help | ||
20 | Enable this option to test S32C1I instruction behavior at boot. | ||
21 | Correct operation of this instruction requires some cooperation from hardware | ||
22 | external to the processor (such as bus bridge, bus fabric, or memory controller). | ||
23 | It is easy to make wrong hardware configuration, this test should catch it early. | ||
24 | |||
25 | Say 'N' on stable hardware. | ||
26 | |||
16 | endmenu | 27 | endmenu |
diff --git a/arch/xtensa/include/asm/regs.h b/arch/xtensa/include/asm/regs.h index 8a8aa61ccc8d..6aaf6d6a5fc4 100644 --- a/arch/xtensa/include/asm/regs.h +++ b/arch/xtensa/include/asm/regs.h | |||
@@ -52,6 +52,10 @@ | |||
52 | #define EXCCAUSE_SPECULATION 7 | 52 | #define EXCCAUSE_SPECULATION 7 |
53 | #define EXCCAUSE_PRIVILEGED 8 | 53 | #define EXCCAUSE_PRIVILEGED 8 |
54 | #define EXCCAUSE_UNALIGNED 9 | 54 | #define EXCCAUSE_UNALIGNED 9 |
55 | #define EXCCAUSE_INSTR_DATA_ERROR 12 | ||
56 | #define EXCCAUSE_LOAD_STORE_DATA_ERROR 13 | ||
57 | #define EXCCAUSE_INSTR_ADDR_ERROR 14 | ||
58 | #define EXCCAUSE_LOAD_STORE_ADDR_ERROR 15 | ||
55 | #define EXCCAUSE_ITLB_MISS 16 | 59 | #define EXCCAUSE_ITLB_MISS 16 |
56 | #define EXCCAUSE_ITLB_MULTIHIT 17 | 60 | #define EXCCAUSE_ITLB_MULTIHIT 17 |
57 | #define EXCCAUSE_ITLB_PRIVILEGE 18 | 61 | #define EXCCAUSE_ITLB_PRIVILEGE 18 |
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index e53a94b3edbc..45217617c60d 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <asm/page.h> | 42 | #include <asm/page.h> |
43 | #include <asm/setup.h> | 43 | #include <asm/setup.h> |
44 | #include <asm/param.h> | 44 | #include <asm/param.h> |
45 | #include <asm/traps.h> | ||
45 | 46 | ||
46 | #include <platform/hardware.h> | 47 | #include <platform/hardware.h> |
47 | 48 | ||
@@ -235,6 +236,123 @@ extern char _UserExceptionVector_text_end; | |||
235 | extern char _DoubleExceptionVector_literal_start; | 236 | extern char _DoubleExceptionVector_literal_start; |
236 | extern char _DoubleExceptionVector_text_end; | 237 | extern char _DoubleExceptionVector_text_end; |
237 | 238 | ||
239 | |||
240 | #ifdef CONFIG_S32C1I_SELFTEST | ||
241 | #if XCHAL_HAVE_S32C1I | ||
242 | |||
243 | static int __initdata rcw_word, rcw_probe_pc, rcw_exc; | ||
244 | |||
245 | /* | ||
246 | * Basic atomic compare-and-swap, that records PC of S32C1I for probing. | ||
247 | * | ||
248 | * If *v == cmp, set *v = set. Return previous *v. | ||
249 | */ | ||
250 | static inline int probed_compare_swap(int *v, int cmp, int set) | ||
251 | { | ||
252 | int tmp; | ||
253 | |||
254 | __asm__ __volatile__( | ||
255 | " movi %1, 1f\n" | ||
256 | " s32i %1, %4, 0\n" | ||
257 | " wsr %2, scompare1\n" | ||
258 | "1: s32c1i %0, %3, 0\n" | ||
259 | : "=a" (set), "=&a" (tmp) | ||
260 | : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set) | ||
261 | : "memory" | ||
262 | ); | ||
263 | return set; | ||
264 | } | ||
265 | |||
266 | /* Handle probed exception */ | ||
267 | |||
268 | void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause) | ||
269 | { | ||
270 | if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */ | ||
271 | regs->pc += 3; /* skip the s32c1i instruction */ | ||
272 | rcw_exc = exccause; | ||
273 | } else { | ||
274 | do_unhandled(regs, exccause); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | /* Simple test of S32C1I (soc bringup assist) */ | ||
279 | |||
280 | void __init check_s32c1i(void) | ||
281 | { | ||
282 | int n, cause1, cause2; | ||
283 | void *handbus, *handdata, *handaddr; /* temporarily saved handlers */ | ||
284 | |||
285 | rcw_probe_pc = 0; | ||
286 | handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, | ||
287 | do_probed_exception); | ||
288 | handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, | ||
289 | do_probed_exception); | ||
290 | handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, | ||
291 | do_probed_exception); | ||
292 | |||
293 | /* First try an S32C1I that does not store: */ | ||
294 | rcw_exc = 0; | ||
295 | rcw_word = 1; | ||
296 | n = probed_compare_swap(&rcw_word, 0, 2); | ||
297 | cause1 = rcw_exc; | ||
298 | |||
299 | /* took exception? */ | ||
300 | if (cause1 != 0) { | ||
301 | /* unclean exception? */ | ||
302 | if (n != 2 || rcw_word != 1) | ||
303 | panic("S32C1I exception error"); | ||
304 | } else if (rcw_word != 1 || n != 1) { | ||
305 | panic("S32C1I compare error"); | ||
306 | } | ||
307 | |||
308 | /* Then an S32C1I that stores: */ | ||
309 | rcw_exc = 0; | ||
310 | rcw_word = 0x1234567; | ||
311 | n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde); | ||
312 | cause2 = rcw_exc; | ||
313 | |||
314 | if (cause2 != 0) { | ||
315 | /* unclean exception? */ | ||
316 | if (n != 0xabcde || rcw_word != 0x1234567) | ||
317 | panic("S32C1I exception error (b)"); | ||
318 | } else if (rcw_word != 0xabcde || n != 0x1234567) { | ||
319 | panic("S32C1I store error"); | ||
320 | } | ||
321 | |||
322 | /* Verify consistency of exceptions: */ | ||
323 | if (cause1 || cause2) { | ||
324 | pr_warn("S32C1I took exception %d, %d\n", cause1, cause2); | ||
325 | /* If emulation of S32C1I upon bus error gets implemented, | ||
326 | we can get rid of this panic for single core (not SMP) */ | ||
327 | panic("S32C1I exceptions not currently supported"); | ||
328 | } | ||
329 | if (cause1 != cause2) | ||
330 | panic("inconsistent S32C1I exceptions"); | ||
331 | |||
332 | trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus); | ||
333 | trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata); | ||
334 | trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr); | ||
335 | } | ||
336 | |||
337 | #else /* XCHAL_HAVE_S32C1I */ | ||
338 | |||
339 | /* This condition should not occur with a commercially deployed processor. | ||
340 | Display reminder for early engr test or demo chips / FPGA bitstreams */ | ||
341 | void __init check_s32c1i(void) | ||
342 | { | ||
343 | pr_warn("Processor configuration lacks atomic compare-and-swap support!\n"); | ||
344 | } | ||
345 | |||
346 | #endif /* XCHAL_HAVE_S32C1I */ | ||
347 | #else /* CONFIG_S32C1I_SELFTEST */ | ||
348 | |||
349 | void __init check_s32c1i(void) | ||
350 | { | ||
351 | } | ||
352 | |||
353 | #endif /* CONFIG_S32C1I_SELFTEST */ | ||
354 | |||
355 | |||
238 | void __init setup_arch(char **cmdline_p) | 356 | void __init setup_arch(char **cmdline_p) |
239 | { | 357 | { |
240 | extern int mem_reserve(unsigned long, unsigned long, int); | 358 | extern int mem_reserve(unsigned long, unsigned long, int); |
@@ -244,6 +362,8 @@ void __init setup_arch(char **cmdline_p) | |||
244 | boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; | 362 | boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; |
245 | *cmdline_p = command_line; | 363 | *cmdline_p = command_line; |
246 | 364 | ||
365 | check_s32c1i(); | ||
366 | |||
247 | /* Reserve some memory regions */ | 367 | /* Reserve some memory regions */ |
248 | 368 | ||
249 | #ifdef CONFIG_BLK_DEV_INITRD | 369 | #ifdef CONFIG_BLK_DEV_INITRD |