blob: 75b70f3b5913e7f9f5f4fe451ce039c287be6e02 [file] [log] [blame]
/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* Part of this this code is based on the standard ARM spinlock
* implementation (asm/spinlock.h) found in the 2.6.29 kernel.
*/
#ifndef __ASM__ARCH_QC_REMOTE_SPINLOCK_H
#define __ASM__ARCH_QC_REMOTE_SPINLOCK_H
#include <linux/io.h>
#include <linux/types.h>
/* Remote spinlock definitions. */
struct dek_spinlock {
volatile uint8_t self_lock;
volatile uint8_t other_lock;
volatile uint8_t next_yield;
uint8_t pad;
};
typedef union {
volatile uint32_t lock;
struct dek_spinlock dek;
} raw_remote_spinlock_t;
typedef raw_remote_spinlock_t *_remote_spinlock_t;
#define remote_spinlock_id_t const char *
#define SMEM_SPINLOCK_PID_APPS 1
static inline void __raw_remote_ex_spin_lock(raw_remote_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
"1: ldrex %0, [%1]\n"
" teq %0, #0\n"
" strexeq %0, %2, [%1]\n"
" teqeq %0, #0\n"
" bne 1b"
: "=&r" (tmp)
: "r" (&lock->lock), "r" (1)
: "cc");
smp_mb();
}
static inline int __raw_remote_ex_spin_trylock(raw_remote_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
" ldrex %0, [%1]\n"
" teq %0, #0\n"
" strexeq %0, %2, [%1]\n"
: "=&r" (tmp)
: "r" (&lock->lock), "r" (1)
: "cc");
if (tmp == 0) {
smp_mb();
return 1;
}
return 0;
}
static inline void __raw_remote_ex_spin_unlock(raw_remote_spinlock_t *lock)
{
smp_mb();
__asm__ __volatile__(
" str %1, [%0]\n"
:
: "r" (&lock->lock), "r" (0)
: "cc");
}
static inline void __raw_remote_swp_spin_lock(raw_remote_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
"1: swp %0, %2, [%1]\n"
" teq %0, #0\n"
" bne 1b"
: "=&r" (tmp)
: "r" (&lock->lock), "r" (1)
: "cc");
smp_mb();
}
static inline int __raw_remote_swp_spin_trylock(raw_remote_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
" swp %0, %2, [%1]\n"
: "=&r" (tmp)
: "r" (&lock->lock), "r" (1)
: "cc");
if (tmp == 0) {
smp_mb();
return 1;
}
return 0;
}
static inline void __raw_remote_swp_spin_unlock(raw_remote_spinlock_t *lock)
{
smp_mb();
__asm__ __volatile__(
" str %1, [%0]"
:
: "r" (&lock->lock), "r" (0)
: "cc");
}
#define DEK_LOCK_REQUEST 1
#define DEK_LOCK_YIELD (!DEK_LOCK_REQUEST)
#define DEK_YIELD_TURN_SELF 0
static inline void __raw_remote_dek_spin_lock(raw_remote_spinlock_t *lock)
{
lock->dek.self_lock = DEK_LOCK_REQUEST;
while (lock->dek.other_lock) {
if (lock->dek.next_yield == DEK_YIELD_TURN_SELF)
lock->dek.self_lock = DEK_LOCK_YIELD;
while (lock->dek.other_lock)
;
lock->dek.self_lock = DEK_LOCK_REQUEST;
}
lock->dek.next_yield = DEK_YIELD_TURN_SELF;
smp_mb();
}
static inline int __raw_remote_dek_spin_trylock(raw_remote_spinlock_t *lock)
{
lock->dek.self_lock = DEK_LOCK_REQUEST;
if (lock->dek.other_lock) {
lock->dek.self_lock = DEK_LOCK_YIELD;
return 0;
}
lock->dek.next_yield = DEK_YIELD_TURN_SELF;
smp_mb();
return 1;
}
static inline void __raw_remote_dek_spin_unlock(raw_remote_spinlock_t *lock)
{
smp_mb();
lock->dek.self_lock = DEK_LOCK_YIELD;
}
static inline int __raw_remote_dek_spin_release(raw_remote_spinlock_t *lock,
uint32_t pid)
{
return -EINVAL;
}
static inline void __raw_remote_sfpb_spin_lock(raw_remote_spinlock_t *lock)
{
do {
writel_relaxed(SMEM_SPINLOCK_PID_APPS, lock);
smp_mb();
} while (readl_relaxed(lock) != SMEM_SPINLOCK_PID_APPS);
}
static inline int __raw_remote_sfpb_spin_trylock(raw_remote_spinlock_t *lock)
{
return 1;
}
static inline void __raw_remote_sfpb_spin_unlock(raw_remote_spinlock_t *lock)
{
writel_relaxed(0, lock);
smp_mb();
}
/**
* Release spinlock if it is owned by @pid.
*
* This is only to be used for situations where the processor owning
* the spinlock has crashed and the spinlock must be released.
*
* @lock - lock structure
* @pid - processor ID of processor to release
*/
static inline int __raw_remote_gen_spin_release(raw_remote_spinlock_t *lock,
uint32_t pid)
{
int ret = 1;
if (readl_relaxed(&lock->lock) == pid) {
writel_relaxed(0, &lock->lock);
wmb();
ret = 0;
}
return ret;
}
#if defined(CONFIG_MSM_SMD) || defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB)
int _remote_spin_lock_init(remote_spinlock_id_t, _remote_spinlock_t *lock);
void _remote_spin_release_all(uint32_t pid);
#else
static inline
int _remote_spin_lock_init(remote_spinlock_id_t id, _remote_spinlock_t *lock)
{
return -EINVAL;
}
static inline void _remote_spin_release_all(uint32_t pid) {}
#endif
#if defined(CONFIG_MSM_REMOTE_SPINLOCK_DEKKERS)
/* Use Dekker's algorithm when LDREX/STREX and SWP are unavailable for
* shared memory */
#define _remote_spin_lock(lock) __raw_remote_dek_spin_lock(*lock)
#define _remote_spin_unlock(lock) __raw_remote_dek_spin_unlock(*lock)
#define _remote_spin_trylock(lock) __raw_remote_dek_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_dek_spin_release(*lock,\
pid)
#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SWP)
/* Use SWP-based locks when LDREX/STREX are unavailable for shared memory. */
#define _remote_spin_lock(lock) __raw_remote_swp_spin_lock(*lock)
#define _remote_spin_unlock(lock) __raw_remote_swp_spin_unlock(*lock)
#define _remote_spin_trylock(lock) __raw_remote_swp_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock,\
pid)
#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB)
/* Use SFPB Hardware Mutex Registers */
#define _remote_spin_lock(lock) __raw_remote_sfpb_spin_lock(*lock)
#define _remote_spin_unlock(lock) __raw_remote_sfpb_spin_unlock(*lock)
#define _remote_spin_trylock(lock) __raw_remote_sfpb_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock,\
pid)
#else
/* Use LDREX/STREX for shared memory locking, when available */
#define _remote_spin_lock(lock) __raw_remote_ex_spin_lock(*lock)
#define _remote_spin_unlock(lock) __raw_remote_ex_spin_unlock(*lock)
#define _remote_spin_trylock(lock) __raw_remote_ex_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock, \
pid)
#endif
/* Remote mutex definitions. */
typedef struct {
_remote_spinlock_t r_spinlock;
uint32_t delay_us;
} _remote_mutex_t;
struct remote_mutex_id {
remote_spinlock_id_t r_spinlock_id;
uint32_t delay_us;
};
#ifdef CONFIG_MSM_SMD
int _remote_mutex_init(struct remote_mutex_id *id, _remote_mutex_t *lock);
void _remote_mutex_lock(_remote_mutex_t *lock);
void _remote_mutex_unlock(_remote_mutex_t *lock);
int _remote_mutex_trylock(_remote_mutex_t *lock);
#else
static inline
int _remote_mutex_init(struct remote_mutex_id *id, _remote_mutex_t *lock)
{
return -EINVAL;
}
static inline void _remote_mutex_lock(_remote_mutex_t *lock) {}
static inline void _remote_mutex_unlock(_remote_mutex_t *lock) {}
static inline int _remote_mutex_trylock(_remote_mutex_t *lock)
{
return 0;
}
#endif
#endif /* __ASM__ARCH_QC_REMOTE_SPINLOCK_H */