iwmmxt.S 7.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
/*
 *  linux/arch/arm/kernel/iwmmxt.S
 *
 *  XScale iWMMXt (Concan) context switching and handling
 *
 *  Initial code:
 *  Copyright (c) 2003, Intel Corporation
 *
 *  Full lazy switching support, optimizations and more, by Nicolas Pitre
*   Copyright (c) 2003-2004, MontaVista Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/constants.h>

#define MMX_WR0		 	(0x00)
#define MMX_WR1		 	(0x08)
#define MMX_WR2		 	(0x10)
#define MMX_WR3			(0x18)
#define MMX_WR4		 	(0x20)
#define MMX_WR5		 	(0x28)
#define MMX_WR6		 	(0x30)
#define MMX_WR7		 	(0x38)
#define MMX_WR8		 	(0x40)
#define MMX_WR9		 	(0x48)
#define MMX_WR10		(0x50)
#define MMX_WR11		(0x58)
#define MMX_WR12		(0x60)
#define MMX_WR13		(0x68)
#define MMX_WR14		(0x70)
#define MMX_WR15		(0x78)
#define MMX_WCSSF		(0x80)
#define MMX_WCASF		(0x84)
#define MMX_WCGR0		(0x88)
#define MMX_WCGR1		(0x8C)
#define MMX_WCGR2		(0x90)
#define MMX_WCGR3		(0x94)

#define MMX_SIZE		(0x98)

	.text

/*
 * Lazy switching of Concan coprocessor context
 *
 * r10 = struct thread_info pointer
 * r9  = ret_from_exception
 * lr  = undefined instr exit
 *
 * called from prefetch exception handler with interrupts disabled
 */

ENTRY(iwmmxt_task_enable)

	mrc	p15, 0, r2, c15, c1, 0
	tst	r2, #0x3			@ CP0 and CP1 accessible?
	movne	pc, lr				@ if so no business here
	orr	r2, r2, #0x3			@ enable access to CP0 and CP1
	mcr	p15, 0, r2, c15, c1, 0

	ldr	r3, =concan_owner
	add	r0, r10, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r2, [sp, #60]			@ current task pc value
	ldr	r1, [r3]			@ get current Concan owner
	str	r0, [r3]			@ this task now owns Concan regs
	sub	r2, r2, #4			@ adjust pc back
	str	r2, [sp, #60]

	mrc	p15, 0, r2, c2, c0, 0
	mov	r2, r2				@ cpwait

	teq	r1, #0				@ test for last ownership
	mov	lr, r9				@ normal exit from exception
	beq	concan_load			@ no owner, skip save

concan_save:

	tmrc	r2, wCon

	@ CUP? wCx
	tst	r2, #0x1
	beq 	1f

concan_dump:

	wstrw	wCSSF, [r1, #MMX_WCSSF]
	wstrw	wCASF, [r1, #MMX_WCASF]
	wstrw	wCGR0, [r1, #MMX_WCGR0]
	wstrw	wCGR1, [r1, #MMX_WCGR1]
	wstrw	wCGR2, [r1, #MMX_WCGR2]
	wstrw	wCGR3, [r1, #MMX_WCGR3]

1:	@ MUP? wRn
	tst	r2, #0x2
	beq	2f

	wstrd	wR0,  [r1, #MMX_WR0]
	wstrd	wR1,  [r1, #MMX_WR1]
	wstrd	wR2,  [r1, #MMX_WR2]
	wstrd	wR3,  [r1, #MMX_WR3]
	wstrd	wR4,  [r1, #MMX_WR4]
	wstrd	wR5,  [r1, #MMX_WR5]
	wstrd	wR6,  [r1, #MMX_WR6]
	wstrd	wR7,  [r1, #MMX_WR7]
	wstrd	wR8,  [r1, #MMX_WR8]
	wstrd	wR9,  [r1, #MMX_WR9]
	wstrd	wR10, [r1, #MMX_WR10]
	wstrd	wR11, [r1, #MMX_WR11]
	wstrd	wR12, [r1, #MMX_WR12]
	wstrd	wR13, [r1, #MMX_WR13]
	wstrd	wR14, [r1, #MMX_WR14]
	wstrd	wR15, [r1, #MMX_WR15]

2:	teq	r0, #0				@ anything to load?
	moveq	pc, lr

concan_load:

	@ Load wRn
	wldrd	wR0,  [r0, #MMX_WR0]
	wldrd	wR1,  [r0, #MMX_WR1]
	wldrd	wR2,  [r0, #MMX_WR2]
	wldrd	wR3,  [r0, #MMX_WR3]
	wldrd	wR4,  [r0, #MMX_WR4]
	wldrd	wR5,  [r0, #MMX_WR5]
	wldrd	wR6,  [r0, #MMX_WR6]
	wldrd	wR7,  [r0, #MMX_WR7]
	wldrd	wR8,  [r0, #MMX_WR8]
	wldrd	wR9,  [r0, #MMX_WR9]
	wldrd	wR10, [r0, #MMX_WR10]
	wldrd	wR11, [r0, #MMX_WR11]
	wldrd	wR12, [r0, #MMX_WR12]
	wldrd	wR13, [r0, #MMX_WR13]
	wldrd	wR14, [r0, #MMX_WR14]
	wldrd	wR15, [r0, #MMX_WR15]

	@ Load wCx
	wldrw	wCSSF, [r0, #MMX_WCSSF]
	wldrw	wCASF, [r0, #MMX_WCASF]
	wldrw	wCGR0, [r0, #MMX_WCGR0]
	wldrw	wCGR1, [r0, #MMX_WCGR1]
	wldrw	wCGR2, [r0, #MMX_WCGR2]
	wldrw	wCGR3, [r0, #MMX_WCGR3]

	@ clear CUP/MUP (only if r1 != 0)
	teq	r1, #0
	mov 	r2, #0
	moveq	pc, lr
	tmcr	wCon, r2
	mov	pc, lr

/*
 * Back up Concan regs to save area and disable access to them
 * (mainly for gdb or sleep mode usage)
 *
 * r0 = struct thread_info pointer of target task or NULL for any
 */

ENTRY(iwmmxt_task_disable)

	stmfd	sp!, {r4, lr}

	mrs	ip, cpsr
	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, r2

	ldr	r3, =concan_owner
	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r1, [r3]			@ get current Concan owner
	teq	r1, #0				@ any current owner?
	beq	1f				@ no: quit
	teq	r0, #0				@ any owner?
	teqne	r1, r2				@ or specified one?
	bne	1f				@ no: quit

	mrc	p15, 0, r4, c15, c1, 0
	orr	r4, r4, #0x3			@ enable access to CP0 and CP1
	mcr	p15, 0, r4, c15, c1, 0
	mov	r0, #0				@ nothing to load
	str	r0, [r3]			@ no more current owner
	mrc	p15, 0, r2, c2, c0, 0
	mov	r2, r2				@ cpwait
	bl	concan_save

	bic	r4, r4, #0x3			@ disable access to CP0 and CP1
	mcr	p15, 0, r4, c15, c1, 0
	mrc	p15, 0, r2, c2, c0, 0
	mov	r2, r2				@ cpwait

1:	msr	cpsr_c, ip			@ restore interrupt mode
	ldmfd	sp!, {r4, pc}

/*
 * Copy Concan state to given memory address
 *
 * r0 = struct thread_info pointer of target task
 * r1 = memory address where to store Concan state
 *
 * this is called mainly in the creation of signal stack frames
 */

ENTRY(iwmmxt_task_copy)

	mrs	ip, cpsr
	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, r2

	ldr	r3, =concan_owner
	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r3, [r3]			@ get current Concan owner
	teq	r2, r3				@ does this task own it...
	beq	1f

	@ current Concan values are in the task save area
	msr	cpsr_c, ip			@ restore interrupt mode
	mov	r0, r1
	mov	r1, r2
	mov	r2, #MMX_SIZE
	b	memcpy

1:	@ this task owns Concan regs -- grab a copy from there
	mov	r0, #0				@ nothing to load
	mov	r2, #3				@ save all regs
	mov	r3, lr				@ preserve return address
	bl	concan_dump
	msr	cpsr_c, ip			@ restore interrupt mode
	mov	pc, r3

/*
 * Restore Concan state from given memory address
 *
 * r0 = struct thread_info pointer of target task
 * r1 = memory address where to get Concan state from
 *
 * this is used to restore Concan state when unwinding a signal stack frame
 */

ENTRY(iwmmxt_task_restore)

	mrs	ip, cpsr
	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, r2

	ldr	r3, =concan_owner
	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r3, [r3]			@ get current Concan owner
	bic	r2, r2, #0x7			@ 64-bit alignment
	teq	r2, r3				@ does this task own it...
	beq	1f

	@ this task doesn't own Concan regs -- use its save area
	msr	cpsr_c, ip			@ restore interrupt mode
	mov	r0, r2
	mov	r2, #MMX_SIZE
	b	memcpy

1:	@ this task owns Concan regs -- load them directly
	mov	r0, r1
	mov	r1, #0				@ don't clear CUP/MUP
	mov	r3, lr				@ preserve return address
	bl	concan_load
	msr	cpsr_c, ip			@ restore interrupt mode
	mov	pc, r3

/*
 * Concan handling on task switch
 *
 * r0 = previous task_struct pointer (must be preserved)
 * r1 = previous thread_info pointer
 * r2 = next thread_info.cpu_domain pointer (must be preserved)
 *
 * Called only from __switch_to with task preemption disabled.
 * No need to care about preserving r4 and above.
 */
ENTRY(iwmmxt_task_switch)

	mrc	p15, 0, r4, c15, c1, 0
	tst	r4, #0x3			@ CP0 and CP1 accessible?
	bne	1f				@ yes: block them for next task

	ldr	r5, =concan_owner
	add	r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area
	ldr	r5, [r5]			@ get current Concan owner
	teq	r5, r6				@ next task owns it?
	movne	pc, lr				@ no: leave Concan disabled

1:	eor	r4, r4, #3			@ flip Concan access
	mcr	p15, 0, r4, c15, c1, 0

	mrc	p15, 0, r4, c2, c0, 0
	sub	pc, lr, r4, lsr #32		@ cpwait and return

/*
 * Remove Concan ownership of given task
 *
 * r0 = struct thread_info pointer
 */
ENTRY(iwmmxt_task_release)

	mrs	r2, cpsr
	orr	ip, r2, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, ip
	ldr	r3, =concan_owner
	add	r0, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r1, [r3]			@ get current Concan owner
	eors	r0, r0, r1			@ if equal...
	streq	r0, [r3]			@ then clear ownership
	msr	cpsr_c, r2			@ restore interrupts
	mov	pc, lr

	.data
concan_owner:
	.word	0