1129198Scognet/*	$NetBSD: cpufunc_asm_arm10.S,v 1.1 2003/09/06 09:12:29 rearnsha Exp $	*/
2129198Scognet
3139735Simp/*-
4129198Scognet * Copyright (c) 2002 ARM Limited
5129198Scognet * All rights reserved.
6129198Scognet *
7129198Scognet * Redistribution and use in source and binary forms, with or without
8129198Scognet * modification, are permitted provided that the following conditions
9129198Scognet * are met:
10129198Scognet * 1. Redistributions of source code must retain the above copyright
11129198Scognet *    notice, this list of conditions and the following disclaimer.
12129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
13129198Scognet *    notice, this list of conditions and the following disclaimer in the
14129198Scognet *    documentation and/or other materials provided with the distribution.
15129198Scognet * 3. The name of the company may not be used to endorse or promote
16129198Scognet *    products derived from this software without specific prior written
17129198Scognet *    permission.
18129198Scognet *
19129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
20129198Scognet * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21129198Scognet * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22129198Scognet * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23129198Scognet * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24129198Scognet * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25129198Scognet * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29129198Scognet * SUCH DAMAGE.
30129198Scognet *
31129198Scognet * ARM10 assembly functions for CPU / MMU / TLB specific operations
32129198Scognet *
33129198Scognet */
34236991Simp
35129198Scognet#include <machine/asm.h>
36129198Scognet__FBSDID("$FreeBSD$");
37129198Scognet
38129198Scognet/*
39129198Scognet * Functions to set the MMU Translation Table Base register
40129198Scognet *
41129198Scognet * We need to clean and flush the cache as it uses virtual
42129198Scognet * addresses that are about to change.
43129198Scognet */
44129198ScognetENTRY(arm10_setttb)
45129198Scognet	stmfd	sp!, {r0, lr}
46129198Scognet	bl	_C_LABEL(arm10_idcache_wbinv_all)
47129198Scognet	ldmfd	sp!, {r0, lr}
48129198Scognet
49129198Scognet	mcr	p15, 0, r0, c2, c0, 0	/* load new TTB */
50129198Scognet
51129198Scognet	mcr	p15, 0, r0, c8, c7, 0	/* invalidate I+D TLBs */
52129198Scognet	bx	lr
53248361SandrewEND(arm10_setttb)
54129198Scognet
55129198Scognet/*
56129198Scognet * TLB functions
57129198Scognet */
58129198ScognetENTRY(arm10_tlb_flushID_SE)
59129198Scognet	mcr	p15, 0, r0, c8, c6, 1	/* flush D tlb single entry */
60129198Scognet	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
61129198Scognet	bx	lr
62248361SandrewEND(arm10_tlb_flushID_SE)
63129198Scognet
64129198ScognetENTRY(arm10_tlb_flushI_SE)
65129198Scognet	mcr	p15, 0, r0, c8, c5, 1	/* flush I tlb single entry */
66129198Scognet	bx	lr
67248361SandrewEND(arm10_tlb_flushI_SE)
68129198Scognet
69129198Scognet/*
70129198Scognet * Cache operations.  For the entire cache we use the set/index
71129198Scognet * operations.
72129198Scognet */
73129198Scognet	s_max	.req r0
74129198Scognet	i_max	.req r1
75129198Scognet	s_inc	.req r2
76129198Scognet	i_inc	.req r3
77129198Scognet
78129198ScognetENTRY_NP(arm10_icache_sync_range)
79129198Scognet	ldr	ip, .Larm10_line_size
80129198Scognet	cmp	r1, #0x4000
81129198Scognet	bcs	.Larm10_icache_sync_all
82129198Scognet	ldr	ip, [ip]
83129198Scognet	sub	r3, ip, #1
84129198Scognet	and	r2, r0, r3
85129198Scognet	add	r1, r1, r2
86129198Scognet	bic	r0, r0, r3
87129198Scognet.Larm10_sync_next:
88129198Scognet	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
89129198Scognet	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
90129198Scognet	add	r0, r0, ip
91129198Scognet	subs	r1, r1, ip
92246001Sian	bhi	.Larm10_sync_next
93129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
94129198Scognet	bx	lr
95248361SandrewEND(arm10_icache_sync_range)
96129198Scognet
97129198ScognetENTRY_NP(arm10_icache_sync_all)
98129198Scognet.Larm10_icache_sync_all:
99129198Scognet	/*
100129198Scognet	 * We assume that the code here can never be out of sync with the
101129198Scognet	 * dcache, so that we can safely flush the Icache and fall through
102129198Scognet	 * into the Dcache cleaning code.
103129198Scognet	 */
104129198Scognet	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
105129198Scognet	/* Fall through to clean Dcache. */
106129198Scognet
107129198Scognet.Larm10_dcache_wb:
108129198Scognet	ldr	ip, .Larm10_cache_data
109129198Scognet	ldmia	ip, {s_max, i_max, s_inc, i_inc}
110129198Scognet.Lnext_set:
111129198Scognet	orr	ip, s_max, i_max
112129198Scognet.Lnext_index:
113129198Scognet	mcr	p15, 0, ip, c7, c10, 2	/* Clean D cache SE with Set/Index */
114246001Sian	subs	ip, ip, i_inc
115246001Sian	bhs	.Lnext_index		/* Next index */
116129198Scognet	subs	s_max, s_max, s_inc
117246001Sian	bhs	.Lnext_set		/* Next set */
118129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
119129198Scognet	bx	lr
120248361SandrewEND(arm10_icache_sync_all)
121129198Scognet
122129198Scognet.Larm10_line_size:
123129198Scognet	.word	_C_LABEL(arm_pdcache_line_size)
124129198Scognet
125129198ScognetENTRY(arm10_dcache_wb_range)
126129198Scognet	ldr	ip, .Larm10_line_size
127129198Scognet	cmp	r1, #0x4000
128129198Scognet	bcs	.Larm10_dcache_wb
129129198Scognet	ldr	ip, [ip]
130129198Scognet	sub	r3, ip, #1
131129198Scognet	and	r2, r0, r3
132129198Scognet	add	r1, r1, r2
133129198Scognet	bic	r0, r0, r3
134129198Scognet.Larm10_wb_next:
135129198Scognet	mcr	p15, 0, r0, c7, c10, 1	/* Clean D cache SE with VA */
136129198Scognet	add	r0, r0, ip
137129198Scognet	subs	r1, r1, ip
138246001Sian	bhi	.Larm10_wb_next
139129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
140129198Scognet	bx	lr
141248361SandrewEND(arm10_dcache_wb_range)
142129198Scognet
143129198ScognetENTRY(arm10_dcache_wbinv_range)
144129198Scognet	ldr	ip, .Larm10_line_size
145129198Scognet	cmp	r1, #0x4000
146129198Scognet	bcs	.Larm10_dcache_wbinv_all
147129198Scognet	ldr	ip, [ip]
148129198Scognet	sub	r3, ip, #1
149129198Scognet	and	r2, r0, r3
150129198Scognet	add	r1, r1, r2
151129198Scognet	bic	r0, r0, r3
152129198Scognet.Larm10_wbinv_next:
153129198Scognet	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
154129198Scognet	add	r0, r0, ip
155129198Scognet	subs	r1, r1, ip
156246001Sian	bhi	.Larm10_wbinv_next
157129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
158129198Scognet	bx	lr
159248361SandrewEND(arm10_dcache_wbinv_range)
160129198Scognet
161129198Scognet/*
162129198Scognet * Note, we must not invalidate everything.  If the range is too big we
163129198Scognet * must use wb-inv of the entire cache.
164129198Scognet */
165129198ScognetENTRY(arm10_dcache_inv_range)
166129198Scognet	ldr	ip, .Larm10_line_size
167129198Scognet	cmp	r1, #0x4000
168129198Scognet	bcs	.Larm10_dcache_wbinv_all
169129198Scognet	ldr	ip, [ip]
170129198Scognet	sub	r3, ip, #1
171129198Scognet	and	r2, r0, r3
172129198Scognet	add	r1, r1, r2
173129198Scognet	bic	r0, r0, r3
174129198Scognet.Larm10_inv_next:
175129198Scognet	mcr	p15, 0, r0, c7, c6, 1	/* Invalidate D cache SE with VA */
176129198Scognet	add	r0, r0, ip
177129198Scognet	subs	r1, r1, ip
178246001Sian	bhi	.Larm10_inv_next
179129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
180129198Scognet	bx	lr
181248361SandrewEND(arm10_dcache_inv_range)
182129198Scognet
183129198ScognetENTRY(arm10_idcache_wbinv_range)
184129198Scognet	ldr	ip, .Larm10_line_size
185129198Scognet	cmp	r1, #0x4000
186129198Scognet	bcs	.Larm10_idcache_wbinv_all
187129198Scognet	ldr	ip, [ip]
188129198Scognet	sub	r3, ip, #1
189129198Scognet	and	r2, r0, r3
190129198Scognet	add	r1, r1, r2
191129198Scognet	bic	r0, r0, r3
192129198Scognet.Larm10_id_wbinv_next:
193129198Scognet	mcr	p15, 0, r0, c7, c5, 1	/* Invalidate I cache SE with VA */
194129198Scognet	mcr	p15, 0, r0, c7, c14, 1	/* Purge D cache SE with VA */
195129198Scognet	add	r0, r0, ip
196129198Scognet	subs	r1, r1, ip
197246001Sian	bhi	.Larm10_id_wbinv_next
198129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
199129198Scognet	bx	lr
200248361SandrewEND(arm10_idcache_wbinv_range)
201129198Scognet
202129198ScognetENTRY_NP(arm10_idcache_wbinv_all)
203129198Scognet.Larm10_idcache_wbinv_all:
204129198Scognet	/*
205129198Scognet	 * We assume that the code here can never be out of sync with the
206129198Scognet	 * dcache, so that we can safely flush the Icache and fall through
207129198Scognet	 * into the Dcache purging code.
208129198Scognet	 */
209129198Scognet	mcr	p15, 0, r0, c7, c5, 0	/* Flush I cache */
210129198Scognet	/* Fall through to purge Dcache. */
211129198Scognet
212269796SianEENTRY(arm10_dcache_wbinv_all)
213129198Scognet.Larm10_dcache_wbinv_all:
214129198Scognet	ldr	ip, .Larm10_cache_data
215129198Scognet	ldmia	ip, {s_max, i_max, s_inc, i_inc}
216129198Scognet.Lnext_set_inv:
217129198Scognet	orr	ip, s_max, i_max
218129198Scognet.Lnext_index_inv:
219129198Scognet	mcr	p15, 0, ip, c7, c14, 2	/* Purge D cache SE with Set/Index */
220246001Sian	subs	ip, ip, i_inc
221246001Sian	bhs	.Lnext_index_inv		/* Next index */
222129198Scognet	subs	s_max, s_max, s_inc
223246001Sian	bhs	.Lnext_set_inv		/* Next set */
224129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
225129198Scognet	bx	lr
226269796SianEEND(arm10_dcache_wbinv_all)
227248361SandrewEND(arm10_idcache_wbinv_all)
228129198Scognet
229129198Scognet.Larm10_cache_data:
230129198Scognet	.word	_C_LABEL(arm10_dcache_sets_max)
231129198Scognet
232129198Scognet/*
233129198Scognet * Context switch.
234129198Scognet *
235129198Scognet * These is the CPU-specific parts of the context switcher cpu_switch()
236129198Scognet * These functions actually perform the TTB reload.
237129198Scognet *
238129198Scognet * NOTE: Special calling convention
239129198Scognet *	r1, r4-r13 must be preserved
240129198Scognet */
241129198ScognetENTRY(arm10_context_switch)
242129198Scognet	/*
243129198Scognet	 * We can assume that the caches will only contain kernel addresses
244129198Scognet	 * at this point.  So no need to flush them again.
245129198Scognet	 */
246129198Scognet	mcr	p15, 0, r0, c7, c10, 4	/* drain the write buffer */
247129198Scognet	mcr	p15, 0, r0, c2, c0, 0	/* set the new TTB */
248129198Scognet	mcr	p15, 0, r0, c8, c7, 0	/* and flush the I+D tlbs */
249129198Scognet
250129198Scognet	/* Paranoia -- make sure the pipeline is empty. */
251129198Scognet	nop
252129198Scognet	nop
253129198Scognet	nop
254129198Scognet	bx	lr
255248361SandrewEND(arm10_context_switch)
256129198Scognet
257129198Scognet	.bss
258129198Scognet
259129198Scognet/* XXX The following macros should probably be moved to asm.h */
260129198Scognet#define _DATA_OBJECT(x) .globl x; .type x,_ASM_TYPE_OBJECT; x:
261129198Scognet#define C_OBJECT(x)	_DATA_OBJECT(_C_LABEL(x))
262129198Scognet
263129198Scognet/*
264129198Scognet * Parameters for the cache cleaning code.  Note that the order of these
265236991Simp * four variables is assumed in the code above.  Hence the reason for
266129198Scognet * declaring them in the assembler file.
267129198Scognet */
268129198Scognet	.align 0
269129198ScognetC_OBJECT(arm10_dcache_sets_max)
270129198Scognet	.space	4
271129198ScognetC_OBJECT(arm10_dcache_index_max)
272129198Scognet	.space	4
273129198ScognetC_OBJECT(arm10_dcache_sets_inc)
274129198Scognet	.space	4
275129198ScognetC_OBJECT(arm10_dcache_index_inc)
276129198Scognet	.space	4
277