blob: da5c26b06d25ac1afd97b6794557ed31d456f3a1 [file] [log] [blame]
/*
* 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
.global android_atomic_inc
.global android_atomic_dec
.global android_atomic_add
.global android_atomic_and
.global android_atomic_or
.global android_atomic_swap
.global android_atomic_cmpxchg
/*
* ----------------------------------------------------------------------------
* 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 */