OSHW-DEIMOS/SOFTWARE/A64-TERES/linux-a64/drivers/arisc/message_manager/message_manager.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

412 lines
12 KiB
C
Executable File

/*
* arch/arm/mach-sunxi/arisc/message_manager/message_manager.c
*
* Copyright (c) 2012 Allwinner.
* 2012-05-01 Written by sunny (sunny@allwinnertech.com).
* 2012-10-01 Written by superm (superm@allwinnertech.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/hwspinlock.h>
#include "message_manager_i.h"
/* the start and end of message pool */
static struct arisc_message *message_start;
static struct arisc_message *message_end;
/* spinlock for this module */
static spinlock_t msg_mgr_lock;
/* message cache manager */
static struct arisc_message_cache message_cache;
/* semaphore cache manager */
static struct arisc_semaphore_cache sem_cache;
static atomic_t sem_used_num;
static void *arisc_message_pool_base;
static u32 arisc_message_pool_size;
static struct hwspinlock *msg_mgr_hwlock;
/**
* initialize message manager.
* @para: none.
*
* returns: 0 if initialize succeeded, others if failed.
*/
int arisc_message_manager_init(void *addr, u32 size)
{
int i;
msg_mgr_hwlock = hwspin_lock_request_specific(SUNXI_MSG_HWSPINLOCK);
if (!msg_mgr_hwlock)
pr_err("%s,%d request hwspinlock faild!\n", __func__, __LINE__);
arisc_message_pool_base = addr;
arisc_message_pool_size = size;
/* initialize message pool start and end */
message_start = (struct arisc_message *)(arisc_message_pool_base);
message_end = (struct arisc_message *)(arisc_message_pool_base + arisc_message_pool_size);
memset((void *)message_start, 0, arisc_message_pool_size);
/* initialize message_cache */
for (i = 0; i < ARISC_MESSAGE_CACHED_MAX; i++) {
message_cache.cache[i] = NULL;
}
atomic_set(&(message_cache.number), 0);
/* initialzie semaphore allocator */
for (i = 0; i < ARISC_SEM_CACHE_MAX; i++) {
sem_cache.cache[i] = NULL;
}
atomic_set(&(sem_cache.number), 0);
atomic_set(&sem_used_num, 0);
/* initialize message manager spinlock */
spin_lock_init(&(msg_mgr_lock));
return 0;
}
/**
* exit message manager.
* @para: none.
*
* returns: 0 if exit succeeded, others if failed.
*/
int arisc_message_manager_exit(void)
{
int ret;
ret = hwspin_lock_free(msg_mgr_hwlock);
if (ret)
pr_err("%s,%d free hwlock faild!\n", __func__, __LINE__);
return 0;
}
static int arisc_semaphore_invalid(struct semaphore *psemaphore)
{
/* semaphore use system kmalloc, valid range check */
//if ((psemaphore >= ((struct semaphore *)(0xC0000000))) &&
// (psemaphore < ((struct semaphore *)(0xF0000000))))
if (psemaphore) {
/* valid arisc semaphore */
return 0;
}
/* invalid arisc semaphore */
return 1;
}
static struct semaphore *arisc_semaphore_allocate(void)
{
struct semaphore *sem = NULL;
unsigned long msg_flags;
/* try to allocate from cache first */
spin_lock_irqsave(&msg_mgr_lock, msg_flags);
if (atomic_read(&(sem_cache.number))) {
atomic_dec(&(sem_cache.number));
sem = sem_cache.cache[atomic_read(&(sem_cache.number))];
sem_cache.cache[atomic_read(&(sem_cache.number))] = NULL;
if (arisc_semaphore_invalid(sem)) {
ARISC_ERR("allocate cache semaphore [%p] invalid\n", sem);
}
}
spin_unlock_irqrestore(&msg_mgr_lock, msg_flags);
if (arisc_semaphore_invalid(sem)) {
/* cache allocate fail, allocate from kmem */
sem = kmalloc(sizeof(struct semaphore), GFP_KERNEL);
}
/* check allocate semaphore valid or not */
if (arisc_semaphore_invalid(sem)) {
ARISC_ERR("allocate semaphore [%p] invalid\n", sem);
return NULL;
}
/* initialize allocated semaphore */
sema_init(sem, 0);
atomic_inc(&sem_used_num);
return sem;
}
static int arisc_semaphore_free(struct semaphore *sem)
{
struct semaphore *free_sem = sem;
unsigned long msg_flags;
if (arisc_semaphore_invalid(free_sem)) {
ARISC_ERR("free semaphore [%p] invalid\n", free_sem);
return -1;
}
/* try to free semaphore to cache */
spin_lock_irqsave(&msg_mgr_lock, msg_flags);
if (atomic_read(&(sem_cache.number)) < ARISC_SEM_CACHE_MAX) {
sem_cache.cache[atomic_read(&(sem_cache.number))] = free_sem;
atomic_inc(&(sem_cache.number));
free_sem = NULL;
}
spin_unlock_irqrestore(&msg_mgr_lock, msg_flags);
/* try to free semaphore to kmem if free to cache fail */
if (free_sem) {
/* free to kmem */
kfree(free_sem);
}
atomic_dec(&sem_used_num);
return 0;
}
int arisc_semaphore_used_num_query(void)
{
return atomic_read(&sem_used_num);
}
static int arisc_message_invalid(struct arisc_message *pmessage)
{
if ((pmessage >= message_start) &&
(pmessage < message_end)) {
/* valid arisc message */
return 0;
}
/* invalid arisc message */
return 1;
}
/**
* allocate one message frame. mainly use for send message by message-box,
* the message frame allocate form messages pool shared memory area.
* @para: none.
*
* returns: the pointer of allocated message frame, NULL if failed;
*/
struct arisc_message *arisc_message_allocate(unsigned int msg_attr)
{
struct arisc_message *pmessage = NULL;
struct arisc_message *palloc = NULL;
unsigned long hwflags;
unsigned long msg_flags;
if (arisc_suspend_flag_query() && (msg_attr == ARISC_MESSAGE_ATTR_SOFTSYN)) {
msg_attr = ARISC_MESSAGE_ATTR_HARDSYN;
}
/* first find in message_cache */
spin_lock_irqsave(&msg_mgr_lock, msg_flags);
if (atomic_read(&(message_cache.number))) {
ARISC_INF("arisc message_cache.number = 0x%x.\n", atomic_read(&(message_cache.number)));
atomic_dec(&(message_cache.number));
palloc = message_cache.cache[atomic_read(&(message_cache.number))];
ARISC_INF("message [%p] allocate from message_cache\n", palloc);
if (arisc_message_invalid(palloc)) {
ARISC_ERR("allocate cache message [%p] invalid\n", palloc);
}
}
spin_unlock_irqrestore(&msg_mgr_lock, msg_flags);
if (arisc_message_invalid(palloc)) {
/*
* cached message_cache finded fail,
* use spinlock 0 to exclusive with arisc.
*/
hwspin_lock_timeout_irqsave(msg_mgr_hwlock, ARISC_SPINLOCK_TIMEOUT, &hwflags);
/* search from the start of message pool every time. */
pmessage = message_start;
while (pmessage < message_end) {
if (pmessage->state == ARISC_MESSAGE_FREED) {
/* find free message in message pool, allocate it */
palloc = pmessage;
palloc->state = ARISC_MESSAGE_ALLOCATED;
ARISC_INF("message [%p] allocate from message pool\n", palloc);
break;
}
/* next message frame */
pmessage++;
}
/* unlock hwspinlock 0 */
hwspin_unlock_irqrestore(msg_mgr_hwlock, &hwflags);
}
if (arisc_message_invalid(palloc)) {
ARISC_ERR("allocate message [%p] frame is invalid\n", palloc);
return NULL;
}
/* initialize messgae frame */
palloc->next = NULL;
palloc->attr = msg_attr;
if (msg_attr & ARISC_MESSAGE_ATTR_SOFTSYN) {
/* syn message,allocate one semaphore for private */
palloc->private = arisc_semaphore_allocate();
} else {
palloc->private = NULL;
}
return palloc;
}
/**
* free one message frame. mainly use for process message finished,
* free it to messages pool or add to free message queue.
* @pmessage: the pointer of free message frame.
*
* returns: none.
*/
void arisc_message_free(struct arisc_message *pmessage)
{
struct arisc_message *free_message = pmessage;
unsigned long hwflags;
unsigned long msg_flags;
/* check this message valid or not */
if (arisc_message_invalid(free_message)) {
ARISC_ERR("free invalid arisc message [%p]\n", free_message);
return;
}
if (free_message->attr & ARISC_MESSAGE_ATTR_SOFTSYN) {
/* free message semaphore first */
arisc_semaphore_free((struct semaphore *)(free_message->private));
free_message->private = NULL;
}
/* try to free to free_list first */
spin_lock_irqsave(&msg_mgr_lock, msg_flags);
if (atomic_read(&(message_cache.number)) < ARISC_MESSAGE_CACHED_MAX) {
ARISC_INF("insert message [%p] to message_cache\n", free_message);
ARISC_INF("message_cache number : %d\n", atomic_read(&(message_cache.number)));
/* cached this message, message state: ALLOCATED */
message_cache.cache[atomic_read(&(message_cache.number))] = free_message;
atomic_inc(&(message_cache.number));
free_message->next = NULL;
free_message->state = ARISC_MESSAGE_ALLOCATED;
free_message = NULL;
}
spin_unlock_irqrestore(&msg_mgr_lock, msg_flags);
/* try to free message to pool if free to cache fail */
if (free_message) {
/* free to message pool,set message state as FREED. */
hwspin_lock_timeout_irqsave(msg_mgr_hwlock, ARISC_SPINLOCK_TIMEOUT, &hwflags);
ARISC_INF("insert message [%p] to message pool\n", free_message);
free_message->state = ARISC_MESSAGE_FREED;
free_message->next = NULL;
hwspin_unlock_irqrestore(msg_mgr_hwlock, &hwflags);
}
}
/**
* notify system that one message coming.
* @pmessage: the pointer of coming message frame.
*
* returns: 0 if notify succeeded, other if failed.
*/
int arisc_message_coming_notify(struct arisc_message *pmessage)
{
int ret;
/* ac327 receive message to arisc */
ARISC_INF("-------------------------------------------------------------\n");
ARISC_INF(" MESSAGE FROM ARISC \n");
ARISC_INF("message addr : %p\n", pmessage);
ARISC_INF("message type : %x\n", pmessage->type);
ARISC_INF("message attr : %x\n", pmessage->attr);
ARISC_INF("-------------------------------------------------------------\n");
/* message per-process */
pmessage->state = ARISC_MESSAGE_PROCESSING;
/* process message */
switch (pmessage->type) {
case ARISC_AXP_INT_COMING_NOTIFY: {
ARISC_INF("pmu interrupt coming notify\n");
ret = arisc_axp_int_notify(pmessage);
pmessage->result = ret;
break;
}
case ARISC_AUDIO_PERDONE_NOTIFY: {
ARISC_INF("audio perdone notify\n");
ret = arisc_audio_perdone_notify(pmessage);
pmessage->result = ret;
break;
}
case ARISC_REPORT_ERR_INFO: {
ARISC_INF("arisc report error info\n");
ret = arisc_report_error_info(pmessage);
pmessage->result = ret;
break;
}
default : {
ARISC_ERR("invalid message type for ac327 process\n");
ARISC_ERR("message addr : %p\n", pmessage);
ARISC_ERR("message state : %x\n", pmessage->state);
ARISC_ERR("message attr : %x\n", pmessage->attr);
ARISC_ERR("message type : %x\n", pmessage->type);
ARISC_ERR("message result : %x\n", pmessage->result);
ret = -EINVAL;
break;
}
}
/* message post process */
pmessage->state = ARISC_MESSAGE_PROCESSED;
if ((pmessage->attr & ARISC_MESSAGE_ATTR_SOFTSYN) ||
(pmessage->attr & ARISC_MESSAGE_ATTR_HARDSYN)) {
/* synchronous message, should feedback process result */
arisc_hwmsgbox_feedback_message(pmessage, ARISC_SEND_MSG_TIMEOUT);
} else {
/*
* asyn message, no need feedback message result,
* free message directly.
*/
arisc_message_free(pmessage);
}
return ret;
}
struct arisc_message *arisc_message_map_to_cpux(u32 addr)
{
struct arisc_message *message;
message = (struct arisc_message *)((ptrdiff_t)addr + (ptrdiff_t)arisc_message_pool_base);
return message;
}
u32 arisc_message_map_to_cpus(struct arisc_message *message)
{
u32 value = (u32)((ptrdiff_t)message - (ptrdiff_t)arisc_message_pool_base);
return value;
}
int arisc_message_valid(struct arisc_message *pmessage)
{
if ((pmessage >= message_start) && (pmessage < message_end)) {
/* valid message */
return 1;
}
return 0;
}