1/*
2 * SMP- and interrupt-safe semaphores..
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License.  See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 * (C) Copyright 1996  Linus Torvalds
9 * (C) Copyright 1998, 99, 2000, 01  Ralf Baechle
10 * (C) Copyright 1999, 2000  Silicon Graphics, Inc.
11 * Copyright (C) 2000, 01 MIPS Technologies, Inc.  All rights reserved.
12 */
13#ifndef _ASM_SEMAPHORE_H
14#define _ASM_SEMAPHORE_H
15
16#include <asm/system.h>
17#include <asm/atomic.h>
18#include <linux/spinlock.h>
19#include <linux/wait.h>
20#include <linux/config.h>
21#include <linux/rwsem.h>
22
23struct semaphore {
24#ifdef __MIPSEB__
25	atomic_t count;
26	atomic_t waking;
27#else
28	atomic_t waking;
29	atomic_t count;
30#endif
31	wait_queue_head_t wait;
32#if WAITQUEUE_DEBUG
33	long __magic;
34#endif
35} __attribute__((aligned(8)));
36
37#if WAITQUEUE_DEBUG
38# define __SEM_DEBUG_INIT(name) \
39		, (long)&(name).__magic
40#else
41# define __SEM_DEBUG_INIT(name)
42#endif
43
44#ifdef __MIPSEB__
45#define __SEMAPHORE_INITIALIZER(name,count) \
46{ ATOMIC_INIT(count), ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
47	__SEM_DEBUG_INIT(name) }
48#else
49#define __SEMAPHORE_INITIALIZER(name,count) \
50{ ATOMIC_INIT(0), ATOMIC_INIT(count), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
51	__SEM_DEBUG_INIT(name) }
52#endif
53
54#define __MUTEX_INITIALIZER(name) \
55	__SEMAPHORE_INITIALIZER(name,1)
56
57#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
58	struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
59
60#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
61#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
62
63static inline void sema_init (struct semaphore *sem, int val)
64{
65	atomic_set(&sem->count, val);
66	atomic_set(&sem->waking, 0);
67	init_waitqueue_head(&sem->wait);
68#if WAITQUEUE_DEBUG
69	sem->__magic = (long)&sem->__magic;
70#endif
71}
72
73static inline void init_MUTEX (struct semaphore *sem)
74{
75	sema_init(sem, 1);
76}
77
78static inline void init_MUTEX_LOCKED (struct semaphore *sem)
79{
80	sema_init(sem, 0);
81}
82
83asmlinkage void __down(struct semaphore * sem);
84asmlinkage int  __down_interruptible(struct semaphore * sem);
85asmlinkage int __down_trylock(struct semaphore * sem);
86asmlinkage void __up(struct semaphore * sem);
87
88static inline void down(struct semaphore * sem)
89{
90#if WAITQUEUE_DEBUG
91	CHECK_MAGIC(sem->__magic);
92#endif
93	if (atomic_dec_return(&sem->count) < 0)
94		__down(sem);
95}
96
97/*
98 * Interruptible try to acquire a semaphore.  If we obtained
99 * it, return zero.  If we were interrupted, returns -EINTR
100 */
101static inline int down_interruptible(struct semaphore * sem)
102{
103	int ret = 0;
104
105#if WAITQUEUE_DEBUG
106	CHECK_MAGIC(sem->__magic);
107#endif
108	if (atomic_dec_return(&sem->count) < 0)
109		ret = __down_interruptible(sem);
110	return ret;
111}
112
113#ifndef CONFIG_CPU_HAS_LLDSCD
114
115/*
116 * Non-blockingly attempt to down() a semaphore.
117 * Returns zero if we acquired it
118 */
119static inline int down_trylock(struct semaphore * sem)
120{
121	int ret = 0;
122	if (atomic_dec_return(&sem->count) < 0)
123		ret = __down_trylock(sem);
124	return ret;
125}
126
127#else
128
129/*
130 * down_trylock returns 0 on success, 1 if we failed to get the lock.
131 *
132 * We must manipulate count and waking simultaneously and atomically.
133 * Here, we do this by using lld/scd on the pair of 32-bit words.  This
134 * won't work on MIPS32 platforms, however, and must be rewritten.
135 *
136 * Pseudocode:
137 *
138 *   Decrement(sem->count)
139 *   If(sem->count >=0) {
140 *	Return(SUCCESS)			// resource is free
141 *   } else {
142 *	If(sem->waking <= 0) {		// if no wakeup pending
143 *	   Increment(sem->count)	// undo decrement
144 *	   Return(FAILURE)
145 *      } else {
146 *	   Decrement(sem->waking)	// otherwise "steal" wakeup
147 *	   Return(SUCCESS)
148 *	}
149 *   }
150 */
151static inline int down_trylock(struct semaphore * sem)
152{
153	long ret, tmp, tmp2, sub;
154
155#if WAITQUEUE_DEBUG
156	CHECK_MAGIC(sem->__magic);
157#endif
158
159	__asm__ __volatile__(
160	".set\tmips3\t\t\t# down_trylock\n"
161	"0:\tlld\t%1, %4\n\t"
162	"dli\t%3, 0x0000000100000000\n\t"
163	"dsubu\t%1, %3\n\t"
164	"li\t%0, 0\n\t"
165	"bgez\t%1, 2f\n\t"
166	"sll\t%2, %1, 0\n\t"
167	"blez\t%2, 1f\n\t"
168	"daddiu\t%1, %1, -1\n\t"
169	"b\t2f\n\t"
170	"1:\tdaddu\t%1, %1, %3\n"
171	"li\t%0, 1\n"
172	"2:\tscd\t%1, %4\n\t"
173	"beqz\t%1, 0b\n\t"
174	".set	mips0"
175	: "=&r"(ret), "=&r"(tmp), "=&r"(tmp2), "=&r"(sub)
176	: "m"(*sem)
177	: "memory");
178
179	return ret;
180}
181
182#endif /* CONFIG_CPU_HAS_LLDSCD */
183
184/*
185 * Note! This is subtle. We jump to wake people up only if
186 * the semaphore was negative (== somebody was waiting on it).
187 */
188static inline void up(struct semaphore * sem)
189{
190#if WAITQUEUE_DEBUG
191	CHECK_MAGIC(sem->__magic);
192#endif
193	if (atomic_inc_return(&sem->count) <= 0)
194		__up(sem);
195}
196
197static inline int sem_getcount(struct semaphore *sem)
198{
199	return atomic_read(&sem->count);
200}
201
202#endif /* _ASM_SEMAPHORE_H */
203