xref: /OK3568_Linux_fs/kernel/arch/mips/mm/cex-sb1.S (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun/* SPDX-License-Identifier: GPL-2.0-or-later */
2*4882a593Smuzhiyun/*
3*4882a593Smuzhiyun * Copyright (C) 2001,2002,2003 Broadcom Corporation
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun#include <asm/asm.h>
7*4882a593Smuzhiyun#include <asm/regdef.h>
8*4882a593Smuzhiyun#include <asm/mipsregs.h>
9*4882a593Smuzhiyun#include <asm/stackframe.h>
10*4882a593Smuzhiyun#include <asm/cacheops.h>
11*4882a593Smuzhiyun#include <asm/sibyte/board.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun#define C0_ERRCTL     $26	      /* CP0: Error info */
14*4882a593Smuzhiyun#define C0_CERR_I     $27	      /* CP0: Icache error */
15*4882a593Smuzhiyun#define C0_CERR_D     $27,1	      /* CP0: Dcache error */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun	/*
18*4882a593Smuzhiyun	 * Based on SiByte sample software cache-err/cerr.S
19*4882a593Smuzhiyun	 * CVS revision 1.8.  Only the 'unrecoverable' case
20*4882a593Smuzhiyun	 * is changed.
21*4882a593Smuzhiyun	 */
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun	.set	mips64
24*4882a593Smuzhiyun	.set	noreorder
25*4882a593Smuzhiyun	.set	noat
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun	/*
28*4882a593Smuzhiyun	 * sb1_cerr_vec: code to be copied to the Cache Error
29*4882a593Smuzhiyun	 * Exception vector.  The code must be pushed out to memory
30*4882a593Smuzhiyun	 * (either by copying to Kseg0 and Kseg1 both, or by flushing
31*4882a593Smuzhiyun	 * the L1 and L2) since it is fetched as 0xa0000100.
32*4882a593Smuzhiyun	 *
33*4882a593Smuzhiyun	 * NOTE: Be sure this handler is at most 28 instructions long
34*4882a593Smuzhiyun	 * since the final 16 bytes of the exception vector memory
35*4882a593Smuzhiyun	 * (0x170-0x17f) are used to preserve k0, k1, and ra.
36*4882a593Smuzhiyun	 */
37*4882a593Smuzhiyun
38*4882a593SmuzhiyunLEAF(except_vec2_sb1)
39*4882a593Smuzhiyun	/*
40*4882a593Smuzhiyun	 * If this error is recoverable, we need to exit the handler
41*4882a593Smuzhiyun	 * without having dirtied any registers.  To do this,
42*4882a593Smuzhiyun	 * save/restore k0 and k1 from low memory (Useg is direct
43*4882a593Smuzhiyun	 * mapped while ERL=1). Note that we can't save to a
44*4882a593Smuzhiyun	 * CPU-specific location without ruining a register in the
45*4882a593Smuzhiyun	 * process.  This means we are vulnerable to data corruption
46*4882a593Smuzhiyun	 * whenever the handler is reentered by a second CPU.
47*4882a593Smuzhiyun	 */
48*4882a593Smuzhiyun	sd	k0,0x170($0)
49*4882a593Smuzhiyun	sd	k1,0x178($0)
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun#ifdef CONFIG_SB1_CEX_ALWAYS_FATAL
52*4882a593Smuzhiyun	j	handle_vec2_sb1
53*4882a593Smuzhiyun	 nop
54*4882a593Smuzhiyun#else
55*4882a593Smuzhiyun	/*
56*4882a593Smuzhiyun	 * M_ERRCTL_RECOVERABLE is bit 31, which makes it easy to tell
57*4882a593Smuzhiyun	 * if we can fast-path out of here for a h/w-recovered error.
58*4882a593Smuzhiyun	 */
59*4882a593Smuzhiyun	mfc0	k1,C0_ERRCTL
60*4882a593Smuzhiyun	bgtz	k1,attempt_recovery
61*4882a593Smuzhiyun	 sll	k0,k1,1
62*4882a593Smuzhiyun
63*4882a593Smuzhiyunrecovered_dcache:
64*4882a593Smuzhiyun	/*
65*4882a593Smuzhiyun	 * Unlock CacheErr-D (which in turn unlocks CacheErr-DPA).
66*4882a593Smuzhiyun	 * Ought to log the occurrence of this recovered dcache error.
67*4882a593Smuzhiyun	 */
68*4882a593Smuzhiyun	b	recovered
69*4882a593Smuzhiyun	 mtc0	$0,C0_CERR_D
70*4882a593Smuzhiyun
71*4882a593Smuzhiyunattempt_recovery:
72*4882a593Smuzhiyun	/*
73*4882a593Smuzhiyun	 * k0 has C0_ERRCTL << 1, which puts 'DC' at bit 31.  Any
74*4882a593Smuzhiyun	 * Dcache errors we can recover from will take more extensive
75*4882a593Smuzhiyun	 * processing.	For now, they are considered "unrecoverable".
76*4882a593Smuzhiyun	 * Note that 'DC' becoming set (outside of ERL mode) will
77*4882a593Smuzhiyun	 * cause 'IC' to clear; so if there's an Icache error, we'll
78*4882a593Smuzhiyun	 * only find out about it if we recover from this error and
79*4882a593Smuzhiyun	 * continue executing.
80*4882a593Smuzhiyun	 */
81*4882a593Smuzhiyun	bltz	k0,unrecoverable
82*4882a593Smuzhiyun	 sll	k0,1
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun	/*
85*4882a593Smuzhiyun	 * k0 has C0_ERRCTL << 2, which puts 'IC' at bit 31.  If an
86*4882a593Smuzhiyun	 * Icache error isn't indicated, I'm not sure why we got here.
87*4882a593Smuzhiyun	 * Consider that case "unrecoverable" for now.
88*4882a593Smuzhiyun	 */
89*4882a593Smuzhiyun	bgez	k0,unrecoverable
90*4882a593Smuzhiyun
91*4882a593Smuzhiyunattempt_icache_recovery:
92*4882a593Smuzhiyun	/*
93*4882a593Smuzhiyun	 * External icache errors are due to uncorrectable ECC errors
94*4882a593Smuzhiyun	 * in the L2 cache or Memory Controller and cannot be
95*4882a593Smuzhiyun	 * recovered here.
96*4882a593Smuzhiyun	 */
97*4882a593Smuzhiyun	 mfc0	k0,C0_CERR_I		/* delay slot */
98*4882a593Smuzhiyun	li	k1,1 << 26		/* ICACHE_EXTERNAL */
99*4882a593Smuzhiyun	and	k1,k0
100*4882a593Smuzhiyun	bnez	k1,unrecoverable
101*4882a593Smuzhiyun	 andi	k0,0x1fe0
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun	/*
104*4882a593Smuzhiyun	 * Since the error is internal, the 'IDX' field from
105*4882a593Smuzhiyun	 * CacheErr-I is valid and we can just invalidate all blocks
106*4882a593Smuzhiyun	 * in that set.
107*4882a593Smuzhiyun	 */
108*4882a593Smuzhiyun	cache	Index_Invalidate_I,(0<<13)(k0)
109*4882a593Smuzhiyun	cache	Index_Invalidate_I,(1<<13)(k0)
110*4882a593Smuzhiyun	cache	Index_Invalidate_I,(2<<13)(k0)
111*4882a593Smuzhiyun	cache	Index_Invalidate_I,(3<<13)(k0)
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun	/* Ought to log this recovered icache error */
114*4882a593Smuzhiyun
115*4882a593Smuzhiyunrecovered:
116*4882a593Smuzhiyun	/* Restore the saved registers */
117*4882a593Smuzhiyun	ld	k0,0x170($0)
118*4882a593Smuzhiyun	ld	k1,0x178($0)
119*4882a593Smuzhiyun	eret
120*4882a593Smuzhiyun
121*4882a593Smuzhiyununrecoverable:
122*4882a593Smuzhiyun	/* Unrecoverable Icache or Dcache error; log it and/or fail */
123*4882a593Smuzhiyun	j	handle_vec2_sb1
124*4882a593Smuzhiyun	 nop
125*4882a593Smuzhiyun#endif
126*4882a593Smuzhiyun
127*4882a593SmuzhiyunEND(except_vec2_sb1)
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun	LEAF(handle_vec2_sb1)
130*4882a593Smuzhiyun	mfc0	k0,CP0_CONFIG
131*4882a593Smuzhiyun	li	k1,~CONF_CM_CMASK
132*4882a593Smuzhiyun	and	k0,k0,k1
133*4882a593Smuzhiyun	ori	k0,k0,CONF_CM_UNCACHED
134*4882a593Smuzhiyun	mtc0	k0,CP0_CONFIG
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun	SSNOP
137*4882a593Smuzhiyun	SSNOP
138*4882a593Smuzhiyun	SSNOP
139*4882a593Smuzhiyun	SSNOP
140*4882a593Smuzhiyun	bnezl	$0, 1f
141*4882a593Smuzhiyun1:
142*4882a593Smuzhiyun	mfc0	k0, CP0_STATUS
143*4882a593Smuzhiyun	sll	k0, k0, 3			# check CU0 (kernel?)
144*4882a593Smuzhiyun	bltz	k0, 2f
145*4882a593Smuzhiyun	 nop
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun	/* Get a valid Kseg0 stack pointer.  Any task's stack pointer
148*4882a593Smuzhiyun	 * will do, although if we ever want to resume execution we
149*4882a593Smuzhiyun	 * better not have corrupted any state. */
150*4882a593Smuzhiyun	get_saved_sp
151*4882a593Smuzhiyun	move	sp, k1
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun2:
154*4882a593Smuzhiyun	j	sb1_cache_error
155*4882a593Smuzhiyun	 nop
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun	END(handle_vec2_sb1)
158