212 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are met:
 | |
|  *     * Redistributions of source code must retain the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer.
 | |
|  *     * Redistributions in binary form must reproduce the above copyright
 | |
|  *       notice, this list of conditions and the following disclaimer in the
 | |
|  *       documentation and/or other materials provided with the distribution.
 | |
|  *     * Neither the name of Code Aurora Forum nor
 | |
|  *       the names of its contributors may be used to endorse or promote
 | |
|  *       products derived from this software without specific prior written
 | |
|  *       permission.
 | |
|  *
 | |
|  * Alternatively, provided that this notice is retained in full, this software
 | |
|  * may be relicensed by the recipient under the terms of the GNU General Public
 | |
|  * License version 2 ("GPL") and only version 2, in which case the provisions of
 | |
|  * the GPL apply INSTEAD OF those given above.  If the recipient relicenses the
 | |
|  * software under the GPL, then the identification text in the MODULE_LICENSE
 | |
|  * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL".  Once a
 | |
|  * recipient changes the license terms to the GPL, subsequent recipients shall
 | |
|  * not relicense under alternate licensing terms, including the BSD or dual
 | |
|  * BSD/GPL terms.  In addition, the following license statement immediately
 | |
|  * below and between the words START and END shall also then apply when this
 | |
|  * software is relicensed under the GPL:
 | |
|  *
 | |
|  * START
 | |
|  *
 | |
|  * 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.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program; if not, write to the Free Software Foundation, Inc.,
 | |
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | |
|  *
 | |
|  * END
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | |
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 | |
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
|  * POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/err.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/string.h>
 | |
| 
 | |
| #include <asm/system.h>
 | |
| 
 | |
| #include <mach/remote_spinlock.h>
 | |
| #include <mach/dal.h>
 | |
| #include "smd_private.h"
 | |
| 
 | |
| #define SMEM_SPINLOCK_COUNT 8
 | |
| #define SMEM_SPINLOCK_ARRAY_SIZE (SMEM_SPINLOCK_COUNT * sizeof(uint32_t))
 | |
| 
 | |
| #if defined(CONFIG_ARCH_MSM7X30)
 | |
| static int remote_spinlock_smem_init(int id, _remote_spinlock_t *lock)
 | |
| {
 | |
| 	_remote_spinlock_t spinlock_start;
 | |
| 
 | |
| 	if (id >= SMEM_SPINLOCK_COUNT)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	spinlock_start = smem_alloc(SMEM_SPINLOCK_ARRAY,
 | |
| 				    SMEM_SPINLOCK_ARRAY_SIZE);
 | |
| 	if (spinlock_start == NULL)
 | |
| 		return -ENXIO;
 | |
| 
 | |
| 	*lock = spinlock_start + id;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| remote_spinlock_dal_init(const char *chunk_name, _remote_spinlock_t *lock)
 | |
| {
 | |
| 	void *dal_smem_start, *dal_smem_end;
 | |
| 	uint32_t dal_smem_size;
 | |
| 	struct dal_chunk_header *cur_header;
 | |
| 
 | |
| 	if (!chunk_name)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	dal_smem_start = smem_item(SMEM_DAL_AREA, &dal_smem_size);
 | |
| 	if (!dal_smem_start)
 | |
| 		return -ENXIO;
 | |
| 
 | |
| 	dal_smem_end = dal_smem_start + dal_smem_size;
 | |
| 
 | |
| 	/* Find first chunk header */
 | |
| 	cur_header = (struct dal_chunk_header *)
 | |
| 			(((uint32_t)dal_smem_start + (4095)) & ~4095);
 | |
| 	*lock = NULL;
 | |
| 	while (cur_header->size != 0
 | |
| 		&& ((uint32_t)(cur_header + 1) < (uint32_t)dal_smem_end)) {
 | |
| 
 | |
| 		/* Check if chunk name matches */
 | |
| 		if (!strncmp(cur_header->name, chunk_name,
 | |
| 						DAL_CHUNK_NAME_LENGTH)) {
 | |
| 			*lock = (_remote_spinlock_t)&cur_header->lock;
 | |
| 			return 0;
 | |
| 		}
 | |
| 		cur_header = (void *)cur_header + cur_header->size;
 | |
| 	}
 | |
| 
 | |
| 	pr_err("%s: DAL remote lock \"%s\" not found.\n", __func__,
 | |
| 		chunk_name);
 | |
| 	return -EINVAL;
 | |
| }
 | |
| 
 | |
| #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;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int _remote_spin_lock_init(remote_spin_lock_id_t id, _remote_spinlock_t *lock)
 | |
| {
 | |
| #if defined(CONFIG_ARCH_MSM7X30)
 | |
| 	BUG_ON(id == NULL);
 | |
| 
 | |
| 	if (id[0] == 'D' && id[1] == ':') {
 | |
| 		/* DAL chunk name starts after "D:" */
 | |
| 		return remote_spinlock_dal_init(&id[2], lock);
 | |
| 	} else if (id[0] == 'S' && id[1] == ':') {
 | |
| 		/* Single-digit SMEM lock ID follows "S:" */
 | |
| 		BUG_ON(id[3] != '\0');
 | |
| 		return remote_spinlock_smem_init((((uint8_t)id[2])-'0'), lock);
 | |
| 	} else
 | |
| 		return -EINVAL;
 | |
| #else
 | |
| 	_remote_spinlock_t spinlock_start;
 | |
| 
 | |
| 	if (id >= SMEM_SPINLOCK_COUNT)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	spinlock_start = smem_alloc(SMEM_SPINLOCK_ARRAY,
 | |
| 				    SMEM_SPINLOCK_ARRAY_SIZE);
 | |
| 	if (spinlock_start == NULL)
 | |
| 		return -ENXIO;
 | |
| 
 | |
| 	*lock = spinlock_start + id;
 | |
| 
 | |
| 	return 0;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #if defined(CONFIG_ARCH_MSM7X30)
 | |
| void _remote_spin_lock(_remote_spinlock_t *lock)
 | |
| {
 | |
| 	__raw_remote_dek_spin_lock((raw_remote_spinlock_t *) (*lock));
 | |
| }
 | |
| 
 | |
| void _remote_spin_unlock(_remote_spinlock_t *lock)
 | |
| {
 | |
| 	__raw_remote_dek_spin_unlock((raw_remote_spinlock_t *) (*lock));
 | |
| }
 | |
| #endif |