/* Copyright (C) 2012-2022 Free Software Foundation, Inc.
   Contributed by Richard Henderson <rth@redhat.com>.

   This file is part of the GNU Atomic Library (libatomic).

   Libatomic is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   Libatomic is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   more details.

   Under Section 7 of GPL version 3, you are granted additional
   permissions described in the GCC Runtime Library Exception, version
   3.1, as published by the Free Software Foundation.

   You should have received a copy of the GNU General Public License and
   a copy of the GCC Runtime Library Exception along with this program;
   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
   <http://www.gnu.org/licenses/>.  */

#include <config/arm/arm-config.h>


/* Kernel helper for 32-bit compare-and-exchange.  */
typedef int (__kernel_cmpxchg_t) (UWORD oldval, UWORD newval, UWORD *ptr);
#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *) 0xffff0fc0)

/* Kernel helper for 64-bit compare-and-exchange.  */
typedef int (__kernel_cmpxchg64_t) (const U_8 * oldval, const U_8 * newval,
				    U_8 *ptr);
#define __kernel_cmpxchg64 (*(__kernel_cmpxchg64_t *) 0xffff0f60)

/* Kernel helper for memory barrier.  */
typedef void (__kernel_dmb_t) (void);
#define __kernel_dmb (*(__kernel_dmb_t *) 0xffff0fa0)

/* Kernel helper page version number.  */
static inline unsigned*
__kernel_helper_version ()
{
  unsigned *volatile addr = (unsigned int *)0xffff0ffc;
  return addr;
}

#define __kernel_helper_version (*__kernel_helper_version())

#ifndef HAVE_STREX
static inline bool
atomic_compare_exchange_w (UWORD *mptr, UWORD *eptr, UWORD newval,
			   bool weak_p UNUSED, int sm UNUSED, int fm UNUSED)
{
  bool ret = true;
  UWORD oldval;

  oldval = *eptr;
  if (__builtin_expect (__kernel_cmpxchg (oldval, newval, mptr) != 0, 0))
    {
      oldval = *mptr;
      ret = false;
    }
  *eptr = oldval;

  return ret;
}
# define atomic_compare_exchange_w atomic_compare_exchange_w
# if N == WORDSIZE
#  define atomic_compare_exchange_n atomic_compare_exchange_w
# endif
#endif /* HAVE_STREX */

#if !defined(HAVE_STREXBHD) && defined(HAVE_KERNEL64) && N == 8
static inline bool
atomic_compare_exchange_n (UTYPE *mptr, UTYPE *eptr, UTYPE newval,
			   bool weak_p UNUSED, int sm UNUSED, int fm UNUSED)
{
  if (__kernel_cmpxchg64 (eptr, &newval, mptr) == 0)
    return true;
  else
    {
      *eptr = *mptr;
      return false;
    }
}
#define atomic_compare_exchange_n atomic_compare_exchange_n
#endif

#if !defined(HAVE_DMB) && !defined(HAVE_DMB_MCR)
static inline void
pre_barrier(int model UNUSED)
{
  __kernel_dmb ();
}

static inline void
post_barrier(int model UNUSED)
{
  __kernel_dmb ();
}
# define pre_post_barrier 1
#endif /* !HAVE_DMB */

#if HAVE_IFUNC
extern bool libat_have_strexbhd HIDDEN;

# define IFUNC_COND_1	libat_have_strexbhd
# define IFUNC_COND_2	(__kernel_helper_version >= 5)

/* Alternative 1 is -march=armv7-a -- we have everything native.  */
# if IFUNC_ALT == 1
#  undef  HAVE_ATOMIC_CAS_1
#  undef  HAVE_ATOMIC_CAS_2
#  undef  HAVE_ATOMIC_CAS_4
#  undef  HAVE_ATOMIC_CAS_8
#  undef  HAVE_ATOMIC_EXCHANGE_1
#  undef  HAVE_ATOMIC_EXCHANGE_2
#  undef  HAVE_ATOMIC_EXCHANGE_4
#  undef  HAVE_ATOMIC_EXCHANGE_8
#  undef  HAVE_ATOMIC_LDST_1
#  undef  HAVE_ATOMIC_LDST_2
#  undef  HAVE_ATOMIC_LDST_4
#  undef  HAVE_ATOMIC_LDST_8
#  undef  HAVE_ATOMIC_FETCH_OP_1
#  undef  HAVE_ATOMIC_FETCH_OP_2
#  undef  HAVE_ATOMIC_FETCH_OP_4
#  undef  HAVE_ATOMIC_FETCH_OP_8
#  undef  HAVE_ATOMIC_TAS_1
#  undef  HAVE_ATOMIC_TAS_2
#  undef  HAVE_ATOMIC_TAS_4
#  undef  HAVE_ATOMIC_TAS_8
#  define HAVE_ATOMIC_CAS_1		1
#  define HAVE_ATOMIC_CAS_2		1
#  define HAVE_ATOMIC_CAS_4		1
#  define HAVE_ATOMIC_CAS_8		1
#  define HAVE_ATOMIC_EXCHANGE_1	1
#  define HAVE_ATOMIC_EXCHANGE_2	1
#  define HAVE_ATOMIC_EXCHANGE_4	1
#  define HAVE_ATOMIC_EXCHANGE_8	1
#  define HAVE_ATOMIC_LDST_1		1
#  define HAVE_ATOMIC_LDST_2		1
#  define HAVE_ATOMIC_LDST_4		1
#  define HAVE_ATOMIC_LDST_8		1
#  define HAVE_ATOMIC_FETCH_OP_1	1
#  define HAVE_ATOMIC_FETCH_OP_2	1
#  define HAVE_ATOMIC_FETCH_OP_4	1
#  define HAVE_ATOMIC_FETCH_OP_8	1
#  define HAVE_ATOMIC_TAS_1		1
#  define HAVE_ATOMIC_TAS_2		1
#  define HAVE_ATOMIC_TAS_4		1
#  define HAVE_ATOMIC_TAS_8		1
# endif /* IFUNC_ALT == 1 */

# undef  MAYBE_HAVE_ATOMIC_CAS_1
# define MAYBE_HAVE_ATOMIC_CAS_1	IFUNC_COND_1
# undef  MAYBE_HAVE_ATOMIC_EXCHANGE_1
# define MAYBE_HAVE_ATOMIC_EXCHANGE_1	MAYBE_HAVE_ATOMIC_CAS_1
# undef  MAYBE_HAVE_ATOMIC_LDST_1
# define MAYBE_HAVE_ATOMIC_LDST_1	MAYBE_HAVE_ATOMIC_CAS_1
# undef  MAYBE_HAVE_ATOMIC_CAS_2
# define MAYBE_HAVE_ATOMIC_CAS_2	IFUNC_COND_1
# undef  MAYBE_HAVE_ATOMIC_EXCHANGE_2
# define MAYBE_HAVE_ATOMIC_EXCHANGE_2	MAYBE_HAVE_ATOMIC_CAS_2
# undef  MAYBE_HAVE_ATOMIC_LDST_2
# define MAYBE_HAVE_ATOMIC_LDST_2	MAYBE_HAVE_ATOMIC_CAS_2
# undef  MAYBE_HAVE_ATOMIC_CAS_4
# define MAYBE_HAVE_ATOMIC_CAS_4	IFUNC_COND_1
# undef  MAYBE_HAVE_ATOMIC_EXCHANGE_4
# define MAYBE_HAVE_ATOMIC_EXCHANGE_4	MAYBE_HAVE_ATOMIC_CAS_4
# undef  MAYBE_HAVE_ATOMIC_LDST_4
# define MAYBE_HAVE_ATOMIC_LDST_4	MAYBE_HAVE_ATOMIC_CAS_4
# undef  MAYBE_HAVE_ATOMIC_CAS_8
# define MAYBE_HAVE_ATOMIC_CAS_8	(IFUNC_COND_1 | IFUNC_COND_2)
# undef  MAYBE_HAVE_ATOMIC_EXCHANGE_8
# define MAYBE_HAVE_ATOMIC_EXCHANGE_8	MAYBE_HAVE_ATOMIC_CAS_8
# undef  MAYBE_HAVE_ATOMIC_LDST_8
# define MAYBE_HAVE_ATOMIC_LDST_8	MAYBE_HAVE_ATOMIC_CAS_8

# define IFUNC_NCOND(N)			(N == 8 ? 2 : 1)

#endif /* HAVE_IFUNC */

#include_next <host-config.h>
