atomic.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /*
  2. * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. */
  8. #ifndef _ASM_ARC_ATOMIC_H
  9. #define _ASM_ARC_ATOMIC_H
  10. #ifndef __ASSEMBLY__
  11. #include <linux/types.h>
  12. #include <linux/compiler.h>
  13. #include <asm/cmpxchg.h>
  14. #include <asm/barrier.h>
  15. #include <asm/smp.h>
  16. #define ATOMIC_INIT(i) { (i) }
  17. #ifndef CONFIG_ARC_PLAT_EZNPS
  18. #define atomic_read(v) READ_ONCE((v)->counter)
  19. #ifdef CONFIG_ARC_HAS_LLSC
  20. #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
  21. #define ATOMIC_OP(op, c_op, asm_op) \
  22. static inline void atomic_##op(int i, atomic_t *v) \
  23. { \
  24. unsigned int val; \
  25. \
  26. __asm__ __volatile__( \
  27. "1: llock %[val], [%[ctr]] \n" \
  28. " " #asm_op " %[val], %[val], %[i] \n" \
  29. " scond %[val], [%[ctr]] \n" \
  30. " bnz 1b \n" \
  31. : [val] "=&r" (val) /* Early clobber to prevent reg reuse */ \
  32. : [ctr] "r" (&v->counter), /* Not "m": llock only supports reg direct addr mode */ \
  33. [i] "ir" (i) \
  34. : "cc"); \
  35. } \
  36. #define ATOMIC_OP_RETURN(op, c_op, asm_op) \
  37. static inline int atomic_##op##_return(int i, atomic_t *v) \
  38. { \
  39. unsigned int val; \
  40. \
  41. /* \
  42. * Explicit full memory barrier needed before/after as \
  43. * LLOCK/SCOND thmeselves don't provide any such semantics \
  44. */ \
  45. smp_mb(); \
  46. \
  47. __asm__ __volatile__( \
  48. "1: llock %[val], [%[ctr]] \n" \
  49. " " #asm_op " %[val], %[val], %[i] \n" \
  50. " scond %[val], [%[ctr]] \n" \
  51. " bnz 1b \n" \
  52. : [val] "=&r" (val) \
  53. : [ctr] "r" (&v->counter), \
  54. [i] "ir" (i) \
  55. : "cc"); \
  56. \
  57. smp_mb(); \
  58. \
  59. return val; \
  60. }
  61. #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
  62. static inline int atomic_fetch_##op(int i, atomic_t *v) \
  63. { \
  64. unsigned int val, orig; \
  65. \
  66. /* \
  67. * Explicit full memory barrier needed before/after as \
  68. * LLOCK/SCOND thmeselves don't provide any such semantics \
  69. */ \
  70. smp_mb(); \
  71. \
  72. __asm__ __volatile__( \
  73. "1: llock %[orig], [%[ctr]] \n" \
  74. " " #asm_op " %[val], %[orig], %[i] \n" \
  75. " scond %[val], [%[ctr]] \n" \
  76. " bnz 1b \n" \
  77. : [val] "=&r" (val), \
  78. [orig] "=&r" (orig) \
  79. : [ctr] "r" (&v->counter), \
  80. [i] "ir" (i) \
  81. : "cc"); \
  82. \
  83. smp_mb(); \
  84. \
  85. return orig; \
  86. }
  87. #else /* !CONFIG_ARC_HAS_LLSC */
  88. #ifndef CONFIG_SMP
  89. /* violating atomic_xxx API locking protocol in UP for optimization sake */
  90. #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
  91. #else
  92. static inline void atomic_set(atomic_t *v, int i)
  93. {
  94. /*
  95. * Independent of hardware support, all of the atomic_xxx() APIs need
  96. * to follow the same locking rules to make sure that a "hardware"
  97. * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
  98. * sequence
  99. *
  100. * Thus atomic_set() despite being 1 insn (and seemingly atomic)
  101. * requires the locking.
  102. */
  103. unsigned long flags;
  104. atomic_ops_lock(flags);
  105. WRITE_ONCE(v->counter, i);
  106. atomic_ops_unlock(flags);
  107. }
  108. #define atomic_set_release(v, i) atomic_set((v), (i))
  109. #endif
  110. /*
  111. * Non hardware assisted Atomic-R-M-W
  112. * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
  113. */
  114. #define ATOMIC_OP(op, c_op, asm_op) \
  115. static inline void atomic_##op(int i, atomic_t *v) \
  116. { \
  117. unsigned long flags; \
  118. \
  119. atomic_ops_lock(flags); \
  120. v->counter c_op i; \
  121. atomic_ops_unlock(flags); \
  122. }
  123. #define ATOMIC_OP_RETURN(op, c_op, asm_op) \
  124. static inline int atomic_##op##_return(int i, atomic_t *v) \
  125. { \
  126. unsigned long flags; \
  127. unsigned long temp; \
  128. \
  129. /* \
  130. * spin lock/unlock provides the needed smp_mb() before/after \
  131. */ \
  132. atomic_ops_lock(flags); \
  133. temp = v->counter; \
  134. temp c_op i; \
  135. v->counter = temp; \
  136. atomic_ops_unlock(flags); \
  137. \
  138. return temp; \
  139. }
  140. #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
  141. static inline int atomic_fetch_##op(int i, atomic_t *v) \
  142. { \
  143. unsigned long flags; \
  144. unsigned long orig; \
  145. \
  146. /* \
  147. * spin lock/unlock provides the needed smp_mb() before/after \
  148. */ \
  149. atomic_ops_lock(flags); \
  150. orig = v->counter; \
  151. v->counter c_op i; \
  152. atomic_ops_unlock(flags); \
  153. \
  154. return orig; \
  155. }
  156. #endif /* !CONFIG_ARC_HAS_LLSC */
  157. #define ATOMIC_OPS(op, c_op, asm_op) \
  158. ATOMIC_OP(op, c_op, asm_op) \
  159. ATOMIC_OP_RETURN(op, c_op, asm_op) \
  160. ATOMIC_FETCH_OP(op, c_op, asm_op)
  161. ATOMIC_OPS(add, +=, add)
  162. ATOMIC_OPS(sub, -=, sub)
  163. #define atomic_andnot atomic_andnot
  164. #define atomic_fetch_andnot atomic_fetch_andnot
  165. #undef ATOMIC_OPS
  166. #define ATOMIC_OPS(op, c_op, asm_op) \
  167. ATOMIC_OP(op, c_op, asm_op) \
  168. ATOMIC_FETCH_OP(op, c_op, asm_op)
  169. ATOMIC_OPS(and, &=, and)
  170. ATOMIC_OPS(andnot, &= ~, bic)
  171. ATOMIC_OPS(or, |=, or)
  172. ATOMIC_OPS(xor, ^=, xor)
  173. #else /* CONFIG_ARC_PLAT_EZNPS */
  174. static inline int atomic_read(const atomic_t *v)
  175. {
  176. int temp;
  177. __asm__ __volatile__(
  178. " ld.di %0, [%1]"
  179. : "=r"(temp)
  180. : "r"(&v->counter)
  181. : "memory");
  182. return temp;
  183. }
  184. static inline void atomic_set(atomic_t *v, int i)
  185. {
  186. __asm__ __volatile__(
  187. " st.di %0,[%1]"
  188. :
  189. : "r"(i), "r"(&v->counter)
  190. : "memory");
  191. }
  192. #define ATOMIC_OP(op, c_op, asm_op) \
  193. static inline void atomic_##op(int i, atomic_t *v) \
  194. { \
  195. __asm__ __volatile__( \
  196. " mov r2, %0\n" \
  197. " mov r3, %1\n" \
  198. " .word %2\n" \
  199. : \
  200. : "r"(i), "r"(&v->counter), "i"(asm_op) \
  201. : "r2", "r3", "memory"); \
  202. } \
  203. #define ATOMIC_OP_RETURN(op, c_op, asm_op) \
  204. static inline int atomic_##op##_return(int i, atomic_t *v) \
  205. { \
  206. unsigned int temp = i; \
  207. \
  208. /* Explicit full memory barrier needed before/after */ \
  209. smp_mb(); \
  210. \
  211. __asm__ __volatile__( \
  212. " mov r2, %0\n" \
  213. " mov r3, %1\n" \
  214. " .word %2\n" \
  215. " mov %0, r2" \
  216. : "+r"(temp) \
  217. : "r"(&v->counter), "i"(asm_op) \
  218. : "r2", "r3", "memory"); \
  219. \
  220. smp_mb(); \
  221. \
  222. temp c_op i; \
  223. \
  224. return temp; \
  225. }
  226. #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
  227. static inline int atomic_fetch_##op(int i, atomic_t *v) \
  228. { \
  229. unsigned int temp = i; \
  230. \
  231. /* Explicit full memory barrier needed before/after */ \
  232. smp_mb(); \
  233. \
  234. __asm__ __volatile__( \
  235. " mov r2, %0\n" \
  236. " mov r3, %1\n" \
  237. " .word %2\n" \
  238. " mov %0, r2" \
  239. : "+r"(temp) \
  240. : "r"(&v->counter), "i"(asm_op) \
  241. : "r2", "r3", "memory"); \
  242. \
  243. smp_mb(); \
  244. \
  245. return temp; \
  246. }
  247. #define ATOMIC_OPS(op, c_op, asm_op) \
  248. ATOMIC_OP(op, c_op, asm_op) \
  249. ATOMIC_OP_RETURN(op, c_op, asm_op) \
  250. ATOMIC_FETCH_OP(op, c_op, asm_op)
  251. ATOMIC_OPS(add, +=, CTOP_INST_AADD_DI_R2_R2_R3)
  252. #define atomic_sub(i, v) atomic_add(-(i), (v))
  253. #define atomic_sub_return(i, v) atomic_add_return(-(i), (v))
  254. #define atomic_fetch_sub(i, v) atomic_fetch_add(-(i), (v))
  255. #undef ATOMIC_OPS
  256. #define ATOMIC_OPS(op, c_op, asm_op) \
  257. ATOMIC_OP(op, c_op, asm_op) \
  258. ATOMIC_FETCH_OP(op, c_op, asm_op)
  259. ATOMIC_OPS(and, &=, CTOP_INST_AAND_DI_R2_R2_R3)
  260. ATOMIC_OPS(or, |=, CTOP_INST_AOR_DI_R2_R2_R3)
  261. ATOMIC_OPS(xor, ^=, CTOP_INST_AXOR_DI_R2_R2_R3)
  262. #endif /* CONFIG_ARC_PLAT_EZNPS */
  263. #undef ATOMIC_OPS
  264. #undef ATOMIC_FETCH_OP
  265. #undef ATOMIC_OP_RETURN
  266. #undef ATOMIC_OP
  267. #ifdef CONFIG_GENERIC_ATOMIC64
  268. #include <asm-generic/atomic64.h>
  269. #else /* Kconfig ensures this is only enabled with needed h/w assist */
  270. /*
  271. * ARCv2 supports 64-bit exclusive load (LLOCKD) / store (SCONDD)
  272. * - The address HAS to be 64-bit aligned
  273. * - There are 2 semantics involved here:
  274. * = exclusive implies no interim update between load/store to same addr
  275. * = both words are observed/updated together: this is guaranteed even
  276. * for regular 64-bit load (LDD) / store (STD). Thus atomic64_set()
  277. * is NOT required to use LLOCKD+SCONDD, STD suffices
  278. */
  279. typedef struct {
  280. aligned_u64 counter;
  281. } atomic64_t;
  282. #define ATOMIC64_INIT(a) { (a) }
  283. static inline long long atomic64_read(const atomic64_t *v)
  284. {
  285. unsigned long long val;
  286. __asm__ __volatile__(
  287. " ldd %0, [%1] \n"
  288. : "=r"(val)
  289. : "r"(&v->counter));
  290. return val;
  291. }
  292. static inline void atomic64_set(atomic64_t *v, long long a)
  293. {
  294. /*
  295. * This could have been a simple assignment in "C" but would need
  296. * explicit volatile. Otherwise gcc optimizers could elide the store
  297. * which borked atomic64 self-test
  298. * In the inline asm version, memory clobber needed for exact same
  299. * reason, to tell gcc about the store.
  300. *
  301. * This however is not needed for sibling atomic64_add() etc since both
  302. * load/store are explicitly done in inline asm. As long as API is used
  303. * for each access, gcc has no way to optimize away any load/store
  304. */
  305. __asm__ __volatile__(
  306. " std %0, [%1] \n"
  307. :
  308. : "r"(a), "r"(&v->counter)
  309. : "memory");
  310. }
  311. #define ATOMIC64_OP(op, op1, op2) \
  312. static inline void atomic64_##op(long long a, atomic64_t *v) \
  313. { \
  314. unsigned long long val; \
  315. \
  316. __asm__ __volatile__( \
  317. "1: \n" \
  318. " llockd %0, [%1] \n" \
  319. " " #op1 " %L0, %L0, %L2 \n" \
  320. " " #op2 " %H0, %H0, %H2 \n" \
  321. " scondd %0, [%1] \n" \
  322. " bnz 1b \n" \
  323. : "=&r"(val) \
  324. : "r"(&v->counter), "ir"(a) \
  325. : "cc"); \
  326. } \
  327. #define ATOMIC64_OP_RETURN(op, op1, op2) \
  328. static inline long long atomic64_##op##_return(long long a, atomic64_t *v) \
  329. { \
  330. unsigned long long val; \
  331. \
  332. smp_mb(); \
  333. \
  334. __asm__ __volatile__( \
  335. "1: \n" \
  336. " llockd %0, [%1] \n" \
  337. " " #op1 " %L0, %L0, %L2 \n" \
  338. " " #op2 " %H0, %H0, %H2 \n" \
  339. " scondd %0, [%1] \n" \
  340. " bnz 1b \n" \
  341. : [val] "=&r"(val) \
  342. : "r"(&v->counter), "ir"(a) \
  343. : "cc"); /* memory clobber comes from smp_mb() */ \
  344. \
  345. smp_mb(); \
  346. \
  347. return val; \
  348. }
  349. #define ATOMIC64_FETCH_OP(op, op1, op2) \
  350. static inline long long atomic64_fetch_##op(long long a, atomic64_t *v) \
  351. { \
  352. unsigned long long val, orig; \
  353. \
  354. smp_mb(); \
  355. \
  356. __asm__ __volatile__( \
  357. "1: \n" \
  358. " llockd %0, [%2] \n" \
  359. " " #op1 " %L1, %L0, %L3 \n" \
  360. " " #op2 " %H1, %H0, %H3 \n" \
  361. " scondd %1, [%2] \n" \
  362. " bnz 1b \n" \
  363. : "=&r"(orig), "=&r"(val) \
  364. : "r"(&v->counter), "ir"(a) \
  365. : "cc"); /* memory clobber comes from smp_mb() */ \
  366. \
  367. smp_mb(); \
  368. \
  369. return orig; \
  370. }
  371. #define ATOMIC64_OPS(op, op1, op2) \
  372. ATOMIC64_OP(op, op1, op2) \
  373. ATOMIC64_OP_RETURN(op, op1, op2) \
  374. ATOMIC64_FETCH_OP(op, op1, op2)
  375. #define atomic64_andnot atomic64_andnot
  376. #define atomic64_fetch_andnot atomic64_fetch_andnot
  377. ATOMIC64_OPS(add, add.f, adc)
  378. ATOMIC64_OPS(sub, sub.f, sbc)
  379. ATOMIC64_OPS(and, and, and)
  380. ATOMIC64_OPS(andnot, bic, bic)
  381. ATOMIC64_OPS(or, or, or)
  382. ATOMIC64_OPS(xor, xor, xor)
  383. #undef ATOMIC64_OPS
  384. #undef ATOMIC64_FETCH_OP
  385. #undef ATOMIC64_OP_RETURN
  386. #undef ATOMIC64_OP
  387. static inline long long
  388. atomic64_cmpxchg(atomic64_t *ptr, long long expected, long long new)
  389. {
  390. long long prev;
  391. smp_mb();
  392. __asm__ __volatile__(
  393. "1: llockd %0, [%1] \n"
  394. " brne %L0, %L2, 2f \n"
  395. " brne %H0, %H2, 2f \n"
  396. " scondd %3, [%1] \n"
  397. " bnz 1b \n"
  398. "2: \n"
  399. : "=&r"(prev)
  400. : "r"(ptr), "ir"(expected), "r"(new)
  401. : "cc"); /* memory clobber comes from smp_mb() */
  402. smp_mb();
  403. return prev;
  404. }
  405. static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
  406. {
  407. long long prev;
  408. smp_mb();
  409. __asm__ __volatile__(
  410. "1: llockd %0, [%1] \n"
  411. " scondd %2, [%1] \n"
  412. " bnz 1b \n"
  413. "2: \n"
  414. : "=&r"(prev)
  415. : "r"(ptr), "r"(new)
  416. : "cc"); /* memory clobber comes from smp_mb() */
  417. smp_mb();
  418. return prev;
  419. }
  420. /**
  421. * atomic64_dec_if_positive - decrement by 1 if old value positive
  422. * @v: pointer of type atomic64_t
  423. *
  424. * The function returns the old value of *v minus 1, even if
  425. * the atomic variable, v, was not decremented.
  426. */
  427. static inline long long atomic64_dec_if_positive(atomic64_t *v)
  428. {
  429. long long val;
  430. smp_mb();
  431. __asm__ __volatile__(
  432. "1: llockd %0, [%1] \n"
  433. " sub.f %L0, %L0, 1 # w0 - 1, set C on borrow\n"
  434. " sub.c %H0, %H0, 1 # if C set, w1 - 1\n"
  435. " brlt %H0, 0, 2f \n"
  436. " scondd %0, [%1] \n"
  437. " bnz 1b \n"
  438. "2: \n"
  439. : "=&r"(val)
  440. : "r"(&v->counter)
  441. : "cc"); /* memory clobber comes from smp_mb() */
  442. smp_mb();
  443. return val;
  444. }
  445. #define atomic64_dec_if_positive atomic64_dec_if_positive
  446. /**
  447. * atomic64_fetch_add_unless - add unless the number is a given value
  448. * @v: pointer of type atomic64_t
  449. * @a: the amount to add to v...
  450. * @u: ...unless v is equal to u.
  451. *
  452. * Atomically adds @a to @v, if it was not @u.
  453. * Returns the old value of @v
  454. */
  455. static inline long long atomic64_fetch_add_unless(atomic64_t *v, long long a,
  456. long long u)
  457. {
  458. long long old, temp;
  459. smp_mb();
  460. __asm__ __volatile__(
  461. "1: llockd %0, [%2] \n"
  462. " brne %L0, %L4, 2f # continue to add since v != u \n"
  463. " breq.d %H0, %H4, 3f # return since v == u \n"
  464. "2: \n"
  465. " add.f %L1, %L0, %L3 \n"
  466. " adc %H1, %H0, %H3 \n"
  467. " scondd %1, [%2] \n"
  468. " bnz 1b \n"
  469. "3: \n"
  470. : "=&r"(old), "=&r" (temp)
  471. : "r"(&v->counter), "r"(a), "r"(u)
  472. : "cc"); /* memory clobber comes from smp_mb() */
  473. smp_mb();
  474. return old;
  475. }
  476. #define atomic64_fetch_add_unless atomic64_fetch_add_unless
  477. #endif /* !CONFIG_GENERIC_ATOMIC64 */
  478. #endif /* !__ASSEMBLY__ */
  479. #endif