1/*
2 *  linux/arch/arm26/mm/proc-arm2,3.S
3 *
4 *  Copyright (C) 1997-1999 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 *  MMU functions for ARM2,3
11 *
12 *  These are the low level assembler for performing cache
13 *  and memory functions on ARM2, ARM250 and ARM3 processors.
14 */
15#include <linux/linkage.h>
16#include <asm/assembler.h>
17#include <asm/asm-offsets.h>
18#include <asm/procinfo.h>
19#include <asm/ptrace.h>
20
21/*
22 * MEMC workhorse code.  It's both a horse which things it's a pig.
23 */
24/*
25 * Function: cpu_memc_update_entry(pgd_t *pgd, unsigned long phys_pte, unsigned long addr)
26 * Params  : pgd	Page tables/MEMC mapping
27 *         : phys_pte	physical address, or PTE
28 *         : addr	virtual address
29 */
30ENTRY(cpu_memc_update_entry)
31		tst	r1, #PAGE_PRESENT		@ is the page present
32		orreq	r1, r1, #PAGE_OLD | PAGE_CLEAN
33		moveq	r2, #0x01f00000
34		mov	r3, r1, lsr #13			@ convert to physical page nr
35		and	r3, r3, #0x3fc
36		adr	ip, memc_phys_table_32
37		ldr	r3, [ip, r3]
38		tst	r1, #PAGE_OLD | PAGE_NOT_USER
39		biceq	r3, r3, #0x200
40		tsteq	r1, #PAGE_READONLY | PAGE_CLEAN
41		biceq	r3, r3, #0x300
42		mov	r2, r2, lsr #15			@ virtual -> nr
43		orr	r3, r3, r2, lsl #15
44		and	r2, r2, #0x300
45		orr	r3, r3, r2, lsl #2
46		and	r2, r3, #255
47		sub	r0, r0, #256 * 4
48		str	r3, [r0, r2, lsl #2]
49		strb	r3, [r3]
50		movs	pc, lr
51/*
52 * Params  : r0 = preserved
53 *         : r1 = memc table base (preserved)
54 *         : r2 = page table entry
55 *         : r3 = preserved
56 *         : r4 = unused
57 *         : r5 = memc physical address translation table
58 *         : ip = virtual address (preserved)
59 */
60update_pte:
61		mov	r4, r2, lsr #13
62		and	r4, r4, #0x3fc
63		ldr	r4, [r5, r4]			@ covert to MEMC page
64
65		tst	r2, #PAGE_OLD | PAGE_NOT_USER	@ check for MEMC read
66		biceq	r4, r4, #0x200
67		tsteq	r2, #PAGE_READONLY | PAGE_CLEAN	@ check for MEMC write
68		biceq	r4, r4, #0x300
69
70		orr	r4, r4, ip
71		and	r2, ip, #0x01800000
72		orr	r4, r4, r2, lsr #13
73
74		and	r2, r4, #255
75		str	r4, [r1, r2, lsl #2]
76		movs	pc, lr
77
78/*
79 * Params  : r0 = preserved
80 *         : r1 = memc table base (preserved)
81 *         : r2 = page table base
82 *         : r3 = preserved
83 *         : r4 = unused
84 *         : r5 = memc physical address translation table
85 *         : ip = virtual address (updated)
86 */
87update_pte_table:
88		stmfd	sp!, {r0, lr}
89		bic	r0, r2, #3
901:		ldr	r2, [r0], #4			@ get entry
91		tst	r2, #PAGE_PRESENT		@ page present
92		blne	update_pte			@ process pte
93		add	ip, ip, #32768			@ increment virt addr
94		ldr	r2, [r0], #4			@ get entry
95		tst	r2, #PAGE_PRESENT		@ page present
96		blne	update_pte			@ process pte
97		add	ip, ip, #32768			@ increment virt addr
98		ldr	r2, [r0], #4			@ get entry
99		tst	r2, #PAGE_PRESENT		@ page present
100		blne	update_pte			@ process pte
101		add	ip, ip, #32768			@ increment virt addr
102		ldr	r2, [r0], #4			@ get entry
103		tst	r2, #PAGE_PRESENT		@ page present
104		blne	update_pte			@ process pte
105		add	ip, ip, #32768			@ increment virt addr
106		tst	ip, #32768 * 31			@ finished?
107		bne	1b
108		ldmfd	sp!, {r0, pc}^
109
110/*
111 * Function: cpu_memc_update_all(pgd_t *pgd)
112 * Params  : pgd	Page tables/MEMC mapping
113 * Notes   : this is optimised for 32k pages
114 */
115ENTRY(cpu_memc_update_all)
116		stmfd	sp!, {r4, r5, lr}
117		bl	clear_tables
118		sub	r1, r0, #256 * 4		@ start of MEMC tables
119		adr	r5, memc_phys_table_32		@ Convert to logical page number
120		mov	ip, #0				@ virtual address
1211:		ldmia	r0!, {r2, r3}                   @ load two pgd entries
122		tst	r2, #PAGE_PRESENT               @ is pgd entry present?
123		addeq	ip, ip, #1048576        @FIXME - PAGE_PRESENT is for PTEs technically...
124		blne	update_pte_table
125		mov	r2, r3
126		tst	r2, #PAGE_PRESENT		@ is pgd entry present?
127		addeq	ip, ip, #1048576
128		blne	update_pte_table
129		teq	ip, #32 * 1048576
130		bne	1b
131		ldmfd	sp!, {r4, r5, pc}^
132
133/*
134 * Build the table to map from physical page number to memc page number
135 */
136		.type	memc_phys_table_32, #object
137memc_phys_table_32:
138		.irp	b7, 0x00, 0x80
139		.irp	b6, 0x00, 0x02
140		.irp	b5, 0x00, 0x04
141		.irp	b4, 0x00, 0x01
142
143		.irp	b3, 0x00, 0x40
144		.irp	b2, 0x00, 0x20
145		.irp	b1, 0x00, 0x10
146		.irp	b0, 0x00, 0x08
147		.long	0x03800300 + \b7 + \b6 + \b5 + \b4 + \b3 + \b2 + \b1 + \b0
148		.endr
149		.endr
150		.endr
151		.endr
152
153		.endr
154		.endr
155		.endr
156		.endr
157		.size	memc_phys_table_32, . - memc_phys_table_32
158
159/*
160 * helper for cpu_memc_update_all, this clears out all
161 * mappings, setting them close to the top of memory,
162 * and inaccessible (0x01f00000).
163 * Params  : r0 = page table pointer
164 */
165clear_tables:	ldr	r1, _arm3_set_pgd - 4
166		ldr	r2, [r1]
167		sub	r1, r0, #256 * 4		@ start of MEMC tables
168		add	r2, r1, r2, lsl #2		@ end of tables
169		mov	r3, #0x03f00000			@ Default mapping (null mapping)
170		orr	r3, r3, #0x00000f00
171		orr	r4, r3, #1
172		orr	r5, r3, #2
173		orr	ip, r3, #3
1741:		stmia	r1!, {r3, r4, r5, ip}
175		add	r3, r3, #4
176		add	r4, r4, #4
177		add	r5, r5, #4
178		add	ip, ip, #4
179		stmia	r1!, {r3, r4, r5, ip}
180		add	r3, r3, #4
181		add	r4, r4, #4
182		add	r5, r5, #4
183		add	ip, ip, #4
184		teq	r1, r2
185		bne	1b
186		mov	pc, lr
187
188/*
189 * Function: *_set_pgd(pgd_t *pgd)
190 * Params  : pgd	New page tables/MEMC mapping
191 * Purpose : update MEMC hardware with new mapping
192 */
193		.word	page_nr   @ extern - declared in mm-memc.c
194_arm3_set_pgd:	mcr	p15, 0, r1, c1, c0, 0		@ flush cache
195_arm2_set_pgd:	stmfd	sp!, {lr}
196		ldr	r1, _arm3_set_pgd - 4
197		ldr	r2, [r1]
198		sub	r0, r0, #256 * 4		@ start of MEMC tables
199		add	r1, r0, r2, lsl #2		@ end of tables
2001:		ldmia	r0!, {r2, r3, ip, lr}
201		strb	r2, [r2]
202		strb	r3, [r3]
203		strb	ip, [ip]
204		strb	lr, [lr]
205		ldmia	r0!, {r2, r3, ip, lr}
206		strb	r2, [r2]
207		strb	r3, [r3]
208		strb	ip, [ip]
209		strb	lr, [lr]
210		teq	r0, r1
211		bne	1b
212		ldmfd	sp!, {pc}^
213
214/*
215 * Function: *_proc_init (void)
216 * Purpose : Initialise the cache control registers
217 */
218_arm3_proc_init:
219		mov	r0, #0x001f0000
220		orr	r0, r0, #0x0000ff00
221		orr	r0, r0, #0x000000ff
222		mcr	p15, 0, r0, c3, c0		@ ARM3 Cacheable
223		mcr     p15, 0, r0, c4, c0		@ ARM3 Updateable
224		mov	r0, #0
225		mcr     p15, 0, r0, c5, c0		@ ARM3 Disruptive
226		mcr     p15, 0, r0, c1, c0		@ ARM3 Flush
227		mov	r0, #3
228		mcr     p15, 0, r0, c2, c0		@ ARM3 Control
229_arm2_proc_init:
230		movs	pc, lr
231
232/*
233 * Function: *_proc_fin (void)
234 * Purpose : Finalise processor (disable caches)
235 */
236_arm3_proc_fin:	mov	r0, #2
237		mcr	p15, 0, r0, c2, c0
238_arm2_proc_fin:	orrs	pc, lr, #PSR_I_BIT|PSR_F_BIT
239
240/*
241 * Function: *_xchg_1 (int new, volatile void *ptr)
242 * Params  : new	New value to store at...
243 *	   : ptr	pointer to byte-wide location
244 * Purpose : Performs an exchange operation
245 * Returns : Original byte data at 'ptr'
246 */
247_arm2_xchg_1:	mov	r2, pc
248		orr	r2, r2, #PSR_I_BIT
249		teqp	r2, #0
250		ldrb	r2, [r1]
251		strb	r0, [r1]
252		mov	r0, r2
253		movs	pc, lr
254
255_arm3_xchg_1:	swpb	r0, r0, [r1]
256		movs	pc, lr
257
258/*
259 * Function: *_xchg_4 (int new, volatile void *ptr)
260 * Params  : new	New value to store at...
261 *	   : ptr	pointer to word-wide location
262 * Purpose : Performs an exchange operation
263 * Returns : Original word data at 'ptr'
264 */
265_arm2_xchg_4:	mov	r2, pc
266		orr	r2, r2, #PSR_I_BIT
267		teqp	r2, #0
268		ldr	r2, [r1]
269		str	r0, [r1]
270		mov	r0, r2
271		movs	pc, lr
272
273_arm3_xchg_4:	swp	r0, r0, [r1]
274		movs	pc, lr
275
276_arm2_3_check_bugs:
277		bics	pc, lr, #PSR_F_BIT		@ Clear FIQ disable bit
278
279armvlsi_name:	.asciz	"ARM/VLSI"
280_arm2_name:	.asciz	"ARM 2"
281_arm250_name:	.asciz	"ARM 250"
282_arm3_name:	.asciz	"ARM 3"
283
284		.section ".init.text", #alloc, #execinstr
285/*
286 * Purpose : Function pointers used to access above functions - all calls
287 *	     come through these
288 */
289		.globl	arm2_processor_functions
290arm2_processor_functions:
291		.word	_arm2_3_check_bugs
292		.word	_arm2_proc_init
293		.word	_arm2_proc_fin
294		.word	_arm2_set_pgd
295		.word	_arm2_xchg_1
296		.word	_arm2_xchg_4
297
298cpu_arm2_info:
299		.long	armvlsi_name
300		.long	_arm2_name
301
302		.globl	arm250_processor_functions
303arm250_processor_functions:
304		.word	_arm2_3_check_bugs
305		.word	_arm2_proc_init
306		.word	_arm2_proc_fin
307		.word	_arm2_set_pgd
308		.word	_arm3_xchg_1
309		.word	_arm3_xchg_4
310
311cpu_arm250_info:
312		.long	armvlsi_name
313		.long	_arm250_name
314
315		.globl	arm3_processor_functions
316arm3_processor_functions:
317		.word	_arm2_3_check_bugs
318		.word	_arm3_proc_init
319		.word	_arm3_proc_fin
320		.word	_arm3_set_pgd
321		.word	_arm3_xchg_1
322		.word	_arm3_xchg_4
323
324cpu_arm3_info:
325		.long	armvlsi_name
326		.long	_arm3_name
327
328arm2_arch_name:	.asciz	"armv1"
329arm3_arch_name:	.asciz	"armv2"
330arm2_elf_name:	.asciz	"v1"
331arm3_elf_name:	.asciz	"v2"
332		.align
333
334		.section ".proc.info", #alloc, #execinstr
335
336		.long	0x41560200
337		.long	0xfffffff0
338		.long	arm2_arch_name
339		.long	arm2_elf_name
340		.long   0
341		.long	cpu_arm2_info
342		.long	arm2_processor_functions
343
344		.long	0x41560250
345		.long	0xfffffff0
346		.long	arm3_arch_name
347		.long	arm3_elf_name
348		.long   0
349		.long	cpu_arm250_info
350		.long	arm250_processor_functions
351
352		.long	0x41560300
353		.long	0xfffffff0
354		.long	arm3_arch_name
355		.long	arm3_elf_name
356		.long   0
357		.long	cpu_arm3_info
358		.long	arm3_processor_functions
359