|
@@ -0,0 +1,127 @@
|
|
|
+/*
|
|
|
+ * Atomic operations that C can't guarantee us. Useful for
|
|
|
+ * resource counting etc..
|
|
|
+ *
|
|
|
+ * But use these as seldom as possible since they are much more slower
|
|
|
+ * than regular operations.
|
|
|
+ *
|
|
|
+ * This file is subject to the terms and conditions of the GNU General Public
|
|
|
+ * License. See the file "COPYING" in the main directory of this archive
|
|
|
+ * for more details.
|
|
|
+ *
|
|
|
+ * Copyright (C) 1996, 97, 99, 2000, 03, 04, 06 by Ralf Baechle
|
|
|
+ */
|
|
|
+#ifndef _ASM_ATOMIC_H
|
|
|
+#define _ASM_ATOMIC_H
|
|
|
+
|
|
|
+#include <linux/irqflags.h>
|
|
|
+#include <linux/types.h>
|
|
|
+#include <asm/barrier.h>
|
|
|
+#include <asm/cpu-features.h>
|
|
|
+#include <asm/cmpxchg.h>
|
|
|
+#include <asm/war.h>
|
|
|
+
|
|
|
+#define ATOMIC_INIT(i) { (i) }
|
|
|
+
|
|
|
+/*
|
|
|
+ * atomic_read - read atomic variable
|
|
|
+ * @v: pointer of type atomic_t
|
|
|
+ *
|
|
|
+ * Atomically reads the value of @v.
|
|
|
+ */
|
|
|
+#define atomic_read(v) (*(volatile int *)&(v)->counter)
|
|
|
+
|
|
|
+/*
|
|
|
+ * atomic_set - set atomic variable
|
|
|
+ * @v: pointer of type atomic_t
|
|
|
+ * @i: required value
|
|
|
+ *
|
|
|
+ * Atomically sets the value of @v to @i.
|
|
|
+ */
|
|
|
+#define atomic_set(v, i) ((v)->counter = (i))
|
|
|
+
|
|
|
+/*
|
|
|
+ * atomic_add - add integer to atomic variable
|
|
|
+ * @i: integer value to add
|
|
|
+ * @v: pointer of type atomic_t
|
|
|
+ *
|
|
|
+ * Atomically adds @i to @v.
|
|
|
+ */
|
|
|
+static __inline__ void atomic_add(int i, atomic_t * v)
|
|
|
+{
|
|
|
+ if (kernel_uses_llsc && R10000_LLSC_WAR) {
|
|
|
+ int temp;
|
|
|
+
|
|
|
+ __asm__ __volatile__(
|
|
|
+ " .set mips3 \n"
|
|
|
+ "1: ll %0, %1 # atomic_add \n"
|
|
|
+ " addu %0, %2 \n"
|
|
|
+ " sc %0, %1 \n"
|
|
|
+ " beqzl %0, 1b \n"
|
|
|
+ " .set mips0 \n"
|
|
|
+ : "=&r" (temp), "+m" (v->counter)
|
|
|
+ : "Ir" (i));
|
|
|
+ } else if (kernel_uses_llsc) {
|
|
|
+ int temp;
|
|
|
+
|
|
|
+ do {
|
|
|
+ __asm__ __volatile__(
|
|
|
+ " .set mips3 \n"
|
|
|
+ " ll %0, %1 # atomic_add \n"
|
|
|
+ " addu %0, %2 \n"
|
|
|
+ " sc %0, %1 \n"
|
|
|
+ " .set mips0 \n"
|
|
|
+ : "=&r" (temp), "+m" (v->counter)
|
|
|
+ : "Ir" (i));
|
|
|
+ } while (unlikely(!temp));
|
|
|
+ } else {
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ raw_local_irq_save(flags);
|
|
|
+ v->counter += i;
|
|
|
+ raw_local_irq_restore(flags);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * atomic_sub - subtract the atomic variable
|
|
|
+ * @i: integer value to subtract
|
|
|
+ * @v: pointer of type atomic_t
|
|
|
+ *
|
|
|
+ * Atomically subtracts @i from @v.
|
|
|
+ */
|
|
|
+static __inline__ void atomic_sub(int i, atomic_t * v)
|
|
|
+{
|
|
|
+ if (kernel_uses_llsc && R10000_LLSC_WAR) {
|
|
|
+ int temp;
|
|
|
+
|
|
|
+ __asm__ __volatile__(
|
|
|
+ " .set mips3 \n"
|
|
|
+ "1: ll %0, %1 # atomic_sub \n"
|
|
|
+ " subu %0, %2 \n"
|
|
|
+ " sc %0, %1 \n"
|
|
|
+ " beqzl %0, 1b \n"
|
|
|
+ " .set mips0 \n"
|
|
|
+ : "=&r" (temp), "+m" (v->counter)
|
|
|
+ : "Ir" (i));
|
|
|
+ } else if (kernel_uses_llsc) {
|
|
|
+ int temp;
|
|
|
+
|
|
|
+ do {
|
|
|
+ __asm__ __volatile__(
|
|
|
+ " .set mips3 \n"
|
|
|
+ " ll %0, %1 # atomic_sub \n"
|
|
|
+ " subu %0, %2 \n"
|
|
|
+ " sc %0, %1 \n"
|
|
|
+ " .set mips0 \n"
|
|
|
+ : "=&r" (temp), "+m" (v->counter)
|
|
|
+ : "Ir" (i));
|
|
|
+ } while (unlikely(!temp));
|
|
|
+ } else {
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ raw_local_irq_save(flags);
|
|
|
+ v->counter -= i;
|
|
|
+ raw_local_irq_restore(flags);
|
|
|
+ }
|
|
|
+}
|