summaryrefslogtreecommitdiff
path: root/arch/metag/include/asm/spinlock_lnkget.h
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2012-10-09 11:00:24 +0100
committerJames Hogan <james.hogan@imgtec.com>2013-03-02 20:09:50 +0000
commit6006c0d8ce9441dd1363bf14f18a8e28d3588460 (patch)
tree786183053c89e11b3058b8a16f7953744b819340 /arch/metag/include/asm/spinlock_lnkget.h
parent9b802d1f43978869fcd98e92b854fd8785cefee7 (diff)
metag: Atomics, locks and bitops
Add header files to implement Meta hardware thread locks (used by some other atomic operations), atomics, spinlocks, and bitops. There are 2 main types of atomic primitives for metag (in addition to IRQs off on UP): - LOCK instructions provide locking between hardware threads. - LNKGET/LNKSET instructions provide load-linked/store-conditional operations allowing for lighter weight atomics on Meta2 LOCK instructions allow for hardware threads to acquire voluntary or exclusive hardware thread locks: - LOCK0 releases exclusive and voluntary lock from the running hardware thread. - LOCK1 acquires the voluntary hardware lock, blocking until it becomes available. - LOCK2 implies LOCK1, and additionally acquires the exclusive hardware lock, blocking all other hardware threads from executing. Signed-off-by: James Hogan <james.hogan@imgtec.com>
Diffstat (limited to 'arch/metag/include/asm/spinlock_lnkget.h')
-rw-r--r--arch/metag/include/asm/spinlock_lnkget.h249
1 files changed, 249 insertions, 0 deletions
diff --git a/arch/metag/include/asm/spinlock_lnkget.h b/arch/metag/include/asm/spinlock_lnkget.h
new file mode 100644
index 000000000000..ad8436feed8d
--- /dev/null
+++ b/arch/metag/include/asm/spinlock_lnkget.h
@@ -0,0 +1,249 @@
+#ifndef __ASM_SPINLOCK_LNKGET_H
+#define __ASM_SPINLOCK_LNKGET_H
+
+/*
+ * None of these asm statements clobber memory as LNKSET writes around
+ * the cache so the memory it modifies cannot safely be read by any means
+ * other than these accessors.
+ */
+
+static inline int arch_spin_is_locked(arch_spinlock_t *lock)
+{
+ int ret;
+
+ asm volatile ("LNKGETD %0, [%1]\n"
+ "TST %0, #1\n"
+ "MOV %0, #1\n"
+ "XORZ %0, %0, %0\n"
+ : "=&d" (ret)
+ : "da" (&lock->lock)
+ : "cc");
+ return ret;
+}
+
+static inline void arch_spin_lock(arch_spinlock_t *lock)
+{
+ int tmp;
+
+ asm volatile ("1: LNKGETD %0,[%1]\n"
+ " TST %0, #1\n"
+ " ADD %0, %0, #1\n"
+ " LNKSETDZ [%1], %0\n"
+ " BNZ 1b\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " BNZ 1b\n"
+ : "=&d" (tmp)
+ : "da" (&lock->lock)
+ : "cc");
+
+ smp_mb();
+}
+
+/* Returns 0 if failed to acquire lock */
+static inline int arch_spin_trylock(arch_spinlock_t *lock)
+{
+ int tmp;
+
+ asm volatile (" LNKGETD %0,[%1]\n"
+ " TST %0, #1\n"
+ " ADD %0, %0, #1\n"
+ " LNKSETDZ [%1], %0\n"
+ " BNZ 1f\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " MOV %0, #1\n"
+ "1: XORNZ %0, %0, %0\n"
+ : "=&d" (tmp)
+ : "da" (&lock->lock)
+ : "cc");
+
+ smp_mb();
+
+ return tmp;
+}
+
+static inline void arch_spin_unlock(arch_spinlock_t *lock)
+{
+ smp_mb();
+
+ asm volatile (" SETD [%0], %1\n"
+ :
+ : "da" (&lock->lock), "da" (0)
+ : "memory");
+}
+
+/*
+ * RWLOCKS
+ *
+ *
+ * Write locks are easy - we just set bit 31. When unlocking, we can
+ * just write zero since the lock is exclusively held.
+ */
+
+static inline void arch_write_lock(arch_rwlock_t *rw)
+{
+ int tmp;
+
+ asm volatile ("1: LNKGETD %0,[%1]\n"
+ " CMP %0, #0\n"
+ " ADD %0, %0, %2\n"
+ " LNKSETDZ [%1], %0\n"
+ " BNZ 1b\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " BNZ 1b\n"
+ : "=&d" (tmp)
+ : "da" (&rw->lock), "bd" (0x80000000)
+ : "cc");
+
+ smp_mb();
+}
+
+static inline int arch_write_trylock(arch_rwlock_t *rw)
+{
+ int tmp;
+
+ asm volatile (" LNKGETD %0,[%1]\n"
+ " CMP %0, #0\n"
+ " ADD %0, %0, %2\n"
+ " LNKSETDZ [%1], %0\n"
+ " BNZ 1f\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " MOV %0,#1\n"
+ "1: XORNZ %0, %0, %0\n"
+ : "=&d" (tmp)
+ : "da" (&rw->lock), "bd" (0x80000000)
+ : "cc");
+
+ smp_mb();
+
+ return tmp;
+}
+
+static inline void arch_write_unlock(arch_rwlock_t *rw)
+{
+ smp_mb();
+
+ asm volatile (" SETD [%0], %1\n"
+ :
+ : "da" (&rw->lock), "da" (0)
+ : "memory");
+}
+
+/* write_can_lock - would write_trylock() succeed? */
+static inline int arch_write_can_lock(arch_rwlock_t *rw)
+{
+ int ret;
+
+ asm volatile ("LNKGETD %0, [%1]\n"
+ "CMP %0, #0\n"
+ "MOV %0, #1\n"
+ "XORNZ %0, %0, %0\n"
+ : "=&d" (ret)
+ : "da" (&rw->lock)
+ : "cc");
+ return ret;
+}
+
+/*
+ * Read locks are a bit more hairy:
+ * - Exclusively load the lock value.
+ * - Increment it.
+ * - Store new lock value if positive, and we still own this location.
+ * If the value is negative, we've already failed.
+ * - If we failed to store the value, we want a negative result.
+ * - If we failed, try again.
+ * Unlocking is similarly hairy. We may have multiple read locks
+ * currently active. However, we know we won't have any write
+ * locks.
+ */
+static inline void arch_read_lock(arch_rwlock_t *rw)
+{
+ int tmp;
+
+ asm volatile ("1: LNKGETD %0,[%1]\n"
+ " ADDS %0, %0, #1\n"
+ " LNKSETDPL [%1], %0\n"
+ " BMI 1b\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " BNZ 1b\n"
+ : "=&d" (tmp)
+ : "da" (&rw->lock)
+ : "cc");
+
+ smp_mb();
+}
+
+static inline void arch_read_unlock(arch_rwlock_t *rw)
+{
+ int tmp;
+
+ smp_mb();
+
+ asm volatile ("1: LNKGETD %0,[%1]\n"
+ " SUB %0, %0, #1\n"
+ " LNKSETD [%1], %0\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " BNZ 1b\n"
+ : "=&d" (tmp)
+ : "da" (&rw->lock)
+ : "cc", "memory");
+}
+
+static inline int arch_read_trylock(arch_rwlock_t *rw)
+{
+ int tmp;
+
+ asm volatile (" LNKGETD %0,[%1]\n"
+ " ADDS %0, %0, #1\n"
+ " LNKSETDPL [%1], %0\n"
+ " BMI 1f\n"
+ " DEFR %0, TXSTAT\n"
+ " ANDT %0, %0, #HI(0x3f000000)\n"
+ " CMPT %0, #HI(0x02000000)\n"
+ " MOV %0,#1\n"
+ " BZ 2f\n"
+ "1: MOV %0,#0\n"
+ "2:\n"
+ : "=&d" (tmp)
+ : "da" (&rw->lock)
+ : "cc");
+
+ smp_mb();
+
+ return tmp;
+}
+
+/* read_can_lock - would read_trylock() succeed? */
+static inline int arch_read_can_lock(arch_rwlock_t *rw)
+{
+ int tmp;
+
+ asm volatile ("LNKGETD %0, [%1]\n"
+ "CMP %0, %2\n"
+ "MOV %0, #1\n"
+ "XORZ %0, %0, %0\n"
+ : "=&d" (tmp)
+ : "da" (&rw->lock), "bd" (0x80000000)
+ : "cc");
+ return tmp;
+}
+
+#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
+#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
+
+#define arch_spin_relax(lock) cpu_relax()
+#define arch_read_relax(lock) cpu_relax()
+#define arch_write_relax(lock) cpu_relax()
+
+#endif /* __ASM_SPINLOCK_LNKGET_H */