18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Atomic operations for the Hexagon architecture 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#ifndef _ASM_ATOMIC_H 98c2ecf20Sopenharmony_ci#define _ASM_ATOMIC_H 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <asm/cmpxchg.h> 138c2ecf20Sopenharmony_ci#include <asm/barrier.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* Normal writes in our arch don't clear lock reservations */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic inline void atomic_set(atomic_t *v, int new) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci asm volatile( 208c2ecf20Sopenharmony_ci "1: r6 = memw_locked(%0);\n" 218c2ecf20Sopenharmony_ci " memw_locked(%0,p0) = %1;\n" 228c2ecf20Sopenharmony_ci " if (!P0) jump 1b;\n" 238c2ecf20Sopenharmony_ci : 248c2ecf20Sopenharmony_ci : "r" (&v->counter), "r" (new) 258c2ecf20Sopenharmony_ci : "memory", "p0", "r6" 268c2ecf20Sopenharmony_ci ); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define atomic_set_release(v, i) atomic_set((v), (i)) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * atomic_read - reads a word, atomically 338c2ecf20Sopenharmony_ci * @v: pointer to atomic value 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Assumes all word reads on our architecture are atomic. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#define atomic_read(v) READ_ONCE((v)->counter) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * atomic_xchg - atomic 418c2ecf20Sopenharmony_ci * @v: pointer to memory to change 428c2ecf20Sopenharmony_ci * @new: new value (technically passed in a register -- see xchg) 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define atomic_xchg(v, new) (xchg(&((v)->counter), (new))) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * atomic_cmpxchg - atomic compare-and-exchange values 498c2ecf20Sopenharmony_ci * @v: pointer to value to change 508c2ecf20Sopenharmony_ci * @old: desired old value to match 518c2ecf20Sopenharmony_ci * @new: new value to put in 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Parameters are then pointer, value-in-register, value-in-register, 548c2ecf20Sopenharmony_ci * and the output is the old value. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * Apparently this is complicated for archs that don't support 578c2ecf20Sopenharmony_ci * the memw_locked like we do (or it's broken or whatever). 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * Kind of the lynchpin of the rest of the generically defined routines. 608c2ecf20Sopenharmony_ci * Remember V2 had that bug with dotnew predicate set by memw_locked. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * "old" is "expected" old val, __oldval is actual old value 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic inline int atomic_cmpxchg(atomic_t *v, int old, int new) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int __oldval; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci asm volatile( 698c2ecf20Sopenharmony_ci "1: %0 = memw_locked(%1);\n" 708c2ecf20Sopenharmony_ci " { P0 = cmp.eq(%0,%2);\n" 718c2ecf20Sopenharmony_ci " if (!P0.new) jump:nt 2f; }\n" 728c2ecf20Sopenharmony_ci " memw_locked(%1,P0) = %3;\n" 738c2ecf20Sopenharmony_ci " if (!P0) jump 1b;\n" 748c2ecf20Sopenharmony_ci "2:\n" 758c2ecf20Sopenharmony_ci : "=&r" (__oldval) 768c2ecf20Sopenharmony_ci : "r" (&v->counter), "r" (old), "r" (new) 778c2ecf20Sopenharmony_ci : "memory", "p0" 788c2ecf20Sopenharmony_ci ); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return __oldval; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define ATOMIC_OP(op) \ 848c2ecf20Sopenharmony_cistatic inline void atomic_##op(int i, atomic_t *v) \ 858c2ecf20Sopenharmony_ci{ \ 868c2ecf20Sopenharmony_ci int output; \ 878c2ecf20Sopenharmony_ci \ 888c2ecf20Sopenharmony_ci __asm__ __volatile__ ( \ 898c2ecf20Sopenharmony_ci "1: %0 = memw_locked(%1);\n" \ 908c2ecf20Sopenharmony_ci " %0 = "#op "(%0,%2);\n" \ 918c2ecf20Sopenharmony_ci " memw_locked(%1,P3)=%0;\n" \ 928c2ecf20Sopenharmony_ci " if (!P3) jump 1b;\n" \ 938c2ecf20Sopenharmony_ci : "=&r" (output) \ 948c2ecf20Sopenharmony_ci : "r" (&v->counter), "r" (i) \ 958c2ecf20Sopenharmony_ci : "memory", "p3" \ 968c2ecf20Sopenharmony_ci ); \ 978c2ecf20Sopenharmony_ci} \ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define ATOMIC_OP_RETURN(op) \ 1008c2ecf20Sopenharmony_cistatic inline int atomic_##op##_return(int i, atomic_t *v) \ 1018c2ecf20Sopenharmony_ci{ \ 1028c2ecf20Sopenharmony_ci int output; \ 1038c2ecf20Sopenharmony_ci \ 1048c2ecf20Sopenharmony_ci __asm__ __volatile__ ( \ 1058c2ecf20Sopenharmony_ci "1: %0 = memw_locked(%1);\n" \ 1068c2ecf20Sopenharmony_ci " %0 = "#op "(%0,%2);\n" \ 1078c2ecf20Sopenharmony_ci " memw_locked(%1,P3)=%0;\n" \ 1088c2ecf20Sopenharmony_ci " if (!P3) jump 1b;\n" \ 1098c2ecf20Sopenharmony_ci : "=&r" (output) \ 1108c2ecf20Sopenharmony_ci : "r" (&v->counter), "r" (i) \ 1118c2ecf20Sopenharmony_ci : "memory", "p3" \ 1128c2ecf20Sopenharmony_ci ); \ 1138c2ecf20Sopenharmony_ci return output; \ 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define ATOMIC_FETCH_OP(op) \ 1178c2ecf20Sopenharmony_cistatic inline int atomic_fetch_##op(int i, atomic_t *v) \ 1188c2ecf20Sopenharmony_ci{ \ 1198c2ecf20Sopenharmony_ci int output, val; \ 1208c2ecf20Sopenharmony_ci \ 1218c2ecf20Sopenharmony_ci __asm__ __volatile__ ( \ 1228c2ecf20Sopenharmony_ci "1: %0 = memw_locked(%2);\n" \ 1238c2ecf20Sopenharmony_ci " %1 = "#op "(%0,%3);\n" \ 1248c2ecf20Sopenharmony_ci " memw_locked(%2,P3)=%1;\n" \ 1258c2ecf20Sopenharmony_ci " if (!P3) jump 1b;\n" \ 1268c2ecf20Sopenharmony_ci : "=&r" (output), "=&r" (val) \ 1278c2ecf20Sopenharmony_ci : "r" (&v->counter), "r" (i) \ 1288c2ecf20Sopenharmony_ci : "memory", "p3" \ 1298c2ecf20Sopenharmony_ci ); \ 1308c2ecf20Sopenharmony_ci return output; \ 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciATOMIC_OPS(add) 1368c2ecf20Sopenharmony_ciATOMIC_OPS(sub) 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#undef ATOMIC_OPS 1398c2ecf20Sopenharmony_ci#define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciATOMIC_OPS(and) 1428c2ecf20Sopenharmony_ciATOMIC_OPS(or) 1438c2ecf20Sopenharmony_ciATOMIC_OPS(xor) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#undef ATOMIC_OPS 1468c2ecf20Sopenharmony_ci#undef ATOMIC_FETCH_OP 1478c2ecf20Sopenharmony_ci#undef ATOMIC_OP_RETURN 1488c2ecf20Sopenharmony_ci#undef ATOMIC_OP 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * atomic_fetch_add_unless - add unless the number is a given value 1528c2ecf20Sopenharmony_ci * @v: pointer to value 1538c2ecf20Sopenharmony_ci * @a: amount to add 1548c2ecf20Sopenharmony_ci * @u: unless value is equal to u 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Returns old value. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic inline int atomic_fetch_add_unless(atomic_t *v, int a, int u) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int __oldval; 1638c2ecf20Sopenharmony_ci register int tmp; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci asm volatile( 1668c2ecf20Sopenharmony_ci "1: %0 = memw_locked(%2);" 1678c2ecf20Sopenharmony_ci " {" 1688c2ecf20Sopenharmony_ci " p3 = cmp.eq(%0, %4);" 1698c2ecf20Sopenharmony_ci " if (p3.new) jump:nt 2f;" 1708c2ecf20Sopenharmony_ci " %1 = add(%0, %3);" 1718c2ecf20Sopenharmony_ci " }" 1728c2ecf20Sopenharmony_ci " memw_locked(%2, p3) = %1;" 1738c2ecf20Sopenharmony_ci " {" 1748c2ecf20Sopenharmony_ci " if (!p3) jump 1b;" 1758c2ecf20Sopenharmony_ci " }" 1768c2ecf20Sopenharmony_ci "2:" 1778c2ecf20Sopenharmony_ci : "=&r" (__oldval), "=&r" (tmp) 1788c2ecf20Sopenharmony_ci : "r" (v), "r" (a), "r" (u) 1798c2ecf20Sopenharmony_ci : "memory", "p3" 1808c2ecf20Sopenharmony_ci ); 1818c2ecf20Sopenharmony_ci return __oldval; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci#define atomic_fetch_add_unless atomic_fetch_add_unless 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#endif 186