Home | History | Annotate | Line # | Download | only in pa
      1   1.1  mrg /* Linux-specific atomic operations for PA Linux.
      2  1.11  mrg    Copyright (C) 2008-2022 Free Software Foundation, Inc.
      3   1.1  mrg    Based on code contributed by CodeSourcery for ARM EABI Linux.
      4   1.1  mrg    Modifications for PA Linux by Helge Deller <deller (at) gmx.de>
      5   1.1  mrg 
      6   1.1  mrg This file is part of GCC.
      7   1.1  mrg 
      8   1.1  mrg GCC is free software; you can redistribute it and/or modify it under
      9   1.1  mrg the terms of the GNU General Public License as published by the Free
     10   1.1  mrg Software Foundation; either version 3, or (at your option) any later
     11   1.1  mrg version.
     12   1.1  mrg 
     13   1.1  mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     14   1.1  mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
     15   1.1  mrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     16   1.1  mrg for more details.
     17   1.1  mrg 
     18   1.1  mrg Under Section 7 of GPL version 3, you are granted additional
     19   1.1  mrg permissions described in the GCC Runtime Library Exception, version
     20   1.1  mrg 3.1, as published by the Free Software Foundation.
     21   1.1  mrg 
     22   1.1  mrg You should have received a copy of the GNU General Public License and
     23   1.1  mrg a copy of the GCC Runtime Library Exception along with this program;
     24   1.1  mrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     25   1.1  mrg <http://www.gnu.org/licenses/>.  */
     26   1.1  mrg 
     27   1.1  mrg #define EFAULT  14
     28   1.1  mrg #define EBUSY   16
     29   1.1  mrg #define ENOSYS 251
     30   1.1  mrg 
     31  1.11  mrg #define _ASM_EFAULT "-14"
     32  1.11  mrg 
     33  1.10  mrg typedef unsigned char u8;
     34  1.10  mrg typedef short unsigned int u16;
     35  1.10  mrg #ifdef __LP64__
     36  1.10  mrg typedef long unsigned int u64;
     37  1.10  mrg #else
     38  1.10  mrg typedef long long unsigned int u64;
     39  1.10  mrg #endif
     40  1.10  mrg 
     41   1.5  mrg /* PA-RISC 2.0 supports out-of-order execution for loads and stores.
     42   1.5  mrg    Thus, we need to synchonize memory accesses.  For more info, see:
     43   1.5  mrg    "Advanced Performance Features of the 64-bit PA-8000" by Doug Hunt.
     44   1.1  mrg 
     45   1.1  mrg    We implement byte, short and int versions of each atomic operation
     46   1.1  mrg    using the kernel helper defined below.  There is no support for
     47   1.1  mrg    64-bit operations yet.  */
     48   1.1  mrg 
     49   1.1  mrg /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace).  */
     50   1.3  mrg #define LWS_CAS (sizeof(long) == 4 ? 0 : 1)
     51   1.1  mrg 
     52   1.1  mrg /* Kernel helper for compare-and-exchange a 32-bit value.  */
     53   1.1  mrg static inline long
     54   1.8  mrg __kernel_cmpxchg (volatile void *mem, int oldval, int newval)
     55   1.1  mrg {
     56   1.1  mrg   register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
     57   1.3  mrg   register int lws_old asm("r25") = oldval;
     58   1.3  mrg   register int lws_new asm("r24") = newval;
     59   1.1  mrg   register long lws_ret   asm("r28");
     60   1.1  mrg   register long lws_errno asm("r21");
     61   1.1  mrg   asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
     62   1.3  mrg 			"ldi	%2, %%r20		\n\t"
     63  1.11  mrg 			"cmpiclr,<> " _ASM_EFAULT ", %%r21, %%r0\n\t"
     64  1.11  mrg 			"iitlbp %%r0,(%%sr0, %%r0)	\n\t"
     65   1.3  mrg 	: "=r" (lws_ret), "=r" (lws_errno)
     66   1.3  mrg 	: "i" (LWS_CAS), "r" (lws_mem), "r" (lws_old), "r" (lws_new)
     67   1.1  mrg 	: "r1", "r20", "r22", "r23", "r29", "r31", "memory"
     68   1.1  mrg   );
     69   1.1  mrg 
     70   1.1  mrg   /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains
     71   1.1  mrg      the old value from memory.  If this value is equal to OLDVAL, the
     72   1.1  mrg      new value was written to memory.  If not, return -EBUSY.  */
     73   1.1  mrg   if (!lws_errno && lws_ret != oldval)
     74   1.8  mrg     return -EBUSY;
     75   1.1  mrg 
     76   1.1  mrg   return lws_errno;
     77   1.1  mrg }
     78   1.1  mrg 
     79   1.3  mrg static inline long
     80   1.8  mrg __kernel_cmpxchg2 (volatile void *mem, const void *oldval, const void *newval,
     81   1.3  mrg 		   int val_size)
     82   1.3  mrg {
     83   1.3  mrg   register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
     84   1.3  mrg   register unsigned long lws_old asm("r25") = (unsigned long) oldval;
     85   1.3  mrg   register unsigned long lws_new asm("r24") = (unsigned long) newval;
     86   1.3  mrg   register int lws_size asm("r23") = val_size;
     87   1.3  mrg   register long lws_ret   asm("r28");
     88   1.3  mrg   register long lws_errno asm("r21");
     89   1.3  mrg   asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
     90   1.3  mrg 			"ldi	%6, %%r20		\n\t"
     91  1.11  mrg 			"cmpiclr,<> " _ASM_EFAULT ", %%r21, %%r0\n\t"
     92  1.11  mrg 			"iitlbp %%r0,(%%sr0, %%r0)	\n\t"
     93   1.3  mrg 	: "=r" (lws_ret), "=r" (lws_errno), "+r" (lws_mem),
     94   1.3  mrg 	  "+r" (lws_old), "+r" (lws_new), "+r" (lws_size)
     95   1.3  mrg 	: "i" (2)
     96   1.3  mrg 	: "r1", "r20", "r22", "r29", "r31", "fr4", "memory"
     97   1.3  mrg   );
     98   1.3  mrg 
     99   1.3  mrg   /* If the kernel LWS call is successful, lws_ret contains 0.  */
    100   1.3  mrg   if (__builtin_expect (lws_ret == 0, 1))
    101   1.3  mrg     return 0;
    102   1.3  mrg 
    103   1.3  mrg   /* If the kernel LWS call fails with no error, return -EBUSY */
    104   1.3  mrg   if (__builtin_expect (!lws_errno, 0))
    105   1.3  mrg     return -EBUSY;
    106   1.3  mrg 
    107   1.3  mrg   return lws_errno;
    108   1.3  mrg }
    109   1.1  mrg #define HIDDEN __attribute__ ((visibility ("hidden")))
    110   1.1  mrg 
    111   1.1  mrg /* Big endian masks  */
    112   1.1  mrg #define INVERT_MASK_1 24
    113   1.1  mrg #define INVERT_MASK_2 16
    114   1.1  mrg 
    115   1.1  mrg #define MASK_1 0xffu
    116   1.1  mrg #define MASK_2 0xffffu
    117   1.1  mrg 
    118   1.3  mrg #define FETCH_AND_OP_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX)		\
    119   1.3  mrg   TYPE HIDDEN								\
    120   1.8  mrg   __sync_fetch_and_##OP##_##WIDTH (volatile void *ptr, TYPE val)	\
    121   1.1  mrg   {									\
    122   1.3  mrg     TYPE tmp, newval;							\
    123   1.3  mrg     long failure;							\
    124   1.1  mrg 									\
    125   1.1  mrg     do {								\
    126   1.8  mrg       tmp = __atomic_load_n ((volatile TYPE *)ptr, __ATOMIC_RELAXED);	\
    127   1.3  mrg       newval = PFX_OP (tmp INF_OP val);					\
    128   1.3  mrg       failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX);		\
    129   1.1  mrg     } while (failure != 0);						\
    130   1.1  mrg 									\
    131   1.1  mrg     return tmp;								\
    132   1.1  mrg   }
    133   1.1  mrg 
    134  1.10  mrg FETCH_AND_OP_2 (add,   , +, u64, 8, 3)
    135  1.10  mrg FETCH_AND_OP_2 (sub,   , -, u64, 8, 3)
    136  1.10  mrg FETCH_AND_OP_2 (or,    , |, u64, 8, 3)
    137  1.10  mrg FETCH_AND_OP_2 (and,   , &, u64, 8, 3)
    138  1.10  mrg FETCH_AND_OP_2 (xor,   , ^, u64, 8, 3)
    139  1.10  mrg FETCH_AND_OP_2 (nand, ~, &, u64, 8, 3)
    140  1.10  mrg 
    141  1.10  mrg FETCH_AND_OP_2 (add,   , +, u16, 2, 1)
    142  1.10  mrg FETCH_AND_OP_2 (sub,   , -, u16, 2, 1)
    143  1.10  mrg FETCH_AND_OP_2 (or,    , |, u16, 2, 1)
    144  1.10  mrg FETCH_AND_OP_2 (and,   , &, u16, 2, 1)
    145  1.10  mrg FETCH_AND_OP_2 (xor,   , ^, u16, 2, 1)
    146  1.10  mrg FETCH_AND_OP_2 (nand, ~, &, u16, 2, 1)
    147  1.10  mrg 
    148  1.10  mrg FETCH_AND_OP_2 (add,   , +, u8, 1, 0)
    149  1.10  mrg FETCH_AND_OP_2 (sub,   , -, u8, 1, 0)
    150  1.10  mrg FETCH_AND_OP_2 (or,    , |, u8, 1, 0)
    151  1.10  mrg FETCH_AND_OP_2 (and,   , &, u8, 1, 0)
    152  1.10  mrg FETCH_AND_OP_2 (xor,   , ^, u8, 1, 0)
    153  1.10  mrg FETCH_AND_OP_2 (nand, ~, &, u8, 1, 0)
    154   1.1  mrg 
    155   1.3  mrg #define OP_AND_FETCH_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX)		\
    156   1.1  mrg   TYPE HIDDEN								\
    157   1.8  mrg   __sync_##OP##_and_fetch_##WIDTH (volatile void *ptr, TYPE val)	\
    158   1.1  mrg   {									\
    159   1.3  mrg     TYPE tmp, newval;							\
    160   1.3  mrg     long failure;							\
    161   1.1  mrg 									\
    162   1.3  mrg     do {								\
    163   1.8  mrg       tmp = __atomic_load_n ((volatile TYPE *)ptr, __ATOMIC_RELAXED);	\
    164   1.3  mrg       newval = PFX_OP (tmp INF_OP val);					\
    165   1.3  mrg       failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX);		\
    166   1.3  mrg     } while (failure != 0);						\
    167   1.3  mrg 									\
    168   1.3  mrg     return PFX_OP (tmp INF_OP val);					\
    169   1.3  mrg   }
    170   1.3  mrg 
    171  1.10  mrg OP_AND_FETCH_2 (add,   , +, u64, 8, 3)
    172  1.10  mrg OP_AND_FETCH_2 (sub,   , -, u64, 8, 3)
    173  1.10  mrg OP_AND_FETCH_2 (or,    , |, u64, 8, 3)
    174  1.10  mrg OP_AND_FETCH_2 (and,   , &, u64, 8, 3)
    175  1.10  mrg OP_AND_FETCH_2 (xor,   , ^, u64, 8, 3)
    176  1.10  mrg OP_AND_FETCH_2 (nand, ~, &, u64, 8, 3)
    177  1.10  mrg 
    178  1.10  mrg OP_AND_FETCH_2 (add,   , +, u16, 2, 1)
    179  1.10  mrg OP_AND_FETCH_2 (sub,   , -, u16, 2, 1)
    180  1.10  mrg OP_AND_FETCH_2 (or,    , |, u16, 2, 1)
    181  1.10  mrg OP_AND_FETCH_2 (and,   , &, u16, 2, 1)
    182  1.10  mrg OP_AND_FETCH_2 (xor,   , ^, u16, 2, 1)
    183  1.10  mrg OP_AND_FETCH_2 (nand, ~, &, u16, 2, 1)
    184  1.10  mrg 
    185  1.10  mrg OP_AND_FETCH_2 (add,   , +, u8, 1, 0)
    186  1.10  mrg OP_AND_FETCH_2 (sub,   , -, u8, 1, 0)
    187  1.10  mrg OP_AND_FETCH_2 (or,    , |, u8, 1, 0)
    188  1.10  mrg OP_AND_FETCH_2 (and,   , &, u8, 1, 0)
    189  1.10  mrg OP_AND_FETCH_2 (xor,   , ^, u8, 1, 0)
    190  1.10  mrg OP_AND_FETCH_2 (nand, ~, &, u8, 1, 0)
    191   1.3  mrg 
    192   1.3  mrg #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
    193   1.8  mrg   unsigned int HIDDEN							\
    194   1.8  mrg   __sync_fetch_and_##OP##_4 (volatile void *ptr, unsigned int val)	\
    195   1.3  mrg   {									\
    196   1.8  mrg     unsigned int tmp;							\
    197   1.3  mrg     long failure;							\
    198   1.1  mrg 									\
    199   1.1  mrg     do {								\
    200   1.8  mrg       tmp = __atomic_load_n ((volatile unsigned int *)ptr,		\
    201   1.8  mrg 			     __ATOMIC_RELAXED);				\
    202   1.3  mrg       failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val));	\
    203   1.1  mrg     } while (failure != 0);						\
    204   1.1  mrg 									\
    205   1.3  mrg     return tmp;								\
    206   1.1  mrg   }
    207   1.1  mrg 
    208   1.3  mrg FETCH_AND_OP_WORD (add,   , +)
    209   1.3  mrg FETCH_AND_OP_WORD (sub,   , -)
    210   1.3  mrg FETCH_AND_OP_WORD (or,    , |)
    211   1.3  mrg FETCH_AND_OP_WORD (and,   , &)
    212   1.3  mrg FETCH_AND_OP_WORD (xor,   , ^)
    213   1.3  mrg FETCH_AND_OP_WORD (nand, ~, &)
    214   1.1  mrg 
    215   1.1  mrg #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
    216   1.8  mrg   unsigned int HIDDEN							\
    217   1.8  mrg   __sync_##OP##_and_fetch_4 (volatile void *ptr, unsigned int val)	\
    218   1.1  mrg   {									\
    219   1.8  mrg     unsigned int tmp;							\
    220   1.3  mrg     long failure;							\
    221   1.1  mrg 									\
    222   1.1  mrg     do {								\
    223   1.8  mrg       tmp = __atomic_load_n ((volatile unsigned int *)ptr,		\
    224   1.8  mrg 			     __ATOMIC_RELAXED);				\
    225   1.3  mrg       failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val));	\
    226   1.1  mrg     } while (failure != 0);						\
    227   1.1  mrg 									\
    228   1.1  mrg     return PFX_OP (tmp INF_OP val);					\
    229   1.1  mrg   }
    230   1.1  mrg 
    231   1.1  mrg OP_AND_FETCH_WORD (add,   , +)
    232   1.1  mrg OP_AND_FETCH_WORD (sub,   , -)
    233   1.1  mrg OP_AND_FETCH_WORD (or,    , |)
    234   1.1  mrg OP_AND_FETCH_WORD (and,   , &)
    235   1.1  mrg OP_AND_FETCH_WORD (xor,   , ^)
    236   1.1  mrg OP_AND_FETCH_WORD (nand, ~, &)
    237   1.1  mrg 
    238   1.3  mrg typedef unsigned char bool;
    239   1.3  mrg 
    240   1.3  mrg #define COMPARE_AND_SWAP_2(TYPE, WIDTH, INDEX)				\
    241   1.3  mrg   TYPE HIDDEN								\
    242   1.8  mrg   __sync_val_compare_and_swap_##WIDTH (volatile void *ptr, TYPE oldval,	\
    243   1.3  mrg 				       TYPE newval)			\
    244   1.3  mrg   {									\
    245   1.3  mrg     TYPE actual_oldval;							\
    246   1.3  mrg     long fail;								\
    247   1.3  mrg 									\
    248   1.3  mrg     while (1)								\
    249   1.3  mrg       {									\
    250   1.8  mrg 	actual_oldval = __atomic_load_n ((volatile TYPE *)ptr,		\
    251   1.8  mrg 					 __ATOMIC_RELAXED);		\
    252   1.3  mrg 									\
    253   1.3  mrg 	if (__builtin_expect (oldval != actual_oldval, 0))		\
    254   1.3  mrg 	  return actual_oldval;						\
    255   1.3  mrg 									\
    256   1.3  mrg 	fail = __kernel_cmpxchg2 (ptr, &actual_oldval, &newval, INDEX);	\
    257   1.3  mrg 									\
    258   1.3  mrg 	if (__builtin_expect (!fail, 1))				\
    259   1.3  mrg 	  return actual_oldval;						\
    260   1.3  mrg       }									\
    261   1.3  mrg   }									\
    262   1.3  mrg 									\
    263   1.8  mrg   _Bool HIDDEN								\
    264   1.8  mrg   __sync_bool_compare_and_swap_##WIDTH (volatile void *ptr,		\
    265   1.8  mrg 					TYPE oldval, TYPE newval)	\
    266   1.3  mrg   {									\
    267   1.3  mrg     long failure = __kernel_cmpxchg2 (ptr, &oldval, &newval, INDEX);	\
    268   1.3  mrg     return (failure == 0);						\
    269   1.3  mrg   }
    270   1.3  mrg 
    271  1.10  mrg COMPARE_AND_SWAP_2 (u64, 8, 3)
    272  1.10  mrg COMPARE_AND_SWAP_2 (u16, 2, 1)
    273  1.10  mrg COMPARE_AND_SWAP_2 (u8, 1, 0)
    274   1.8  mrg 
    275   1.8  mrg unsigned int HIDDEN
    276   1.8  mrg __sync_val_compare_and_swap_4 (volatile void *ptr, unsigned int oldval,
    277   1.8  mrg 			       unsigned int newval)
    278   1.1  mrg {
    279   1.3  mrg   long fail;
    280   1.8  mrg   unsigned int actual_oldval;
    281   1.1  mrg 
    282   1.1  mrg   while (1)
    283   1.1  mrg     {
    284   1.8  mrg       actual_oldval = __atomic_load_n ((volatile unsigned int *)ptr,
    285   1.8  mrg 				       __ATOMIC_RELAXED);
    286   1.1  mrg 
    287   1.1  mrg       if (__builtin_expect (oldval != actual_oldval, 0))
    288   1.1  mrg 	return actual_oldval;
    289   1.1  mrg 
    290   1.3  mrg       fail = __kernel_cmpxchg (ptr, actual_oldval, newval);
    291   1.1  mrg 
    292   1.1  mrg       if (__builtin_expect (!fail, 1))
    293   1.1  mrg 	return actual_oldval;
    294   1.1  mrg     }
    295   1.1  mrg }
    296   1.1  mrg 
    297   1.8  mrg _Bool HIDDEN
    298   1.8  mrg __sync_bool_compare_and_swap_4 (volatile void *ptr, unsigned int oldval,
    299   1.8  mrg 				unsigned int newval)
    300   1.1  mrg {
    301   1.3  mrg   long failure = __kernel_cmpxchg (ptr, oldval, newval);
    302   1.1  mrg   return (failure == 0);
    303   1.1  mrg }
    304   1.1  mrg 
    305   1.3  mrg #define SYNC_LOCK_TEST_AND_SET_2(TYPE, WIDTH, INDEX)			\
    306   1.3  mrg TYPE HIDDEN								\
    307   1.8  mrg   __sync_lock_test_and_set_##WIDTH (volatile void *ptr, TYPE val)	\
    308   1.1  mrg   {									\
    309   1.3  mrg     TYPE oldval;							\
    310   1.3  mrg     long failure;							\
    311   1.3  mrg 									\
    312   1.3  mrg     do {								\
    313   1.8  mrg       oldval = __atomic_load_n ((volatile TYPE *)ptr,			\
    314   1.8  mrg 				__ATOMIC_RELAXED);			\
    315   1.3  mrg       failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX);		\
    316   1.3  mrg     } while (failure != 0);						\
    317   1.3  mrg 									\
    318   1.3  mrg     return oldval;							\
    319   1.1  mrg   }
    320   1.1  mrg 
    321  1.10  mrg SYNC_LOCK_TEST_AND_SET_2 (u64, 8, 3)
    322  1.10  mrg SYNC_LOCK_TEST_AND_SET_2 (u16, 2, 1)
    323  1.10  mrg SYNC_LOCK_TEST_AND_SET_2 (u8, 1, 0)
    324   1.1  mrg 
    325   1.8  mrg unsigned int HIDDEN
    326   1.8  mrg __sync_lock_test_and_set_4 (volatile void *ptr, unsigned int val)
    327   1.1  mrg {
    328   1.3  mrg   long failure;
    329   1.8  mrg   unsigned int oldval;
    330   1.1  mrg 
    331   1.1  mrg   do {
    332   1.8  mrg     oldval = __atomic_load_n ((volatile unsigned int *)ptr, __ATOMIC_RELAXED);
    333   1.3  mrg     failure = __kernel_cmpxchg (ptr, oldval, val);
    334   1.1  mrg   } while (failure != 0);
    335   1.1  mrg 
    336   1.1  mrg   return oldval;
    337   1.1  mrg }
    338   1.1  mrg 
    339   1.8  mrg #define SYNC_LOCK_RELEASE_1(TYPE, WIDTH, INDEX)			\
    340   1.3  mrg   void HIDDEN							\
    341   1.8  mrg   __sync_lock_release_##WIDTH (volatile void *ptr)		\
    342   1.3  mrg   {								\
    343   1.8  mrg     TYPE oldval, val = 0;					\
    344   1.8  mrg     long failure;						\
    345   1.8  mrg 								\
    346   1.8  mrg     do {							\
    347   1.8  mrg       oldval = __atomic_load_n ((volatile TYPE *)ptr,		\
    348   1.8  mrg 				__ATOMIC_RELAXED);		\
    349   1.8  mrg       failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX);	\
    350   1.8  mrg     } while (failure != 0);					\
    351   1.1  mrg   }
    352   1.1  mrg 
    353  1.10  mrg SYNC_LOCK_RELEASE_1 (u64, 8, 3)
    354  1.10  mrg SYNC_LOCK_RELEASE_1 (u16, 2, 1)
    355  1.10  mrg SYNC_LOCK_RELEASE_1 (u8, 1, 0)
    356   1.8  mrg 
    357   1.8  mrg void HIDDEN
    358   1.8  mrg __sync_lock_release_4 (volatile void *ptr)
    359   1.8  mrg {
    360   1.8  mrg   long failure;
    361   1.8  mrg   unsigned int oldval;
    362   1.8  mrg 
    363   1.8  mrg   do {
    364   1.8  mrg     oldval = __atomic_load_n ((volatile unsigned int *)ptr, __ATOMIC_RELAXED);
    365   1.8  mrg     failure = __kernel_cmpxchg (ptr, oldval, 0);
    366   1.8  mrg   } while (failure != 0);
    367   1.8  mrg }
    368