Home | History | Annotate | Line # | Download | only in nds32
      1 /* Linux-specific atomic operations for NDS32 Linux.
      2    Copyright (C) 2012-2024 Free Software Foundation, Inc.
      3 
      4 This file is free software; you can redistribute it and/or modify it
      5 under the terms of the GNU General Public License as published by the
      6 Free Software Foundation; either version 3, or (at your option) any
      7 later version.
      8 
      9 This file is distributed in the hope that it will be useful, but
     10 WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 General Public License for more details.
     13 
     14 Under Section 7 of GPL version 3, you are granted additional
     15 permissions described in the GCC Runtime Library Exception, version
     16 3.1, as published by the Free Software Foundation.
     17 
     18 You should have received a copy of the GNU General Public License and
     19 a copy of the GCC Runtime Library Exception along with this program;
     20 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     21 <http://www.gnu.org/licenses/>.  */
     22 
     23 /* We implement byte, short and int versions of each atomic operation
     24    using the kernel helper defined below.  There is no support for
     25    64-bit operations yet.  */
     26 
     27 /* This function copy form NDS32 Linux-kernal. */
     28 static inline int
     29 __kernel_cmpxchg (int oldval, int newval, int *mem)
     30 {
     31   int temp1, temp2, temp3, offset;
     32 
     33   asm volatile ("msync\tall\n"
     34 		"movi\t%0, #0\n"
     35 		"1:\n"
     36 		"\tllw\t%1, [%4+%0]\n"
     37 		"\tsub\t%3, %1, %6\n"
     38 		"\tcmovz\t%2, %5, %3\n"
     39 		"\tcmovn\t%2, %1, %3\n"
     40 		"\tscw\t%2, [%4+%0]\n"
     41 		"\tbeqz\t%2, 1b\n"
     42 		: "=&r" (offset), "=&r" (temp3), "=&r" (temp2), "=&r" (temp1)
     43 		: "r" (mem), "r" (newval), "r" (oldval) : "memory");
     44 
     45   return temp1;
     46 }
     47 
     48 #define HIDDEN __attribute__ ((visibility ("hidden")))
     49 
     50 #ifdef __NDS32_EL__
     51 #define INVERT_MASK_1 0
     52 #define INVERT_MASK_2 0
     53 #else
     54 #define INVERT_MASK_1 24
     55 #define INVERT_MASK_2 16
     56 #endif
     57 
     58 #define MASK_1 0xffu
     59 #define MASK_2 0xffffu
     60 
     61 #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
     62   int HIDDEN								\
     63   __sync_fetch_and_##OP##_4 (int *ptr, int val)				\
     64   {									\
     65     int failure, tmp;							\
     66 									\
     67     do {								\
     68       tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);			\
     69       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
     70     } while (failure != 0);						\
     71 									\
     72     return tmp;								\
     73   }
     74 
     75 FETCH_AND_OP_WORD (add,   , +)
     76 FETCH_AND_OP_WORD (sub,   , -)
     77 FETCH_AND_OP_WORD (or,    , |)
     78 FETCH_AND_OP_WORD (and,   , &)
     79 FETCH_AND_OP_WORD (xor,   , ^)
     80 FETCH_AND_OP_WORD (nand, ~, &)
     81 
     82 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
     83 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
     84 
     85 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
     86    subword-sized quantities.  */
     87 
     88 #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
     89   TYPE HIDDEN								\
     90   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)			\
     91   {									\
     92     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
     93     unsigned int mask, shift, oldval, newval;				\
     94     int failure;							\
     95 									\
     96     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
     97     mask = MASK_##WIDTH << shift;					\
     98 									\
     99     do {								\
    100       oldval = __atomic_load_n (wordptr, __ATOMIC_SEQ_CST);		\
    101       newval = ((PFX_OP (((oldval & mask) >> shift)			\
    102 			 INF_OP (unsigned int) val)) << shift) & mask;	\
    103       newval |= oldval & ~mask;						\
    104       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
    105     } while (failure != 0);						\
    106 									\
    107     return (RETURN & mask) >> shift;					\
    108   }
    109 
    110 
    111 SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
    112 SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
    113 SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
    114 SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
    115 SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
    116 SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
    117 
    118 SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
    119 SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
    120 SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
    121 SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
    122 SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
    123 SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
    124 
    125 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
    126   int HIDDEN								\
    127   __sync_##OP##_and_fetch_4 (int *ptr, int val)				\
    128   {									\
    129     int tmp, failure;							\
    130 									\
    131     do {								\
    132       tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);			\
    133       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
    134     } while (failure != 0);						\
    135 									\
    136     return PFX_OP (tmp INF_OP val);					\
    137   }
    138 
    139 OP_AND_FETCH_WORD (add,   , +)
    140 OP_AND_FETCH_WORD (sub,   , -)
    141 OP_AND_FETCH_WORD (or,    , |)
    142 OP_AND_FETCH_WORD (and,   , &)
    143 OP_AND_FETCH_WORD (xor,   , ^)
    144 OP_AND_FETCH_WORD (nand, ~, &)
    145 
    146 SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
    147 SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
    148 SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
    149 SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
    150 SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
    151 SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
    152 
    153 SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
    154 SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
    155 SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
    156 SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
    157 SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
    158 SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
    159 
    160 int HIDDEN
    161 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
    162 {
    163   int actual_oldval, fail;
    164 
    165   while (1)
    166     {
    167       actual_oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
    168 
    169       if (oldval != actual_oldval)
    170 	return actual_oldval;
    171 
    172       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
    173 
    174       if (!fail)
    175 	return oldval;
    176     }
    177 }
    178 
    179 #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
    180   TYPE HIDDEN								\
    181   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
    182 				       TYPE newval)			\
    183   {									\
    184     int *wordptr = (int *)((unsigned long) ptr & ~3), fail;		\
    185     unsigned int mask, shift, actual_oldval, actual_newval;		\
    186 									\
    187     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
    188     mask = MASK_##WIDTH << shift;					\
    189 									\
    190     while (1)								\
    191       {									\
    192 	actual_oldval = __atomic_load_n (wordptr, __ATOMIC_SEQ_CST); 	\
    193 									\
    194 	if (((actual_oldval & mask) >> shift) != (unsigned int) oldval)	\
    195 	  return (actual_oldval & mask) >> shift;			\
    196 									\
    197 	actual_newval = (actual_oldval & ~mask)				\
    198 			| (((unsigned int) newval << shift) & mask);	\
    199 									\
    200 	fail = __kernel_cmpxchg (actual_oldval, actual_newval,		\
    201 				 wordptr);				\
    202 									\
    203 	if (!fail)							\
    204 	  return oldval;						\
    205       }									\
    206   }
    207 
    208 SUBWORD_VAL_CAS (unsigned short, 2)
    209 SUBWORD_VAL_CAS (unsigned char,  1)
    210 
    211 typedef unsigned char bool;
    212 
    213 bool HIDDEN
    214 __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
    215 {
    216   int failure = __kernel_cmpxchg (oldval, newval, ptr);
    217   return (failure == 0);
    218 }
    219 
    220 #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
    221   bool HIDDEN								\
    222   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
    223 					TYPE newval)			\
    224   {									\
    225     TYPE actual_oldval							\
    226       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);	\
    227     return (oldval == actual_oldval);					\
    228   }
    229 
    230 SUBWORD_BOOL_CAS (unsigned short, 2)
    231 SUBWORD_BOOL_CAS (unsigned char,  1)
    232 
    233 int HIDDEN
    234 __sync_lock_test_and_set_4 (int *ptr, int val)
    235 {
    236   int failure, oldval;
    237 
    238   do {
    239     oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
    240     failure = __kernel_cmpxchg (oldval, val, ptr);
    241   } while (failure != 0);
    242 
    243   return oldval;
    244 }
    245 
    246 #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)				\
    247   TYPE HIDDEN								\
    248   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)		\
    249   {									\
    250     int failure;							\
    251     unsigned int oldval, newval, shift, mask;				\
    252     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
    253 									\
    254     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
    255     mask = MASK_##WIDTH << shift;					\
    256 									\
    257     do {								\
    258       oldval = __atomic_load_n (wordptr, __ATOMIC_SEQ_CST);		\
    259       newval = (oldval & ~mask)						\
    260 	       | (((unsigned int) val << shift) & mask);		\
    261       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
    262     } while (failure != 0);						\
    263 									\
    264     return (oldval & mask) >> shift;					\
    265   }
    266 
    267 SUBWORD_TEST_AND_SET (unsigned short, 2)
    268 SUBWORD_TEST_AND_SET (unsigned char,  1)
    269 
    270 #define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
    271   void HIDDEN								\
    272   __sync_lock_release_##WIDTH (TYPE *ptr)				\
    273   {									\
    274     /* All writes before this point must be seen before we release	\
    275        the lock itself.  */						\
    276     __builtin_nds32_msync_all ();					\
    277     *ptr = 0;								\
    278   }
    279 
    280 SYNC_LOCK_RELEASE (int,   4)
    281 SYNC_LOCK_RELEASE (short, 2)
    282 SYNC_LOCK_RELEASE (char,  1)
    283