1/*
2 * Copyright (C) 2005 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <machine/cpu-features.h>
18
19/*
20 * NOTE: these atomic operations are SMP safe on all architectures.
21 */
22
23	.text
24	.align
25
26    .global android_atomic_write
27    .type android_atomic_write, %function
28
29	.global android_atomic_inc
30	.type android_atomic_inc, %function
31	.global android_atomic_dec
32	.type android_atomic_dec, %function
33
34	.global android_atomic_add
35	.type android_atomic_add, %function
36	.global android_atomic_and
37	.type android_atomic_and, %function
38	.global android_atomic_or
39	.type android_atomic_or, %function
40
41    .global android_atomic_swap
42    .type android_atomic_swap, %function
43
44	.global android_atomic_cmpxchg
45	.type android_atomic_cmpxchg, %function
46
47/*
48 * ----------------------------------------------------------------------------
49 * int __kernel_cmpxchg(int oldval, int newval, int *ptr)
50 * clobbered: r3, ip, flags
51 * return 0 if a swap was made, non-zero otherwise.
52 */
53
54   .equ     kernel_cmpxchg, 0xFFFF0FC0
55   .equ     kernel_atomic_base, 0xFFFF0FFF
56
57/*
58 * ----------------------------------------------------------------------------
59 * android_atomic_write
60 * input: r0=value, r1=address
61 * output: void
62 */
63
64android_atomic_write:
65    str     r0, [r1]
66    bx      lr;
67
68/*
69 * ----------------------------------------------------------------------------
70 * android_atomic_inc
71 * input: r0 = address
72 * output: r0 = old value
73 */
74
75android_atomic_inc:
76    .fnstart
77    .save {r4, lr}
78    stmdb   sp!, {r4, lr}
79    mov     r2, r0
801: @ android_atomic_inc
81    ldr     r0, [r2]
82    mov     r3, #kernel_atomic_base
83#ifdef __ARM_HAVE_PC_INTERWORK
84    add     lr, pc, #4
85    add     r1, r0, #1
86    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
87#else
88    add     r1, r0, #1
89    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
90    mov     lr, pc
91    bx      r3
92#endif
93    bcc     1b
94    sub     r0, r1, #1
95    ldmia   sp!, {r4, lr}
96    bx      lr
97    .fnend
98
99/*
100 * ----------------------------------------------------------------------------
101 * android_atomic_dec
102 * input: r0=address
103 * output: r0 = old value
104 */
105
106android_atomic_dec:
107    .fnstart
108    .save {r4, lr}
109    stmdb   sp!, {r4, lr}
110    mov     r2, r0
1111: @ android_atomic_dec
112    ldr     r0, [r2]
113    mov     r3, #kernel_atomic_base
114#ifdef __ARM_HAVE_PC_INTERWORK
115    add     lr, pc, #4
116    sub     r1, r0, #1
117    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
118#else
119    sub     r1, r0, #1
120    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
121    mov     lr, pc
122    bx      r3
123#endif
124    bcc     1b
125    add     r0, r1, #1
126    ldmia   sp!, {r4, lr}
127    bx      lr
128    .fnend
129
130/*
131 * ----------------------------------------------------------------------------
132 * android_atomic_add
133 * input: r0=value, r1=address
134 * output: r0 = old value
135 */
136
137android_atomic_add:
138    .fnstart
139    .save {r4, lr}
140    stmdb   sp!, {r4, lr}
141    mov     r2, r1
142    mov     r4, r0
1431: @ android_atomic_add
144    ldr     r0, [r2]
145    mov     r3, #kernel_atomic_base
146#ifdef __ARM_HAVE_PC_INTERWORK
147    add     lr, pc, #4
148    add     r1, r0, r4
149    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
150#else
151    add     r1, r0, r4
152    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
153    mov     lr, pc
154    bx      r3
155#endif
156    bcc     1b
157    sub     r0, r1, r4
158    ldmia   sp!, {r4, lr}
159    bx      lr
160    .fnend
161
162
163/*
164 * ----------------------------------------------------------------------------
165 * android_atomic_and
166 * input: r0=value, r1=address
167 * output: r0 = old value
168 */
169
170android_atomic_and:
171    .fnstart
172    .save {r4, r5, lr}
173    stmdb   sp!, {r4, r5, lr}
174    mov     r2, r1              /* r2 = address */
175    mov     r4, r0              /* r4 = the value */
1761: @ android_atomic_and
177    ldr     r0, [r2]            /* r0 = address[0] */
178    mov     r3, #kernel_atomic_base
179#ifdef __ARM_HAVE_PC_INTERWORK
180    add     lr, pc, #8
181    mov     r5, r0              /* r5 = save address[0] */
182    and     r1, r0, r4          /* r1 = new value */
183    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
184#else
185    mov     r5, r0              /* r5 = save address[0] */
186    and     r1, r0, r4          /* r1 = new value */
187    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
188    mov     lr, pc
189    bx      r3
190#endif
191    bcc     1b
192    mov     r0, r5
193    ldmia   sp!, {r4, r5, lr}
194    bx      lr
195    .fnend
196
197/*
198 * ----------------------------------------------------------------------------
199 * android_atomic_or
200 * input: r0=value, r1=address
201 * output: r0 = old value
202 */
203
204android_atomic_or:
205    .fnstart
206    .save {r4, r5, lr}
207    stmdb   sp!, {r4, r5, lr}
208    mov     r2, r1              /* r2 = address */
209    mov     r4, r0              /* r4 = the value */
2101: @ android_atomic_or
211    ldr     r0, [r2]            /* r0 = address[0] */
212    mov     r3, #kernel_atomic_base
213#ifdef __ARM_HAVE_PC_INTERWORK
214    add     lr, pc, #8
215    mov     r5, r0              /* r5 = save address[0] */
216    orr     r1, r0, r4          /* r1 = new value */
217    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
218#else
219    mov     r5, r0              /* r5 = save address[0] */
220    orr     r1, r0, r4          /* r1 = new value */
221    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
222    mov     lr, pc
223    bx      r3
224#endif
225    bcc     1b
226    mov     r0, r5
227    ldmia   sp!, {r4, r5, lr}
228    bx      lr
229    .fnend
230
231/*
232 * ----------------------------------------------------------------------------
233 * android_atomic_swap
234 * input: r0=value, r1=address
235 * output: r0 = old value
236 */
237
238/* replaced swp instruction with ldrex/strex for ARMv6 & ARMv7 */
239android_atomic_swap:
240#if defined (_ARM_HAVE_LDREX_STREX)
2411:  ldrex   r2, [r1]
242    strex   r3, r0, [r1]
243    teq     r3, #0
244    bne     1b
245    mov     r0, r2
246    mcr     p15, 0, r0, c7, c10, 5 /* or, use dmb */
247#else
248    swp     r0, r0, [r1]
249#endif
250    bx      lr
251
252/*
253 * ----------------------------------------------------------------------------
254 * android_atomic_cmpxchg
255 * input: r0=oldvalue, r1=newvalue, r2=address
256 * output: r0 = 0 (xchg done) or non-zero (xchg not done)
257 */
258
259android_atomic_cmpxchg:
260    .fnstart
261    .save {r4, lr}
262    stmdb   sp!, {r4, lr}
263    mov     r4, r0          /* r4 = save oldvalue */
2641: @ android_atomic_cmpxchg
265    mov     r3, #kernel_atomic_base
266#ifdef __ARM_HAVE_PC_INTERWORK
267    add     lr, pc, #4
268    mov     r0, r4          /* r0 = oldvalue */
269    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
270#else
271    mov     r0, r4          /* r0 = oldvalue */
272    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
273    mov     lr, pc
274    bx      r3
275#endif
276    bcs     2f              /* swap was made. we're good, return. */
277    ldr     r3, [r2]        /* swap not made, see if it's because *ptr!=oldvalue */
278    cmp     r3, r4
279    beq     1b
2802: @ android_atomic_cmpxchg
281    ldmia   sp!, {r4, lr}
282    bx      lr
283    .fnend
284
285/*
286 * ----------------------------------------------------------------------------
287 * android_atomic_cmpxchg_64
288 * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
289 * output: r0 = 0 (xchg done) or non-zero (xchg not done)
290 */
291/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
292