/* 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 #include #include #include #include #include #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