^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * S32C1I selftest.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * This file is subject to the terms and conditions of the GNU General Public
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * License. See the file "COPYING" in the main directory of this archive
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * for more details.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (C) 2016 Cadence Design Systems Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <asm/traps.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #if XCHAL_HAVE_S32C1I
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) static int __initdata rcw_word, rcw_probe_pc, rcw_exc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * Basic atomic compare-and-swap, that records PC of S32C1I for probing.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * If *v == cmp, set *v = set. Return previous *v.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static inline int probed_compare_swap(int *v, int cmp, int set)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) int tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) __asm__ __volatile__(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) " movi %1, 1f\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) " s32i %1, %4, 0\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) " wsr %2, scompare1\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) "1: s32c1i %0, %3, 0\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) : "=a" (set), "=&a" (tmp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) : "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) : "memory"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) );
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) return set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) /* Handle probed exception */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) static void __init do_probed_exception(struct pt_regs *regs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) unsigned long exccause)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) regs->pc += 3; /* skip the s32c1i instruction */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) rcw_exc = exccause;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) do_unhandled(regs, exccause);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) /* Simple test of S32C1I (soc bringup assist) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) static int __init check_s32c1i(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) int n, cause1, cause2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) void *handbus, *handdata, *handaddr; /* temporarily saved handlers */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) rcw_probe_pc = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) do_probed_exception);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) do_probed_exception);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) do_probed_exception);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) /* First try an S32C1I that does not store: */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) rcw_exc = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) rcw_word = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) n = probed_compare_swap(&rcw_word, 0, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) cause1 = rcw_exc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) /* took exception? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) if (cause1 != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /* unclean exception? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (n != 2 || rcw_word != 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) panic("S32C1I exception error");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) } else if (rcw_word != 1 || n != 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) panic("S32C1I compare error");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) /* Then an S32C1I that stores: */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) rcw_exc = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) rcw_word = 0x1234567;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) cause2 = rcw_exc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if (cause2 != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) /* unclean exception? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) if (n != 0xabcde || rcw_word != 0x1234567)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) panic("S32C1I exception error (b)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) } else if (rcw_word != 0xabcde || n != 0x1234567) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) panic("S32C1I store error");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) /* Verify consistency of exceptions: */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) if (cause1 || cause2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) pr_warn("S32C1I took exception %d, %d\n", cause1, cause2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) /* If emulation of S32C1I upon bus error gets implemented,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) * we can get rid of this panic for single core (not SMP)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) panic("S32C1I exceptions not currently supported");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) if (cause1 != cause2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) panic("inconsistent S32C1I exceptions");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) #else /* XCHAL_HAVE_S32C1I */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) /* This condition should not occur with a commercially deployed processor.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) * Display reminder for early engr test or demo chips / FPGA bitstreams
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) static int __init check_s32c1i(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) pr_warn("Processor configuration lacks atomic compare-and-swap support!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) #endif /* XCHAL_HAVE_S32C1I */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) early_initcall(check_s32c1i);