aboutsummaryrefslogtreecommitdiffstats
path: root/arch/xtensa
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2012-11-28 02:33:02 -0500
committerChris Zankel <chris@zankel.net>2012-12-19 00:10:22 -0500
commit00273125c39be9cbf619aef90147354a9ed8c385 (patch)
tree331f33a0a31f9306f4612ecc25c4c9a476840966 /arch/xtensa
parent28570e8dac5c86ab10ce2a7e9c02d3aaece63760 (diff)
xtensa: add s32c1i sanity check
Add a brief sanity test of S32C1I functionality. This instruction is needed by the kernel and userland as part of the base ABI (including GCC atomic builtins, certain threading packages, future atomic support in the C++ standard, etc). However, correct operation of this instruction requires some cooperation by hardware external to the processor (such as bus bridge, bus fabric, or memory controller). Minimally exercising this mechanism and reporting explicit status early in the boot process is helpful to chip vendors using the Linux kernel as a benchmark of correctness of hardware. As it turns out, S32C1I is not exercised by the kernel and by uClibc based userland as of early June 2008. This is expected to change soon as both incorporate more recent open source developments. Signed-off-by: Marc Gauthier <marc@tensilica.com> Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa')
-rw-r--r--arch/xtensa/Kconfig.debug11
-rw-r--r--arch/xtensa/include/asm/regs.h4
-rw-r--r--arch/xtensa/kernel/setup.c120
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
16config 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
16endmenu 27endmenu
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;
235extern char _DoubleExceptionVector_literal_start; 236extern char _DoubleExceptionVector_literal_start;
236extern char _DoubleExceptionVector_text_end; 237extern char _DoubleExceptionVector_text_end;
237 238
239
240#ifdef CONFIG_S32C1I_SELFTEST
241#if XCHAL_HAVE_S32C1I
242
243static 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 */
250static 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
268void __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
280void __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 */
341void __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
349void __init check_s32c1i(void)
350{
351}
352
353#endif /* CONFIG_S32C1I_SELFTEST */
354
355
238void __init setup_arch(char **cmdline_p) 356void __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