/*
 * Adapted from
 *   include/asm-generic/bitops/atomic.h
 *
 * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2.
 */
#include "libcflat.h"
#include <bitops.h>
#include <asm/barrier.h>
#include <asm/mmu.h>

void set_bit(int nr, volatile unsigned long *addr)
{
	volatile unsigned long *word = addr + BIT_WORD(nr);
	unsigned long mask = BIT_MASK(nr);

	if (mmu_enabled())
		ATOMIC_BITOP("orr", mask, word);
	else
		*word |= mask;
	smp_mb();
}

void clear_bit(int nr, volatile unsigned long *addr)
{
	volatile unsigned long *word = addr + BIT_WORD(nr);
	unsigned long mask = BIT_MASK(nr);

	if (mmu_enabled())
		ATOMIC_BITOP("bic", mask, word);
	else
		*word &= ~mask;
	smp_mb();
}

int test_bit(int nr, const volatile unsigned long *addr)
{
	const volatile unsigned long *word = addr + BIT_WORD(nr);
	unsigned long mask = BIT_MASK(nr);

	return (*word & mask) != 0;
}

int test_and_set_bit(int nr, volatile unsigned long *addr)
{
	volatile unsigned long *word = addr + BIT_WORD(nr);
	unsigned long mask = BIT_MASK(nr);
	unsigned long old;

	smp_mb();

	if (mmu_enabled()) {
		ATOMIC_TESTOP("orr", mask, word, old);
	} else {
		old = *word;
		*word = old | mask;
	}
	smp_mb();

	return (old & mask) != 0;
}

int test_and_clear_bit(int nr, volatile unsigned long *addr)
{
	volatile unsigned long *word = addr + BIT_WORD(nr);
	unsigned long mask = BIT_MASK(nr);
	unsigned long old;

	smp_mb();

	if (mmu_enabled()) {
		ATOMIC_TESTOP("bic", mask, word, old);
	} else {
		old = *word;
		*word = old & ~mask;
	}
	smp_mb();

	return (old & mask) != 0;
}
