1/*
2 * vr4300.S -- CPU specific support routines
3 *
4 * Copyright (c) 1995,1996 Cygnus Support
5 *
6 * The authors hereby grant permission to use, copy, modify, distribute,
7 * and license this software and its documentation for any purpose, provided
8 * that existing copyright notices are retained in all copies and that this
9 * notice is included verbatim in any distributions. No written agreement,
10 * license, or royalty fee is required for any of the authorized uses.
11 * Modifications to this software may be copyrighted by their authors
12 * and need not follow the licensing terms described here, provided that
13 * the new terms are clearly indicated on the first page of each file where
14 * they apply.
15 */
16
17#ifndef __mips64
18	.set mips3
19#endif
20#ifdef __mips16
21/* This file contains 32 bit assembly code.  */
22	.set nomips16
23#endif
24
25#include "regs.S"
26
27	.text
28	.align	2
29
30	# Taken from "R4300 Preliminary RISC Processor Specification
31	# Revision 2.0 January 1995" page 39: "The Count
32	# register... increments at a constant rate... at one-half the
33	# PClock speed."
34	# We can use this fact to provide small polled delays.
35	.globl	__cpu_timer_poll
36	.ent	__cpu_timer_poll
37__cpu_timer_poll:
38	.set	noreorder
39	# in:	a0 = (unsigned int) number of PClock ticks to wait for
40	# out:	void
41
42	# The Vr4300 counter updates at half PClock, so divide by 2 to
43	# get counter delta:
44	bnezl	a0, 1f		# continue if delta non-zero
45	srl	a0, a0, 1	# divide ticks by 2		{DELAY SLOT}
46	# perform a quick return to the caller:
47	j	ra
48	nop			#				{DELAY SLOT}
491:
50	mfc0	v0, $9		# C0_COUNT:  get current counter value
51	nop
52	nop
53	# We cannot just do the simple test, of adding our delta onto
54	# the current value (ignoring overflow) and then checking for
55	# equality. The counter is incrementing every two PClocks,
56	# which means the counter value can change between
57	# instructions, making it hard to sample at the exact value
58	# desired.
59
60	# However, we do know that our entry delta value is less than
61	# half the number space (since we divide by 2 on entry). This
62	# means we can use a difference in signs to indicate timer
63	# overflow.
64	addu	a0, v0, a0	# unsigned add (ignore overflow)
65	# We know have our end value (which will have been
66	# sign-extended to fill the 64bit register value).
672:
68	# get current counter value:
69	mfc0	v0, $9	# C0_COUNT
70	nop
71	nop
72	# This is an unsigned 32bit subtraction:
73	subu	v0, a0, v0	# delta = (end - now)		{DELAY SLOT}
74	bgtzl	v0, 2b		# looping back is most likely
75	nop
76	# We have now been delayed (in the foreground) for AT LEAST
77	# the required number of counter ticks.
78	j	ra		# return to caller
79	nop			#				{DELAY SLOT}
80	.set	reorder
81	.end	__cpu_timer_poll
82
83	# Flush the processor caches to memory:
84
85	.globl	__cpu_flush
86	.ent	__cpu_flush
87__cpu_flush:
88	.set	noreorder
89	# NOTE: The Vr4300 *CANNOT* have any secondary cache (bit 17
90	# of the CONFIG registered is hard-wired to 1). We just
91	# provide code to flush the Data and Instruction caches.
92
93	# Even though the Vr4300 has hard-wired cache and cache line
94	# sizes, we still interpret the relevant Config register
95	# bits. This allows this code to be used for other conforming
96	# MIPS architectures if desired.
97
98	# Get the config register
99	mfc0	a0, C0_CONFIG
100	nop
101	nop
102	li	a1, 1		# a useful constant
103	#
104	srl	a2, a0, 9	# bits 11..9 for instruction cache size
105	andi	a2, a2, 0x7	# 3bits of information
106	add	a2, a2, 12	# get full power-of-2 value
107	sllv	a2, a1, a2	# instruction cache size
108	#
109	srl	a3, a0, 6	# bits 8..6 for data cache size
110	andi	a3, a3, 0x7	# 3bits of information
111	add	a3, a3, 12	# get full power-of-2 value
112	sllv	a3, a1, a3	# data cache size
113	#
114	li	a1, (1 << 5)	# check IB (instruction cache line size)
115	and	a1, a0, a1	# mask against the CONFIG register value
116	beqz	a1, 1f		# branch on result of delay slot operation
117	nop
118	li	a1, 32		# non-zero, then 32bytes
119	j	2f		# continue
120	nop
1211:
122	li	a1, 16		# 16bytes
1232:
124	#
125	li	t0, (1 << 4)	# check DB (data cache line size)
126	and	a0, a0, t0	# mask against the CONFIG register value
127	beqz	a0, 3f		# branch on result of delay slot operation
128	nop
129	li	a0, 32		# non-zero, then 32bytes
130	j	4f		# continue
131	nop
1323:
133	li	a0, 16		# 16bytes
1344:
135	#
136	# a0 = data cache line size
137	# a1 = instruction cache line size
138	# a2 = instruction cache size
139	# a3 = data cache size
140	#
141	lui	t0, ((K0BASE >> 16) & 0xFFFF)
142	ori	t0, t0, (K0BASE & 0xFFFF)
143	addu	t1, t0, a2	# end cache address
144	subu	t2, a1, 1	# line size mask
145	not	t2		# invert the mask
146	and	t3, t0, t2	# get start address
147	addu	t1, -1
148	and	t1, t2		# get end address
1495:
150	cache	INDEX_INVALIDATE_I,0(t3)
151	bne	t3, t1, 5b
152	addu	t3, a1
153	#
154	addu	t1, t0, a3	# end cache address
155	subu	t2, a0, 1	# line size mask
156	not	t2		# invert the mask
157	and	t3, t0, t2	# get start address
158	addu	t1, -1
159	and	t1, t2		# get end address
1606:
161	cache	INDEX_WRITEBACK_INVALIDATE_D,0(t3)
162	bne	t3, t1, 6b
163	addu	t3, a0
164	#
165	j	ra	# return to the caller
166	nop
167	.set	reorder
168	.end	__cpu_flush
169
170	# NOTE: This variable should *NOT* be addressed relative to
171	# the $gp register since this code is executed before $gp is
172	# initialised... hence we leave it in the text area. This will
173	# cause problems if this routine is ever ROMmed:
174
175	.globl	__buserr_cnt
176__buserr_cnt:
177	.word	0
178	.align	3
179__k1_save:
180	.word	0
181	.word	0
182	.align	2
183
184        .ent __buserr
185        .globl __buserr
186__buserr:
187        .set noat
188	.set noreorder
189	# k0 and k1 available for use:
190	mfc0	k0,C0_CAUSE
191	nop
192	nop
193	andi	k0,k0,0x7c
194	sub	k0,k0,7 << 2
195	beq	k0,$0,__buserr_do
196	nop
197	# call the previous handler
198	la	k0,__previous
199	jr	k0
200	nop
201	#
202__buserr_do:
203	# TODO: check that the cause is indeed a bus error
204	# - if not then just jump to the previous handler
205	la	k0,__k1_save
206	sd	k1,0(k0)
207	#
208        la      k1,__buserr_cnt
209        lw      k0,0(k1)        # increment counter
210        addu    k0,1
211        sw      k0,0(k1)
212	#
213	la	k0,__k1_save
214	ld	k1,0(k0)
215	#
216        mfc0    k0,C0_EPC
217	nop
218	nop
219        addu    k0,k0,4		# skip offending instruction
220	mtc0	k0,C0_EPC	# update EPC
221	nop
222	nop
223	eret
224#        j       k0
225#        rfe
226        .set reorder
227        .set at
228        .end __buserr
229
230__exception_code:
231	.set noreorder
232	lui	k0,%hi(__buserr)
233	daddiu	k0,k0,%lo(__buserr)
234	jr	k0
235	nop
236	.set reorder
237__exception_code_end:
238
239	.data
240__previous:
241	.space	(__exception_code_end - __exception_code)
242	# This subtracting two addresses is working
243	# but is not garenteed to continue working.
244	# The assemble reserves the right to put these
245	# two labels into different frags, and then
246	# cant take their difference.
247
248	.text
249
250	.ent	__default_buserr_handler
251	.globl	__default_buserr_handler
252__default_buserr_handler:
253        .set noreorder
254	# attach our simple bus error handler:
255	# in:  void
256	# out: void
257	mfc0	a0,C0_SR
258	nop
259	li	a1,SR_BEV
260	and	a1,a1,a0
261	beq	a1,$0,baseaddr
262	lui	a0,0x8000	# delay slot
263	lui	a0,0xbfc0
264	daddiu	a0,a0,0x0200
265baseaddr:
266	daddiu	a0,a0,0x0180
267	# a0 = base vector table address
268	la	a1,__exception_code_end
269	la	a2,__exception_code
270	subu	a1,a1,a2
271	la	a3,__previous
272	# there must be a better way of doing this????
273copyloop:
274	lw	v0,0(a0)
275	sw	v0,0(a3)
276	lw	v0,0(a2)
277	sw	v0,0(a0)
278	daddiu	a0,a0,4
279	daddiu	a2,a2,4
280	daddiu	a3,a3,4
281	subu	a1,a1,4
282	bne	a1,$0,copyloop
283	nop
284        la      a0,__buserr_cnt
285	sw	$0,0(a0)
286	j	ra
287	nop
288        .set reorder
289	.end	__default_buserr_handler
290
291	.ent	__restore_buserr_handler
292	.globl	__restore_buserr_handler
293__restore_buserr_handler:
294        .set noreorder
295	# restore original (monitor) bus error handler
296	# in:  void
297	# out: void
298	mfc0	a0,C0_SR
299	nop
300	li	a1,SR_BEV
301	and	a1,a1,a0
302	beq	a1,$0,res_baseaddr
303	lui	a0,0x8000	# delay slot
304	lui	a0,0xbfc0
305	daddiu	a0,a0,0x0200
306res_baseaddr:
307	daddiu	a0,a0,0x0180
308	# a0 = base vector table address
309	la	a1,__exception_code_end
310	la	a3,__exception_code
311	subu	a1,a1,a3
312	la	a3,__previous
313	# there must be a better way of doing this????
314res_copyloop:
315	lw	v0,0(a3)
316	sw	v0,0(a0)
317	daddiu	a0,a0,4
318	daddiu	a3,a3,4
319	subu	a1,a1,4
320	bne	a1,$0,res_copyloop
321	nop
322	j	ra
323	nop
324        .set reorder
325	.end	__restore_buserr_handler
326
327	.ent	__buserr_count
328	.globl	__buserr_count
329__buserr_count:
330        .set noreorder
331	# restore original (monitor) bus error handler
332	# in:  void
333	# out: unsigned int __buserr_cnt
334        la      v0,__buserr_cnt
335	lw	v0,0(v0)
336	j	ra
337	nop
338        .set reorder
339	.end	__buserr_count
340
341/* EOF vr4300.S */
342