162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#ifndef _ARCH_LOONGARCH_LOCAL_H 662306a36Sopenharmony_ci#define _ARCH_LOONGARCH_LOCAL_H 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/percpu.h> 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <asm/cmpxchg.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_citypedef struct { 1462306a36Sopenharmony_ci atomic_long_t a; 1562306a36Sopenharmony_ci} local_t; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define LOCAL_INIT(i) { ATOMIC_LONG_INIT(i) } 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define local_read(l) atomic_long_read(&(l)->a) 2062306a36Sopenharmony_ci#define local_set(l, i) atomic_long_set(&(l)->a, (i)) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define local_add(i, l) atomic_long_add((i), (&(l)->a)) 2362306a36Sopenharmony_ci#define local_sub(i, l) atomic_long_sub((i), (&(l)->a)) 2462306a36Sopenharmony_ci#define local_inc(l) atomic_long_inc(&(l)->a) 2562306a36Sopenharmony_ci#define local_dec(l) atomic_long_dec(&(l)->a) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Same as above, but return the result value 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic inline long local_add_return(long i, local_t *l) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci unsigned long result; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci __asm__ __volatile__( 3562306a36Sopenharmony_ci " " __AMADD " %1, %2, %0 \n" 3662306a36Sopenharmony_ci : "+ZB" (l->a.counter), "=&r" (result) 3762306a36Sopenharmony_ci : "r" (i) 3862306a36Sopenharmony_ci : "memory"); 3962306a36Sopenharmony_ci result = result + i; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return result; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic inline long local_sub_return(long i, local_t *l) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci unsigned long result; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci __asm__ __volatile__( 4962306a36Sopenharmony_ci " " __AMADD "%1, %2, %0 \n" 5062306a36Sopenharmony_ci : "+ZB" (l->a.counter), "=&r" (result) 5162306a36Sopenharmony_ci : "r" (-i) 5262306a36Sopenharmony_ci : "memory"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci result = result - i; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return result; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic inline long local_cmpxchg(local_t *l, long old, long new) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return cmpxchg_local(&l->a.counter, old, new); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic inline bool local_try_cmpxchg(local_t *l, long *old, long new) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return try_cmpxchg_local(&l->a.counter, 6762306a36Sopenharmony_ci (typeof(l->a.counter) *) old, new); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define local_xchg(l, n) (atomic_long_xchg((&(l)->a), (n))) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/** 7362306a36Sopenharmony_ci * local_add_unless - add unless the number is a given value 7462306a36Sopenharmony_ci * @l: pointer of type local_t 7562306a36Sopenharmony_ci * @a: the amount to add to l... 7662306a36Sopenharmony_ci * @u: ...unless l is equal to u. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Atomically adds @a to @l, so long as it was not @u. 7962306a36Sopenharmony_ci * Returns non-zero if @l was not @u, and zero otherwise. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci#define local_add_unless(l, a, u) \ 8262306a36Sopenharmony_ci({ \ 8362306a36Sopenharmony_ci long c, old; \ 8462306a36Sopenharmony_ci c = local_read(l); \ 8562306a36Sopenharmony_ci while (c != (u) && (old = local_cmpxchg((l), c, c + (a))) != c) \ 8662306a36Sopenharmony_ci c = old; \ 8762306a36Sopenharmony_ci c != (u); \ 8862306a36Sopenharmony_ci}) 8962306a36Sopenharmony_ci#define local_inc_not_zero(l) local_add_unless((l), 1, 0) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define local_dec_return(l) local_sub_return(1, (l)) 9262306a36Sopenharmony_ci#define local_inc_return(l) local_add_return(1, (l)) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * local_sub_and_test - subtract value from variable and test result 9662306a36Sopenharmony_ci * @i: integer value to subtract 9762306a36Sopenharmony_ci * @l: pointer of type local_t 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Atomically subtracts @i from @l and returns 10062306a36Sopenharmony_ci * true if the result is zero, or false for all 10162306a36Sopenharmony_ci * other cases. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci#define local_sub_and_test(i, l) (local_sub_return((i), (l)) == 0) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci * local_inc_and_test - increment and test 10762306a36Sopenharmony_ci * @l: pointer of type local_t 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * Atomically increments @l by 1 11062306a36Sopenharmony_ci * and returns true if the result is zero, or false for all 11162306a36Sopenharmony_ci * other cases. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci#define local_inc_and_test(l) (local_inc_return(l) == 0) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * local_dec_and_test - decrement by 1 and test 11762306a36Sopenharmony_ci * @l: pointer of type local_t 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Atomically decrements @l by 1 and 12062306a36Sopenharmony_ci * returns true if the result is 0, or false for all other 12162306a36Sopenharmony_ci * cases. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci#define local_dec_and_test(l) (local_sub_return(1, (l)) == 0) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * local_add_negative - add and test if negative 12762306a36Sopenharmony_ci * @l: pointer of type local_t 12862306a36Sopenharmony_ci * @i: integer value to add 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Atomically adds @i to @l and returns true 13162306a36Sopenharmony_ci * if the result is negative, or false when 13262306a36Sopenharmony_ci * result is greater than or equal to zero. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci#define local_add_negative(i, l) (local_add_return(i, (l)) < 0) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* Use these for per-cpu local_t variables: on some archs they are 13762306a36Sopenharmony_ci * much more efficient than these naive implementations. Note they take 13862306a36Sopenharmony_ci * a variable, not an address. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define __local_inc(l) ((l)->a.counter++) 14262306a36Sopenharmony_ci#define __local_dec(l) ((l)->a.counter++) 14362306a36Sopenharmony_ci#define __local_add(i, l) ((l)->a.counter += (i)) 14462306a36Sopenharmony_ci#define __local_sub(i, l) ((l)->a.counter -= (i)) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#endif /* _ARCH_LOONGARCH_LOCAL_H */ 147