13863c58cSMax Filippov /*
23863c58cSMax Filippov * S32C1I selftest.
33863c58cSMax Filippov *
43863c58cSMax Filippov * This file is subject to the terms and conditions of the GNU General Public
53863c58cSMax Filippov * License. See the file "COPYING" in the main directory of this archive
63863c58cSMax Filippov * for more details.
73863c58cSMax Filippov *
83863c58cSMax Filippov * Copyright (C) 2016 Cadence Design Systems Inc.
93863c58cSMax Filippov */
103863c58cSMax Filippov
113863c58cSMax Filippov #include <linux/init.h>
123863c58cSMax Filippov #include <linux/kernel.h>
133863c58cSMax Filippov
143863c58cSMax Filippov #include <asm/traps.h>
153863c58cSMax Filippov
163863c58cSMax Filippov #if XCHAL_HAVE_S32C1I
173863c58cSMax Filippov
183863c58cSMax Filippov static int __initdata rcw_word, rcw_probe_pc, rcw_exc;
193863c58cSMax Filippov
203863c58cSMax Filippov /*
213863c58cSMax Filippov * Basic atomic compare-and-swap, that records PC of S32C1I for probing.
223863c58cSMax Filippov *
233863c58cSMax Filippov * If *v == cmp, set *v = set. Return previous *v.
243863c58cSMax Filippov */
probed_compare_swap(int * v,int cmp,int set)253863c58cSMax Filippov static inline int probed_compare_swap(int *v, int cmp, int set)
263863c58cSMax Filippov {
273863c58cSMax Filippov int tmp;
283863c58cSMax Filippov
293863c58cSMax Filippov __asm__ __volatile__(
303863c58cSMax Filippov " movi %1, 1f\n"
313863c58cSMax Filippov " s32i %1, %4, 0\n"
323863c58cSMax Filippov " wsr %2, scompare1\n"
333863c58cSMax Filippov "1: s32c1i %0, %3, 0\n"
343863c58cSMax Filippov : "=a" (set), "=&a" (tmp)
353863c58cSMax Filippov : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set)
363863c58cSMax Filippov : "memory"
373863c58cSMax Filippov );
383863c58cSMax Filippov return set;
393863c58cSMax Filippov }
403863c58cSMax Filippov
413863c58cSMax Filippov /* Handle probed exception */
423863c58cSMax Filippov
do_probed_exception(struct pt_regs * regs)43*fc55402bSMax Filippov static void __init do_probed_exception(struct pt_regs *regs)
443863c58cSMax Filippov {
453863c58cSMax Filippov if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */
463863c58cSMax Filippov regs->pc += 3; /* skip the s32c1i instruction */
47*fc55402bSMax Filippov rcw_exc = regs->exccause;
483863c58cSMax Filippov } else {
49*fc55402bSMax Filippov do_unhandled(regs);
503863c58cSMax Filippov }
513863c58cSMax Filippov }
523863c58cSMax Filippov
533863c58cSMax Filippov /* Simple test of S32C1I (soc bringup assist) */
543863c58cSMax Filippov
check_s32c1i(void)553863c58cSMax Filippov static int __init check_s32c1i(void)
563863c58cSMax Filippov {
573863c58cSMax Filippov int n, cause1, cause2;
583863c58cSMax Filippov void *handbus, *handdata, *handaddr; /* temporarily saved handlers */
593863c58cSMax Filippov
603863c58cSMax Filippov rcw_probe_pc = 0;
613863c58cSMax Filippov handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR,
623863c58cSMax Filippov do_probed_exception);
633863c58cSMax Filippov handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR,
643863c58cSMax Filippov do_probed_exception);
653863c58cSMax Filippov handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR,
663863c58cSMax Filippov do_probed_exception);
673863c58cSMax Filippov
683863c58cSMax Filippov /* First try an S32C1I that does not store: */
693863c58cSMax Filippov rcw_exc = 0;
703863c58cSMax Filippov rcw_word = 1;
713863c58cSMax Filippov n = probed_compare_swap(&rcw_word, 0, 2);
723863c58cSMax Filippov cause1 = rcw_exc;
733863c58cSMax Filippov
743863c58cSMax Filippov /* took exception? */
753863c58cSMax Filippov if (cause1 != 0) {
763863c58cSMax Filippov /* unclean exception? */
773863c58cSMax Filippov if (n != 2 || rcw_word != 1)
783863c58cSMax Filippov panic("S32C1I exception error");
793863c58cSMax Filippov } else if (rcw_word != 1 || n != 1) {
803863c58cSMax Filippov panic("S32C1I compare error");
813863c58cSMax Filippov }
823863c58cSMax Filippov
833863c58cSMax Filippov /* Then an S32C1I that stores: */
843863c58cSMax Filippov rcw_exc = 0;
853863c58cSMax Filippov rcw_word = 0x1234567;
863863c58cSMax Filippov n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde);
873863c58cSMax Filippov cause2 = rcw_exc;
883863c58cSMax Filippov
893863c58cSMax Filippov if (cause2 != 0) {
903863c58cSMax Filippov /* unclean exception? */
913863c58cSMax Filippov if (n != 0xabcde || rcw_word != 0x1234567)
923863c58cSMax Filippov panic("S32C1I exception error (b)");
933863c58cSMax Filippov } else if (rcw_word != 0xabcde || n != 0x1234567) {
943863c58cSMax Filippov panic("S32C1I store error");
953863c58cSMax Filippov }
963863c58cSMax Filippov
973863c58cSMax Filippov /* Verify consistency of exceptions: */
983863c58cSMax Filippov if (cause1 || cause2) {
993863c58cSMax Filippov pr_warn("S32C1I took exception %d, %d\n", cause1, cause2);
1003863c58cSMax Filippov /* If emulation of S32C1I upon bus error gets implemented,
1013863c58cSMax Filippov * we can get rid of this panic for single core (not SMP)
1023863c58cSMax Filippov */
1033863c58cSMax Filippov panic("S32C1I exceptions not currently supported");
1043863c58cSMax Filippov }
1053863c58cSMax Filippov if (cause1 != cause2)
1063863c58cSMax Filippov panic("inconsistent S32C1I exceptions");
1073863c58cSMax Filippov
1083863c58cSMax Filippov trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus);
1093863c58cSMax Filippov trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata);
1103863c58cSMax Filippov trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr);
1113863c58cSMax Filippov return 0;
1123863c58cSMax Filippov }
1133863c58cSMax Filippov
1143863c58cSMax Filippov #else /* XCHAL_HAVE_S32C1I */
1153863c58cSMax Filippov
1163863c58cSMax Filippov /* This condition should not occur with a commercially deployed processor.
1173863c58cSMax Filippov * Display reminder for early engr test or demo chips / FPGA bitstreams
1183863c58cSMax Filippov */
check_s32c1i(void)1193863c58cSMax Filippov static int __init check_s32c1i(void)
1203863c58cSMax Filippov {
1213863c58cSMax Filippov pr_warn("Processor configuration lacks atomic compare-and-swap support!\n");
1223863c58cSMax Filippov return 0;
1233863c58cSMax Filippov }
1243863c58cSMax Filippov
1253863c58cSMax Filippov #endif /* XCHAL_HAVE_S32C1I */
1263863c58cSMax Filippov
1273863c58cSMax Filippov early_initcall(check_s32c1i);
128