| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/arm/kernel/iwmmxt.S | 
 | 3 |  * | 
 | 4 |  *  XScale iWMMXt (Concan) context switching and handling | 
 | 5 |  * | 
 | 6 |  *  Initial code: | 
 | 7 |  *  Copyright (c) 2003, Intel Corporation | 
 | 8 |  * | 
 | 9 |  *  Full lazy switching support, optimizations and more, by Nicolas Pitre | 
 | 10 | *   Copyright (c) 2003-2004, MontaVista Software, Inc. | 
 | 11 |  * | 
 | 12 |  * This program is free software; you can redistribute it and/or modify | 
 | 13 |  * it under the terms of the GNU General Public License version 2 as | 
 | 14 |  * published by the Free Software Foundation. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <linux/linkage.h> | 
 | 18 | #include <asm/ptrace.h> | 
 | 19 | #include <asm/thread_info.h> | 
| Sam Ravnborg | e6ae744 | 2005-09-09 21:08:59 +0200 | [diff] [blame] | 20 | #include <asm/asm-offsets.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 |  | 
 | 22 | #define MMX_WR0		 	(0x00) | 
 | 23 | #define MMX_WR1		 	(0x08) | 
 | 24 | #define MMX_WR2		 	(0x10) | 
 | 25 | #define MMX_WR3			(0x18) | 
 | 26 | #define MMX_WR4		 	(0x20) | 
 | 27 | #define MMX_WR5		 	(0x28) | 
 | 28 | #define MMX_WR6		 	(0x30) | 
 | 29 | #define MMX_WR7		 	(0x38) | 
 | 30 | #define MMX_WR8		 	(0x40) | 
 | 31 | #define MMX_WR9		 	(0x48) | 
 | 32 | #define MMX_WR10		(0x50) | 
 | 33 | #define MMX_WR11		(0x58) | 
 | 34 | #define MMX_WR12		(0x60) | 
 | 35 | #define MMX_WR13		(0x68) | 
 | 36 | #define MMX_WR14		(0x70) | 
 | 37 | #define MMX_WR15		(0x78) | 
 | 38 | #define MMX_WCSSF		(0x80) | 
 | 39 | #define MMX_WCASF		(0x84) | 
 | 40 | #define MMX_WCGR0		(0x88) | 
 | 41 | #define MMX_WCGR1		(0x8C) | 
 | 42 | #define MMX_WCGR2		(0x90) | 
 | 43 | #define MMX_WCGR3		(0x94) | 
 | 44 |  | 
 | 45 | #define MMX_SIZE		(0x98) | 
 | 46 |  | 
 | 47 | 	.text | 
 | 48 |  | 
 | 49 | /* | 
 | 50 |  * Lazy switching of Concan coprocessor context | 
 | 51 |  * | 
 | 52 |  * r10 = struct thread_info pointer | 
 | 53 |  * r9  = ret_from_exception | 
 | 54 |  * lr  = undefined instr exit | 
 | 55 |  * | 
 | 56 |  * called from prefetch exception handler with interrupts disabled | 
 | 57 |  */ | 
 | 58 |  | 
 | 59 | ENTRY(iwmmxt_task_enable) | 
 | 60 |  | 
 | 61 | 	mrc	p15, 0, r2, c15, c1, 0 | 
 | 62 | 	tst	r2, #0x3			@ CP0 and CP1 accessible? | 
 | 63 | 	movne	pc, lr				@ if so no business here | 
 | 64 | 	orr	r2, r2, #0x3			@ enable access to CP0 and CP1 | 
 | 65 | 	mcr	p15, 0, r2, c15, c1, 0 | 
 | 66 |  | 
 | 67 | 	ldr	r3, =concan_owner | 
 | 68 | 	add	r0, r10, #TI_IWMMXT_STATE	@ get task Concan save area | 
 | 69 | 	ldr	r2, [sp, #60]			@ current task pc value | 
 | 70 | 	ldr	r1, [r3]			@ get current Concan owner | 
 | 71 | 	str	r0, [r3]			@ this task now owns Concan regs | 
 | 72 | 	sub	r2, r2, #4			@ adjust pc back | 
 | 73 | 	str	r2, [sp, #60] | 
 | 74 |  | 
 | 75 | 	mrc	p15, 0, r2, c2, c0, 0 | 
 | 76 | 	mov	r2, r2				@ cpwait | 
 | 77 |  | 
 | 78 | 	teq	r1, #0				@ test for last ownership | 
 | 79 | 	mov	lr, r9				@ normal exit from exception | 
 | 80 | 	beq	concan_load			@ no owner, skip save | 
 | 81 |  | 
 | 82 | concan_save: | 
 | 83 |  | 
 | 84 | 	tmrc	r2, wCon | 
 | 85 |  | 
 | 86 | 	@ CUP? wCx | 
 | 87 | 	tst	r2, #0x1 | 
 | 88 | 	beq 	1f | 
 | 89 |  | 
 | 90 | concan_dump: | 
 | 91 |  | 
 | 92 | 	wstrw	wCSSF, [r1, #MMX_WCSSF] | 
 | 93 | 	wstrw	wCASF, [r1, #MMX_WCASF] | 
 | 94 | 	wstrw	wCGR0, [r1, #MMX_WCGR0] | 
 | 95 | 	wstrw	wCGR1, [r1, #MMX_WCGR1] | 
 | 96 | 	wstrw	wCGR2, [r1, #MMX_WCGR2] | 
 | 97 | 	wstrw	wCGR3, [r1, #MMX_WCGR3] | 
 | 98 |  | 
 | 99 | 1:	@ MUP? wRn | 
 | 100 | 	tst	r2, #0x2 | 
 | 101 | 	beq	2f | 
 | 102 |  | 
 | 103 | 	wstrd	wR0,  [r1, #MMX_WR0] | 
 | 104 | 	wstrd	wR1,  [r1, #MMX_WR1] | 
 | 105 | 	wstrd	wR2,  [r1, #MMX_WR2] | 
 | 106 | 	wstrd	wR3,  [r1, #MMX_WR3] | 
 | 107 | 	wstrd	wR4,  [r1, #MMX_WR4] | 
 | 108 | 	wstrd	wR5,  [r1, #MMX_WR5] | 
 | 109 | 	wstrd	wR6,  [r1, #MMX_WR6] | 
 | 110 | 	wstrd	wR7,  [r1, #MMX_WR7] | 
 | 111 | 	wstrd	wR8,  [r1, #MMX_WR8] | 
 | 112 | 	wstrd	wR9,  [r1, #MMX_WR9] | 
 | 113 | 	wstrd	wR10, [r1, #MMX_WR10] | 
 | 114 | 	wstrd	wR11, [r1, #MMX_WR11] | 
 | 115 | 	wstrd	wR12, [r1, #MMX_WR12] | 
 | 116 | 	wstrd	wR13, [r1, #MMX_WR13] | 
 | 117 | 	wstrd	wR14, [r1, #MMX_WR14] | 
 | 118 | 	wstrd	wR15, [r1, #MMX_WR15] | 
 | 119 |  | 
 | 120 | 2:	teq	r0, #0				@ anything to load? | 
 | 121 | 	moveq	pc, lr | 
 | 122 |  | 
 | 123 | concan_load: | 
 | 124 |  | 
 | 125 | 	@ Load wRn | 
 | 126 | 	wldrd	wR0,  [r0, #MMX_WR0] | 
 | 127 | 	wldrd	wR1,  [r0, #MMX_WR1] | 
 | 128 | 	wldrd	wR2,  [r0, #MMX_WR2] | 
 | 129 | 	wldrd	wR3,  [r0, #MMX_WR3] | 
 | 130 | 	wldrd	wR4,  [r0, #MMX_WR4] | 
 | 131 | 	wldrd	wR5,  [r0, #MMX_WR5] | 
 | 132 | 	wldrd	wR6,  [r0, #MMX_WR6] | 
 | 133 | 	wldrd	wR7,  [r0, #MMX_WR7] | 
 | 134 | 	wldrd	wR8,  [r0, #MMX_WR8] | 
 | 135 | 	wldrd	wR9,  [r0, #MMX_WR9] | 
 | 136 | 	wldrd	wR10, [r0, #MMX_WR10] | 
 | 137 | 	wldrd	wR11, [r0, #MMX_WR11] | 
 | 138 | 	wldrd	wR12, [r0, #MMX_WR12] | 
 | 139 | 	wldrd	wR13, [r0, #MMX_WR13] | 
 | 140 | 	wldrd	wR14, [r0, #MMX_WR14] | 
 | 141 | 	wldrd	wR15, [r0, #MMX_WR15] | 
 | 142 |  | 
 | 143 | 	@ Load wCx | 
 | 144 | 	wldrw	wCSSF, [r0, #MMX_WCSSF] | 
 | 145 | 	wldrw	wCASF, [r0, #MMX_WCASF] | 
 | 146 | 	wldrw	wCGR0, [r0, #MMX_WCGR0] | 
 | 147 | 	wldrw	wCGR1, [r0, #MMX_WCGR1] | 
 | 148 | 	wldrw	wCGR2, [r0, #MMX_WCGR2] | 
 | 149 | 	wldrw	wCGR3, [r0, #MMX_WCGR3] | 
 | 150 |  | 
 | 151 | 	@ clear CUP/MUP (only if r1 != 0) | 
 | 152 | 	teq	r1, #0 | 
 | 153 | 	mov 	r2, #0 | 
 | 154 | 	moveq	pc, lr | 
 | 155 | 	tmcr	wCon, r2 | 
 | 156 | 	mov	pc, lr | 
 | 157 |  | 
 | 158 | /* | 
 | 159 |  * Back up Concan regs to save area and disable access to them | 
 | 160 |  * (mainly for gdb or sleep mode usage) | 
 | 161 |  * | 
 | 162 |  * r0 = struct thread_info pointer of target task or NULL for any | 
 | 163 |  */ | 
 | 164 |  | 
 | 165 | ENTRY(iwmmxt_task_disable) | 
 | 166 |  | 
 | 167 | 	stmfd	sp!, {r4, lr} | 
 | 168 |  | 
 | 169 | 	mrs	ip, cpsr | 
 | 170 | 	orr	r2, ip, #PSR_I_BIT		@ disable interrupts | 
 | 171 | 	msr	cpsr_c, r2 | 
 | 172 |  | 
 | 173 | 	ldr	r3, =concan_owner | 
 | 174 | 	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area | 
 | 175 | 	ldr	r1, [r3]			@ get current Concan owner | 
 | 176 | 	teq	r1, #0				@ any current owner? | 
 | 177 | 	beq	1f				@ no: quit | 
 | 178 | 	teq	r0, #0				@ any owner? | 
 | 179 | 	teqne	r1, r2				@ or specified one? | 
 | 180 | 	bne	1f				@ no: quit | 
 | 181 |  | 
 | 182 | 	mrc	p15, 0, r4, c15, c1, 0 | 
 | 183 | 	orr	r4, r4, #0x3			@ enable access to CP0 and CP1 | 
 | 184 | 	mcr	p15, 0, r4, c15, c1, 0 | 
 | 185 | 	mov	r0, #0				@ nothing to load | 
 | 186 | 	str	r0, [r3]			@ no more current owner | 
 | 187 | 	mrc	p15, 0, r2, c2, c0, 0 | 
 | 188 | 	mov	r2, r2				@ cpwait | 
 | 189 | 	bl	concan_save | 
 | 190 |  | 
 | 191 | 	bic	r4, r4, #0x3			@ disable access to CP0 and CP1 | 
 | 192 | 	mcr	p15, 0, r4, c15, c1, 0 | 
 | 193 | 	mrc	p15, 0, r2, c2, c0, 0 | 
 | 194 | 	mov	r2, r2				@ cpwait | 
 | 195 |  | 
 | 196 | 1:	msr	cpsr_c, ip			@ restore interrupt mode | 
 | 197 | 	ldmfd	sp!, {r4, pc} | 
 | 198 |  | 
 | 199 | /* | 
 | 200 |  * Copy Concan state to given memory address | 
 | 201 |  * | 
 | 202 |  * r0 = struct thread_info pointer of target task | 
 | 203 |  * r1 = memory address where to store Concan state | 
 | 204 |  * | 
 | 205 |  * this is called mainly in the creation of signal stack frames | 
 | 206 |  */ | 
 | 207 |  | 
 | 208 | ENTRY(iwmmxt_task_copy) | 
 | 209 |  | 
 | 210 | 	mrs	ip, cpsr | 
 | 211 | 	orr	r2, ip, #PSR_I_BIT		@ disable interrupts | 
 | 212 | 	msr	cpsr_c, r2 | 
 | 213 |  | 
 | 214 | 	ldr	r3, =concan_owner | 
 | 215 | 	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area | 
 | 216 | 	ldr	r3, [r3]			@ get current Concan owner | 
 | 217 | 	teq	r2, r3				@ does this task own it... | 
 | 218 | 	beq	1f | 
 | 219 |  | 
 | 220 | 	@ current Concan values are in the task save area | 
 | 221 | 	msr	cpsr_c, ip			@ restore interrupt mode | 
 | 222 | 	mov	r0, r1 | 
 | 223 | 	mov	r1, r2 | 
 | 224 | 	mov	r2, #MMX_SIZE | 
 | 225 | 	b	memcpy | 
 | 226 |  | 
 | 227 | 1:	@ this task owns Concan regs -- grab a copy from there | 
 | 228 | 	mov	r0, #0				@ nothing to load | 
 | 229 | 	mov	r2, #3				@ save all regs | 
 | 230 | 	mov	r3, lr				@ preserve return address | 
 | 231 | 	bl	concan_dump | 
 | 232 | 	msr	cpsr_c, ip			@ restore interrupt mode | 
 | 233 | 	mov	pc, r3 | 
 | 234 |  | 
 | 235 | /* | 
 | 236 |  * Restore Concan state from given memory address | 
 | 237 |  * | 
 | 238 |  * r0 = struct thread_info pointer of target task | 
 | 239 |  * r1 = memory address where to get Concan state from | 
 | 240 |  * | 
 | 241 |  * this is used to restore Concan state when unwinding a signal stack frame | 
 | 242 |  */ | 
 | 243 |  | 
 | 244 | ENTRY(iwmmxt_task_restore) | 
 | 245 |  | 
 | 246 | 	mrs	ip, cpsr | 
 | 247 | 	orr	r2, ip, #PSR_I_BIT		@ disable interrupts | 
 | 248 | 	msr	cpsr_c, r2 | 
 | 249 |  | 
 | 250 | 	ldr	r3, =concan_owner | 
 | 251 | 	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area | 
 | 252 | 	ldr	r3, [r3]			@ get current Concan owner | 
 | 253 | 	bic	r2, r2, #0x7			@ 64-bit alignment | 
 | 254 | 	teq	r2, r3				@ does this task own it... | 
 | 255 | 	beq	1f | 
 | 256 |  | 
 | 257 | 	@ this task doesn't own Concan regs -- use its save area | 
 | 258 | 	msr	cpsr_c, ip			@ restore interrupt mode | 
 | 259 | 	mov	r0, r2 | 
 | 260 | 	mov	r2, #MMX_SIZE | 
 | 261 | 	b	memcpy | 
 | 262 |  | 
 | 263 | 1:	@ this task owns Concan regs -- load them directly | 
 | 264 | 	mov	r0, r1 | 
 | 265 | 	mov	r1, #0				@ don't clear CUP/MUP | 
 | 266 | 	mov	r3, lr				@ preserve return address | 
 | 267 | 	bl	concan_load | 
 | 268 | 	msr	cpsr_c, ip			@ restore interrupt mode | 
 | 269 | 	mov	pc, r3 | 
 | 270 |  | 
 | 271 | /* | 
 | 272 |  * Concan handling on task switch | 
 | 273 |  * | 
| Lennert Buytenhek | ae95bfb | 2006-07-01 19:56:48 +0100 | [diff] [blame] | 274 |  * r0 = next thread_info pointer | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 275 |  * | 
| Lennert Buytenhek | ae95bfb | 2006-07-01 19:56:48 +0100 | [diff] [blame] | 276 |  * Called only from the iwmmxt notifier with task preemption disabled. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 |  */ | 
 | 278 | ENTRY(iwmmxt_task_switch) | 
 | 279 |  | 
| Lennert Buytenhek | ae95bfb | 2006-07-01 19:56:48 +0100 | [diff] [blame] | 280 | 	mrc	p15, 0, r1, c15, c1, 0 | 
 | 281 | 	tst	r1, #0x3			@ CP0 and CP1 accessible? | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | 	bne	1f				@ yes: block them for next task | 
 | 283 |  | 
| Lennert Buytenhek | ae95bfb | 2006-07-01 19:56:48 +0100 | [diff] [blame] | 284 | 	ldr	r2, =concan_owner | 
 | 285 | 	add	r3, r0, #TI_IWMMXT_STATE	@ get next task Concan save area | 
 | 286 | 	ldr	r2, [r2]			@ get current Concan owner | 
 | 287 | 	teq	r2, r3				@ next task owns it? | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 288 | 	movne	pc, lr				@ no: leave Concan disabled | 
 | 289 |  | 
| Lennert Buytenhek | ae95bfb | 2006-07-01 19:56:48 +0100 | [diff] [blame] | 290 | 1:	eor	r1, r1, #3			@ flip Concan access | 
 | 291 | 	mcr	p15, 0, r1, c15, c1, 0 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 292 |  | 
| Lennert Buytenhek | ae95bfb | 2006-07-01 19:56:48 +0100 | [diff] [blame] | 293 | 	mrc	p15, 0, r1, c2, c0, 0 | 
 | 294 | 	sub	pc, lr, r1, lsr #32		@ cpwait and return | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 |  | 
 | 296 | /* | 
 | 297 |  * Remove Concan ownership of given task | 
 | 298 |  * | 
 | 299 |  * r0 = struct thread_info pointer | 
 | 300 |  */ | 
 | 301 | ENTRY(iwmmxt_task_release) | 
 | 302 |  | 
 | 303 | 	mrs	r2, cpsr | 
 | 304 | 	orr	ip, r2, #PSR_I_BIT		@ disable interrupts | 
 | 305 | 	msr	cpsr_c, ip | 
 | 306 | 	ldr	r3, =concan_owner | 
 | 307 | 	add	r0, r0, #TI_IWMMXT_STATE	@ get task Concan save area | 
 | 308 | 	ldr	r1, [r3]			@ get current Concan owner | 
 | 309 | 	eors	r0, r0, r1			@ if equal... | 
 | 310 | 	streq	r0, [r3]			@ then clear ownership | 
 | 311 | 	msr	cpsr_c, r2			@ restore interrupts | 
 | 312 | 	mov	pc, lr | 
 | 313 |  | 
 | 314 | 	.data | 
 | 315 | concan_owner: | 
 | 316 | 	.word	0 | 
 | 317 |  |