18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef __ASM_SPINLOCK_H
38c2ecf20Sopenharmony_ci#define __ASM_SPINLOCK_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <asm/barrier.h>
68c2ecf20Sopenharmony_ci#include <asm/ldcw.h>
78c2ecf20Sopenharmony_ci#include <asm/processor.h>
88c2ecf20Sopenharmony_ci#include <asm/spinlock_types.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic inline int arch_spin_is_locked(arch_spinlock_t *x)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	volatile unsigned int *a = __ldcw_align(x);
138c2ecf20Sopenharmony_ci	return READ_ONCE(*a) == 0;
148c2ecf20Sopenharmony_ci}
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic inline void arch_spin_lock(arch_spinlock_t *x)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	volatile unsigned int *a;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	a = __ldcw_align(x);
218c2ecf20Sopenharmony_ci	while (__ldcw(a) == 0)
228c2ecf20Sopenharmony_ci		while (*a == 0)
238c2ecf20Sopenharmony_ci			continue;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic inline void arch_spin_lock_flags(arch_spinlock_t *x,
278c2ecf20Sopenharmony_ci					unsigned long flags)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	volatile unsigned int *a;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	a = __ldcw_align(x);
328c2ecf20Sopenharmony_ci	while (__ldcw(a) == 0)
338c2ecf20Sopenharmony_ci		while (*a == 0)
348c2ecf20Sopenharmony_ci			if (flags & PSW_SM_I) {
358c2ecf20Sopenharmony_ci				local_irq_enable();
368c2ecf20Sopenharmony_ci				local_irq_disable();
378c2ecf20Sopenharmony_ci			}
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci#define arch_spin_lock_flags arch_spin_lock_flags
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline void arch_spin_unlock(arch_spinlock_t *x)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	volatile unsigned int *a;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	a = __ldcw_align(x);
468c2ecf20Sopenharmony_ci	/* Release with ordered store. */
478c2ecf20Sopenharmony_ci	__asm__ __volatile__("stw,ma %0,0(%1)" : : "r"(1), "r"(a) : "memory");
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic inline int arch_spin_trylock(arch_spinlock_t *x)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	volatile unsigned int *a;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	a = __ldcw_align(x);
558c2ecf20Sopenharmony_ci	return __ldcw(a) != 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * Read-write spinlocks, allowing multiple readers but only one writer.
608c2ecf20Sopenharmony_ci * Unfair locking as Writers could be starved indefinitely by Reader(s)
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * The spinlock itself is contained in @counter and access to it is
638c2ecf20Sopenharmony_ci * serialized with @lock_mutex.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* 1 - lock taken successfully */
678c2ecf20Sopenharmony_cistatic inline int arch_read_trylock(arch_rwlock_t *rw)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int ret = 0;
708c2ecf20Sopenharmony_ci	unsigned long flags;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	local_irq_save(flags);
738c2ecf20Sopenharmony_ci	arch_spin_lock(&(rw->lock_mutex));
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/*
768c2ecf20Sopenharmony_ci	 * zero means writer holds the lock exclusively, deny Reader.
778c2ecf20Sopenharmony_ci	 * Otherwise grant lock to first/subseq reader
788c2ecf20Sopenharmony_ci	 */
798c2ecf20Sopenharmony_ci	if (rw->counter > 0) {
808c2ecf20Sopenharmony_ci		rw->counter--;
818c2ecf20Sopenharmony_ci		ret = 1;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	arch_spin_unlock(&(rw->lock_mutex));
858c2ecf20Sopenharmony_ci	local_irq_restore(flags);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return ret;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* 1 - lock taken successfully */
918c2ecf20Sopenharmony_cistatic inline int arch_write_trylock(arch_rwlock_t *rw)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int ret = 0;
948c2ecf20Sopenharmony_ci	unsigned long flags;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	local_irq_save(flags);
978c2ecf20Sopenharmony_ci	arch_spin_lock(&(rw->lock_mutex));
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/*
1008c2ecf20Sopenharmony_ci	 * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
1018c2ecf20Sopenharmony_ci	 * deny writer. Otherwise if unlocked grant to writer
1028c2ecf20Sopenharmony_ci	 * Hence the claim that Linux rwlocks are unfair to writers.
1038c2ecf20Sopenharmony_ci	 * (can be starved for an indefinite time by readers).
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
1068c2ecf20Sopenharmony_ci		rw->counter = 0;
1078c2ecf20Sopenharmony_ci		ret = 1;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	arch_spin_unlock(&(rw->lock_mutex));
1108c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return ret;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic inline void arch_read_lock(arch_rwlock_t *rw)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	while (!arch_read_trylock(rw))
1188c2ecf20Sopenharmony_ci		cpu_relax();
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic inline void arch_write_lock(arch_rwlock_t *rw)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	while (!arch_write_trylock(rw))
1248c2ecf20Sopenharmony_ci		cpu_relax();
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic inline void arch_read_unlock(arch_rwlock_t *rw)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	unsigned long flags;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	local_irq_save(flags);
1328c2ecf20Sopenharmony_ci	arch_spin_lock(&(rw->lock_mutex));
1338c2ecf20Sopenharmony_ci	rw->counter++;
1348c2ecf20Sopenharmony_ci	arch_spin_unlock(&(rw->lock_mutex));
1358c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic inline void arch_write_unlock(arch_rwlock_t *rw)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	unsigned long flags;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	local_irq_save(flags);
1438c2ecf20Sopenharmony_ci	arch_spin_lock(&(rw->lock_mutex));
1448c2ecf20Sopenharmony_ci	rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
1458c2ecf20Sopenharmony_ci	arch_spin_unlock(&(rw->lock_mutex));
1468c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci#endif /* __ASM_SPINLOCK_H */
150