diff options
| author | Max Filippov <jcmvbkbc@gmail.com> | 2016-11-16 18:02:29 -0500 |
|---|---|---|
| committer | Max Filippov <jcmvbkbc@gmail.com> | 2016-11-16 18:08:28 -0500 |
| commit | 3863c58cc742df9e5f9ef0bde7dc135fdc547c19 (patch) | |
| tree | 8ac44acbbfcaa5d313f746f1367a2bba98e0f8f2 | |
| parent | 03eae3aca94c1e598be3ff40af492e1ca299577b (diff) | |
xtensa: move S32C1I self-test to a separate file
The test is not called from any of the setup functions, so there's no
reason keeping it in the setup.c. Move it to s32c1i_selftest.c and drop
related #include directives.
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
| -rw-r--r-- | arch/xtensa/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/xtensa/kernel/s32c1i_selftest.c | 128 | ||||
| -rw-r--r-- | arch/xtensa/kernel/setup.c | 115 |
3 files changed, 129 insertions, 115 deletions
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index c31f5d5afc7d..264fb89c444e 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile | |||
| @@ -14,6 +14,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += mcount.o | |||
| 14 | obj-$(CONFIG_SMP) += smp.o mxhead.o | 14 | obj-$(CONFIG_SMP) += smp.o mxhead.o |
| 15 | obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o | 15 | obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o |
| 16 | obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o | 16 | obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o |
| 17 | obj-$(CONFIG_S32C1I_SELFTEST) += s32c1i_selftest.o | ||
| 17 | 18 | ||
| 18 | AFLAGS_head.o += -mtext-section-literals | 19 | AFLAGS_head.o += -mtext-section-literals |
| 19 | AFLAGS_mxhead.o += -mtext-section-literals | 20 | AFLAGS_mxhead.o += -mtext-section-literals |
diff --git a/arch/xtensa/kernel/s32c1i_selftest.c b/arch/xtensa/kernel/s32c1i_selftest.c new file mode 100644 index 000000000000..07e56e3a9a8b --- /dev/null +++ b/arch/xtensa/kernel/s32c1i_selftest.c | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | /* | ||
| 2 | * S32C1I selftest. | ||
| 3 | * | ||
| 4 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 5 | * License. See the file "COPYING" in the main directory of this archive | ||
| 6 | * for more details. | ||
| 7 | * | ||
| 8 | * Copyright (C) 2016 Cadence Design Systems Inc. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/kernel.h> | ||
| 13 | |||
| 14 | #include <asm/traps.h> | ||
| 15 | |||
| 16 | #if XCHAL_HAVE_S32C1I | ||
| 17 | |||
| 18 | static int __initdata rcw_word, rcw_probe_pc, rcw_exc; | ||
| 19 | |||
| 20 | /* | ||
| 21 | * Basic atomic compare-and-swap, that records PC of S32C1I for probing. | ||
| 22 | * | ||
| 23 | * If *v == cmp, set *v = set. Return previous *v. | ||
| 24 | */ | ||
| 25 | static inline int probed_compare_swap(int *v, int cmp, int set) | ||
| 26 | { | ||
| 27 | int tmp; | ||
| 28 | |||
| 29 | __asm__ __volatile__( | ||
| 30 | " movi %1, 1f\n" | ||
| 31 | " s32i %1, %4, 0\n" | ||
| 32 | " wsr %2, scompare1\n" | ||
| 33 | "1: s32c1i %0, %3, 0\n" | ||
| 34 | : "=a" (set), "=&a" (tmp) | ||
| 35 | : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set) | ||
| 36 | : "memory" | ||
| 37 | ); | ||
| 38 | return set; | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Handle probed exception */ | ||
| 42 | |||
| 43 | static void __init do_probed_exception(struct pt_regs *regs, | ||
| 44 | unsigned long exccause) | ||
| 45 | { | ||
| 46 | if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */ | ||
| 47 | regs->pc += 3; /* skip the s32c1i instruction */ | ||
| 48 | rcw_exc = exccause; | ||
| 49 | } else { | ||
| 50 | do_unhandled(regs, exccause); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Simple test of S32C1I (soc bringup assist) */ | ||
| 55 | |||
| 56 | static int __init check_s32c1i(void) | ||
| 57 | { | ||
| 58 | int n, cause1, cause2; | ||
| 59 | void *handbus, *handdata, *handaddr; /* temporarily saved handlers */ | ||
| 60 | |||
| 61 | rcw_probe_pc = 0; | ||
| 62 | handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, | ||
| 63 | do_probed_exception); | ||
| 64 | handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, | ||
| 65 | do_probed_exception); | ||
| 66 | handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, | ||
| 67 | do_probed_exception); | ||
| 68 | |||
| 69 | /* First try an S32C1I that does not store: */ | ||
| 70 | rcw_exc = 0; | ||
| 71 | rcw_word = 1; | ||
| 72 | n = probed_compare_swap(&rcw_word, 0, 2); | ||
| 73 | cause1 = rcw_exc; | ||
| 74 | |||
| 75 | /* took exception? */ | ||
| 76 | if (cause1 != 0) { | ||
| 77 | /* unclean exception? */ | ||
| 78 | if (n != 2 || rcw_word != 1) | ||
| 79 | panic("S32C1I exception error"); | ||
| 80 | } else if (rcw_word != 1 || n != 1) { | ||
| 81 | panic("S32C1I compare error"); | ||
| 82 | } | ||
| 83 | |||
| 84 | /* Then an S32C1I that stores: */ | ||
| 85 | rcw_exc = 0; | ||
| 86 | rcw_word = 0x1234567; | ||
| 87 | n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde); | ||
| 88 | cause2 = rcw_exc; | ||
| 89 | |||
| 90 | if (cause2 != 0) { | ||
| 91 | /* unclean exception? */ | ||
| 92 | if (n != 0xabcde || rcw_word != 0x1234567) | ||
| 93 | panic("S32C1I exception error (b)"); | ||
| 94 | } else if (rcw_word != 0xabcde || n != 0x1234567) { | ||
| 95 | panic("S32C1I store error"); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Verify consistency of exceptions: */ | ||
| 99 | if (cause1 || cause2) { | ||
| 100 | pr_warn("S32C1I took exception %d, %d\n", cause1, cause2); | ||
| 101 | /* If emulation of S32C1I upon bus error gets implemented, | ||
| 102 | * we can get rid of this panic for single core (not SMP) | ||
| 103 | */ | ||
| 104 | panic("S32C1I exceptions not currently supported"); | ||
| 105 | } | ||
| 106 | if (cause1 != cause2) | ||
| 107 | panic("inconsistent S32C1I exceptions"); | ||
| 108 | |||
| 109 | trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus); | ||
| 110 | trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata); | ||
| 111 | trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr); | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | #else /* XCHAL_HAVE_S32C1I */ | ||
| 116 | |||
| 117 | /* This condition should not occur with a commercially deployed processor. | ||
| 118 | * Display reminder for early engr test or demo chips / FPGA bitstreams | ||
| 119 | */ | ||
| 120 | static int __init check_s32c1i(void) | ||
| 121 | { | ||
| 122 | pr_warn("Processor configuration lacks atomic compare-and-swap support!\n"); | ||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | |||
| 126 | #endif /* XCHAL_HAVE_S32C1I */ | ||
| 127 | |||
| 128 | early_initcall(check_s32c1i); | ||
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 80ed169dce07..848e8568fb3c 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c | |||
| @@ -44,7 +44,6 @@ | |||
| 44 | #include <asm/page.h> | 44 | #include <asm/page.h> |
| 45 | #include <asm/setup.h> | 45 | #include <asm/setup.h> |
| 46 | #include <asm/param.h> | 46 | #include <asm/param.h> |
| 47 | #include <asm/traps.h> | ||
| 48 | #include <asm/smp.h> | 47 | #include <asm/smp.h> |
| 49 | #include <asm/sysmem.h> | 48 | #include <asm/sysmem.h> |
| 50 | 49 | ||
| @@ -311,120 +310,6 @@ extern char _SecondaryResetVector_text_start; | |||
| 311 | extern char _SecondaryResetVector_text_end; | 310 | extern char _SecondaryResetVector_text_end; |
| 312 | #endif | 311 | #endif |
| 313 | 312 | ||
| 314 | |||
| 315 | #ifdef CONFIG_S32C1I_SELFTEST | ||
| 316 | #if XCHAL_HAVE_S32C1I | ||
| 317 | |||
| 318 | static int __initdata rcw_word, rcw_probe_pc, rcw_exc; | ||
| 319 | |||
| 320 | /* | ||
| 321 | * Basic atomic compare-and-swap, that records PC of S32C1I for probing. | ||
| 322 | * | ||
| 323 | * If *v == cmp, set *v = set. Return previous *v. | ||
| 324 | */ | ||
| 325 | static inline int probed_compare_swap(int *v, int cmp, int set) | ||
| 326 | { | ||
| 327 | int tmp; | ||
| 328 | |||
| 329 | __asm__ __volatile__( | ||
| 330 | " movi %1, 1f\n" | ||
| 331 | " s32i %1, %4, 0\n" | ||
| 332 | " wsr %2, scompare1\n" | ||
| 333 | "1: s32c1i %0, %3, 0\n" | ||
| 334 | : "=a" (set), "=&a" (tmp) | ||
| 335 | : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set) | ||
| 336 | : "memory" | ||
| 337 | ); | ||
| 338 | return set; | ||
| 339 | } | ||
| 340 | |||
| 341 | /* Handle probed exception */ | ||
| 342 | |||
| 343 | static void __init do_probed_exception(struct pt_regs *regs, | ||
| 344 | unsigned long exccause) | ||
| 345 | { | ||
| 346 | if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */ | ||
| 347 | regs->pc += 3; /* skip the s32c1i instruction */ | ||
| 348 | rcw_exc = exccause; | ||
| 349 | } else { | ||
| 350 | do_unhandled(regs, exccause); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | /* Simple test of S32C1I (soc bringup assist) */ | ||
| 355 | |||
| 356 | static int __init check_s32c1i(void) | ||
| 357 | { | ||
| 358 | int n, cause1, cause2; | ||
| 359 | void *handbus, *handdata, *handaddr; /* temporarily saved handlers */ | ||
| 360 | |||
| 361 | rcw_probe_pc = 0; | ||
| 362 | handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, | ||
| 363 | do_probed_exception); | ||
| 364 | handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, | ||
| 365 | do_probed_exception); | ||
| 366 | handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, | ||
| 367 | do_probed_exception); | ||
| 368 | |||
| 369 | /* First try an S32C1I that does not store: */ | ||
| 370 | rcw_exc = 0; | ||
| 371 | rcw_word = 1; | ||
| 372 | n = probed_compare_swap(&rcw_word, 0, 2); | ||
| 373 | cause1 = rcw_exc; | ||
| 374 | |||
| 375 | /* took exception? */ | ||
| 376 | if (cause1 != 0) { | ||
| 377 | /* unclean exception? */ | ||
| 378 | if (n != 2 || rcw_word != 1) | ||
| 379 | panic("S32C1I exception error"); | ||
| 380 | } else if (rcw_word != 1 || n != 1) { | ||
| 381 | panic("S32C1I compare error"); | ||
| 382 | } | ||
| 383 | |||
| 384 | /* Then an S32C1I that stores: */ | ||
| 385 | rcw_exc = 0; | ||
| 386 | rcw_word = 0x1234567; | ||
| 387 | n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde); | ||
| 388 | cause2 = rcw_exc; | ||
| 389 | |||
| 390 | if (cause2 != 0) { | ||
| 391 | /* unclean exception? */ | ||
| 392 | if (n != 0xabcde || rcw_word != 0x1234567) | ||
| 393 | panic("S32C1I exception error (b)"); | ||
| 394 | } else if (rcw_word != 0xabcde || n != 0x1234567) { | ||
| 395 | panic("S32C1I store error"); | ||
| 396 | } | ||
| 397 | |||
| 398 | /* Verify consistency of exceptions: */ | ||
| 399 | if (cause1 || cause2) { | ||
| 400 | pr_warn("S32C1I took exception %d, %d\n", cause1, cause2); | ||
| 401 | /* If emulation of S32C1I upon bus error gets implemented, | ||
| 402 | we can get rid of this panic for single core (not SMP) */ | ||
| 403 | panic("S32C1I exceptions not currently supported"); | ||
| 404 | } | ||
| 405 | if (cause1 != cause2) | ||
| 406 | panic("inconsistent S32C1I exceptions"); | ||
| 407 | |||
| 408 | trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus); | ||
| 409 | trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata); | ||
| 410 | trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr); | ||
| 411 | return 0; | ||
| 412 | } | ||
| 413 | |||
| 414 | #else /* XCHAL_HAVE_S32C1I */ | ||
| 415 | |||
| 416 | /* This condition should not occur with a commercially deployed processor. | ||
| 417 | Display reminder for early engr test or demo chips / FPGA bitstreams */ | ||
| 418 | static int __init check_s32c1i(void) | ||
| 419 | { | ||
| 420 | pr_warn("Processor configuration lacks atomic compare-and-swap support!\n"); | ||
| 421 | return 0; | ||
| 422 | } | ||
| 423 | |||
| 424 | #endif /* XCHAL_HAVE_S32C1I */ | ||
| 425 | early_initcall(check_s32c1i); | ||
| 426 | #endif /* CONFIG_S32C1I_SELFTEST */ | ||
| 427 | |||
| 428 | static inline int mem_reserve(unsigned long start, unsigned long end) | 313 | static inline int mem_reserve(unsigned long start, unsigned long end) |
| 429 | { | 314 | { |
| 430 | return memblock_reserve(start, end - start); | 315 | return memblock_reserve(start, end - start); |
