| /* | 
 |  * Copyright (C) 2005 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include <machine/cpu-features.h> | 
 |  | 
 | /* | 
 |  * NOTE: these atomic operations are SMP safe on all architectures.  | 
 |  */ | 
 |  | 
 | 	.text | 
 | 	.align | 
 | 	 | 
 |     .global android_atomic_write | 
 |     .type android_atomic_write, %function | 
 |  | 
 | 	.global android_atomic_inc | 
 | 	.type android_atomic_inc, %function | 
 | 	.global android_atomic_dec | 
 | 	.type android_atomic_dec, %function | 
 |      | 
 | 	.global android_atomic_add | 
 | 	.type android_atomic_add, %function | 
 | 	.global android_atomic_and | 
 | 	.type android_atomic_and, %function | 
 | 	.global android_atomic_or | 
 | 	.type android_atomic_or, %function | 
 |      | 
 |     .global android_atomic_swap | 
 |     .type android_atomic_swap, %function | 
 | 	 | 
 | 	.global android_atomic_cmpxchg | 
 | 	.type android_atomic_cmpxchg, %function | 
 |  | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * int __kernel_cmpxchg(int oldval, int newval, int *ptr) | 
 |  * clobbered: r3, ip, flags | 
 |  * return 0 if a swap was made, non-zero otherwise. | 
 |  */  | 
 |  | 
 |    .equ     kernel_cmpxchg, 0xFFFF0FC0 | 
 |    .equ     kernel_atomic_base, 0xFFFF0FFF | 
 |  | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_write | 
 |  * input: r0=value, r1=address | 
 |  * output: void | 
 |  */ | 
 |   | 
 | android_atomic_write: | 
 |     str     r0, [r1] | 
 |     bx      lr; | 
 |  | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_inc | 
 |  * input: r0 = address | 
 |  * output: r0 = old value | 
 |  */ | 
 |   | 
 | android_atomic_inc: | 
 |     .fnstart | 
 |     .save {r4, lr} | 
 |     stmdb   sp!, {r4, lr} | 
 |     mov     r2, r0 | 
 | 1: @ android_atomic_inc | 
 |     ldr     r0, [r2] | 
 |     mov     r3, #kernel_atomic_base | 
 | #ifdef __ARM_HAVE_PC_INTERWORK | 
 |     add     lr, pc, #4 | 
 |     add     r1, r0, #1 | 
 |     add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 | #else | 
 |     add     r1, r0, #1 | 
 |     add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 |     mov     lr, pc | 
 |     bx      r3 | 
 | #endif | 
 |     bcc     1b | 
 |     sub     r0, r1, #1 | 
 |     ldmia   sp!, {r4, lr} | 
 |     bx      lr | 
 |     .fnend | 
 |    | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_dec | 
 |  * input: r0=address | 
 |  * output: r0 = old value | 
 |  */ | 
 |   | 
 | android_atomic_dec: | 
 |     .fnstart | 
 |     .save {r4, lr} | 
 |     stmdb   sp!, {r4, lr} | 
 |     mov     r2, r0 | 
 | 1: @ android_atomic_dec | 
 |     ldr     r0, [r2] | 
 |     mov     r3, #kernel_atomic_base | 
 | #ifdef __ARM_HAVE_PC_INTERWORK | 
 |     add     lr, pc, #4 | 
 |     sub     r1, r0, #1 | 
 |     add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 | #else | 
 |     sub     r1, r0, #1 | 
 |     add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 |     mov     lr, pc | 
 |     bx      r3 | 
 | #endif | 
 |     bcc     1b | 
 |     add     r0, r1, #1 | 
 |     ldmia   sp!, {r4, lr} | 
 |     bx      lr | 
 |     .fnend | 
 |      | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_add | 
 |  * input: r0=value, r1=address | 
 |  * output: r0 = old value | 
 |  */ | 
 |  | 
 | android_atomic_add: | 
 |     .fnstart | 
 |     .save {r4, lr} | 
 |     stmdb   sp!, {r4, lr} | 
 |     mov     r2, r1 | 
 |     mov     r4, r0 | 
 | 1: @ android_atomic_add | 
 |     ldr     r0, [r2] | 
 |     mov     r3, #kernel_atomic_base | 
 | #ifdef __ARM_HAVE_PC_INTERWORK | 
 |     add     lr, pc, #4 | 
 |     add     r1, r0, r4 | 
 |     add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 | #else | 
 |     add     r1, r0, r4 | 
 |     add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 |     mov     lr, pc | 
 |     bx      r3 | 
 | #endif | 
 |     bcc     1b | 
 |     sub     r0, r1, r4 | 
 |     ldmia   sp!, {r4, lr} | 
 |     bx      lr | 
 |     .fnend | 
 |      | 
 |      | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_and | 
 |  * input: r0=value, r1=address | 
 |  * output: r0 = old value | 
 |  */ | 
 |  | 
 | android_atomic_and: | 
 |     .fnstart | 
 |     .save {r4, r5, lr} | 
 |     stmdb   sp!, {r4, r5, lr}    | 
 |     mov     r2, r1              /* r2 = address */ | 
 |     mov     r4, r0              /* r4 = the value */ | 
 | 1: @ android_atomic_and | 
 |     ldr     r0, [r2]            /* r0 = address[0] */ | 
 |     mov     r3, #kernel_atomic_base | 
 | #ifdef __ARM_HAVE_PC_INTERWORK | 
 |     add     lr, pc, #8 | 
 |     mov     r5, r0              /* r5 = save address[0] */ | 
 |     and     r1, r0, r4          /* r1 = new value */ | 
 |     add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */ | 
 | #else | 
 |     mov     r5, r0              /* r5 = save address[0] */ | 
 |     and     r1, r0, r4          /* r1 = new value */ | 
 |     add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */ | 
 |     mov     lr, pc | 
 |     bx      r3 | 
 | #endif | 
 |     bcc     1b | 
 |     mov     r0, r5 | 
 |     ldmia   sp!, {r4, r5, lr} | 
 |     bx      lr | 
 |     .fnend | 
 |      | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_or | 
 |  * input: r0=value, r1=address | 
 |  * output: r0 = old value | 
 |  */ | 
 |  | 
 | android_atomic_or: | 
 |     .fnstart | 
 |     .save {r4, r5, lr} | 
 |     stmdb   sp!, {r4, r5, lr}    | 
 |     mov     r2, r1              /* r2 = address */ | 
 |     mov     r4, r0              /* r4 = the value */ | 
 | 1: @ android_atomic_or | 
 |     ldr     r0, [r2]            /* r0 = address[0] */ | 
 |     mov     r3, #kernel_atomic_base | 
 | #ifdef __ARM_HAVE_PC_INTERWORK | 
 |     add     lr, pc, #8 | 
 |     mov     r5, r0              /* r5 = save address[0] */ | 
 |     orr     r1, r0, r4          /* r1 = new value */ | 
 |     add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */ | 
 | #else | 
 |     mov     r5, r0              /* r5 = save address[0] */ | 
 |     orr     r1, r0, r4          /* r1 = new value */ | 
 |     add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */ | 
 |     mov     lr, pc | 
 |     bx      r3 | 
 | #endif | 
 |     bcc     1b | 
 |     mov     r0, r5 | 
 |     ldmia   sp!, {r4, r5, lr} | 
 |     bx      lr | 
 |     .fnend | 
 |  | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_swap | 
 |  * input: r0=value, r1=address | 
 |  * output: r0 = old value | 
 |  */ | 
 |  | 
 | /* replaced swp instruction with ldrex/strex for ARMv6 & ARMv7 */ | 
 | android_atomic_swap: | 
 | #if defined (_ARM_HAVE_LDREX_STREX) | 
 | 1:  ldrex   r2, [r1] | 
 |     strex   r3, r0, [r1] | 
 |     teq     r3, #0 | 
 |     bne     1b | 
 |     mov     r0, r2 | 
 |     mcr     p15, 0, r0, c7, c10, 5 /* or, use dmb */ | 
 | #else | 
 |     swp     r0, r0, [r1] | 
 | #endif | 
 |     bx      lr | 
 |  | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_cmpxchg | 
 |  * input: r0=oldvalue, r1=newvalue, r2=address | 
 |  * output: r0 = 0 (xchg done) or non-zero (xchg not done) | 
 |  */ | 
 |  | 
 | android_atomic_cmpxchg: | 
 |     .fnstart | 
 |     .save {r4, lr} | 
 |     stmdb   sp!, {r4, lr} | 
 |     mov     r4, r0          /* r4 = save oldvalue */ | 
 | 1: @ android_atomic_cmpxchg | 
 |     mov     r3, #kernel_atomic_base | 
 | #ifdef __ARM_HAVE_PC_INTERWORK | 
 |     add     lr, pc, #4 | 
 |     mov     r0, r4          /* r0 = oldvalue */ | 
 |     add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 | #else | 
 |     mov     r0, r4          /* r0 = oldvalue */ | 
 |     add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base) | 
 |     mov     lr, pc | 
 |     bx      r3 | 
 | #endif | 
 |     bcs     2f              /* swap was made. we're good, return. */ | 
 |     ldr     r3, [r2]        /* swap not made, see if it's because *ptr!=oldvalue */ | 
 |     cmp     r3, r4 | 
 |     beq     1b | 
 | 2: @ android_atomic_cmpxchg | 
 |     ldmia   sp!, {r4, lr} | 
 |     bx      lr | 
 |     .fnend | 
 |  | 
 | /* | 
 |  * ---------------------------------------------------------------------------- | 
 |  * android_atomic_cmpxchg_64 | 
 |  * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address | 
 |  * output: r0 = 0 (xchg done) or non-zero (xchg not done) | 
 |  */ | 
 | /* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */ |