/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* 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, using version 2 of the License.             */
/*                                                                           */
/* 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 "common.h"
#include <asm/io.h>
#include <pnx833x.h>
#include <tmml.h>

#ifdef CONFIG_SMP
#error interrupt disable depth code is not compatible with SMP
#endif

#define VALID_CACHE_TYPE(t) \
	(t == tmosalCacheTypeUnified || t == tmosalCacheTypeData || t == tmosalCacheTypeInstruction)

#ifdef PERF_TESTING
tmosalStatistic_t osal_stat;	/* zeroed at insmod time */
spinlock_t osal_stat_lock = SPIN_LOCK_UNLOCKED;
#endif

#define  TMOSAL_DISABLE_INTERRUPT 8

static uint32_t gSavedLastIntStatus = 0;
static int32_t  gInterruptsDisabled = 0;
/* Misc API */

tmErrorCode_t tmosalInit(void)
{
	return TM_OK;
}
EXPORT_SYMBOL(tmosalInit);

void tmosalExit(void)
{
}
EXPORT_SYMBOL(tmosalExit);

tmErrorCode_t tmosalGetSWVersion(ptmSWVersion_t pVersionInfo)
{
	if (!pVersionInfo)
		return TMOSAL_ERR_BAD_PARAM;

	/* copied from pSoS version */
	pVersionInfo->compatibilityNr = TMOSAL_COMPATIBILITY_NR;
	pVersionInfo->majorVersionNr  = TMOSAL_MAJOR_VERSION_NR;
	pVersionInfo->minorVersionNr  = TMOSAL_MINOR_VERSION_NR;

	return TM_OK;
}
EXPORT_SYMBOL(tmosalGetSWVersion);


tmErrorCode_t tmosalDisableInterrupts(pUInt32 pioIntStatus)
{
    tmosalDisableScheduler();
    gSavedLastIntStatus = PNX833X_PIC_INT_PRIORITY;

    /* Shield from people disabling OSAL interrupts from OSAL interrupt context.*/
    if (gSavedLastIntStatus <= TMOSAL_DISABLE_INTERRUPT)
    {
        PNX833X_PIC_INT_PRIORITY = TMOSAL_DISABLE_INTERRUPT;
    }

    *pioIntStatus = gSavedLastIntStatus;
    gInterruptsDisabled++;
    return TM_OK;
}

EXPORT_SYMBOL(tmosalDisableInterrupts);

tmErrorCode_t tmosalGetLastInterruptStatus(pUInt32 pioIntStatus)
{
    tmErrorCode_t error = TM_OK;

    if (gInterruptsDisabled)
    {
    	*pioIntStatus = gSavedLastIntStatus;
    }
    else
    {
        error = TMOSAL_ERR_CONTEXT;
    }
    return error;
}
EXPORT_SYMBOL(tmosalGetLastInterruptStatus);

tmErrorCode_t tmosalEnableInterrupts(const UInt32 kIntStatus)
{
    if (--gInterruptsDisabled < 0)
    {
        BUG();
    }
    gSavedLastIntStatus = kIntStatus;
    PNX833X_PIC_INT_PRIORITY = kIntStatus;
    tmosalEnableScheduler();
    return TM_OK;
}
EXPORT_SYMBOL(tmosalEnableInterrupts);

tmErrorCode_t tmosalDisableInterruptsNoSysCall (pUInt32 pioIntStatus)
{
    return tmosalDisableInterrupts(pioIntStatus);
}
EXPORT_SYMBOL(tmosalDisableInterruptsNoSysCall);

tmErrorCode_t tmosalEnableInterruptsNoSysCall(const UInt32 kIntStatusDummy)
{
    return tmosalEnableInterrupts(kIntStatusDummy);
}
EXPORT_SYMBOL(tmosalEnableInterruptsNoSysCall);

tmErrorCode_t tmosalDisableScheduler(void)
{
    preempt_disable();	/* internal counter used */
    return TM_OK;
}
EXPORT_SYMBOL(tmosalDisableScheduler);

tmErrorCode_t tmosalEnableScheduler(void)
{
    preempt_enable();	/* internal counter used */
    return TM_OK;
}
EXPORT_SYMBOL(tmosalEnableScheduler);

tmErrorCode_t tmosalFlushWithWB(tmosalCacheType_t cacheType)
{
	if (!VALID_CACHE_TYPE(cacheType))
		return TMOSAL_ERR_BAD_CACHE_TYPE;

#ifdef CONFIG_DMA_NONCOHERENT
	/* We attempt to flush a huge range; that will be detected, and
	 * whole cache will be flushed instead */
	if (cacheType == tmosalCacheTypeData)
	{
		tmmlCacheFlush(0, ~0);
		tmmlCacheInvalidate(0, ~0);
	}
#endif

	return TM_OK;
}
EXPORT_SYMBOL(tmosalFlushWithWB);

tmErrorCode_t tmosalFlushWithoutWB(tmosalCacheType_t cacheType)
{
	if (!VALID_CACHE_TYPE(cacheType))
		return TMOSAL_ERR_BAD_CACHE_TYPE;

#ifdef CONFIG_DMA_NONCOHERENT
	/* We attempt to flush a huge range; that will be detected, and
	 * whole cache will be flushed instead */
	if (cacheType == tmosalCacheTypeData)
	{
		tmmlCacheInvalidate(0, ~0);
	}
#endif

	return TM_OK;
}
EXPORT_SYMBOL(tmosalFlushWithoutWB);

tmErrorCode_t tmosalCacheInvalidateWithWB(tmosalCacheType_t cacheType,
		pUInt32 pkAddress, UInt32 bytesNumber)
{
	if (!VALID_CACHE_TYPE(cacheType))
		return TMOSAL_ERR_BAD_CACHE_TYPE;

#ifdef CONFIG_DMA_NONCOHERENT
	if (cacheType == tmosalCacheTypeData)
	{
		tmmlCacheFlush(pkAddress, bytesNumber);
		tmmlCacheInvalidate(pkAddress, bytesNumber);
	}
#endif

	return TM_OK;
}
EXPORT_SYMBOL(tmosalCacheInvalidateWithWB);

tmErrorCode_t tmosalCacheInvalidateWithoutWB(tmosalCacheType_t cacheType,
		pUInt32 pkAddress, UInt32 bytesNumber)
{
	if (!VALID_CACHE_TYPE(cacheType))
		return TMOSAL_ERR_BAD_CACHE_TYPE;

#ifdef CONFIG_DMA_NONCOHERENT
	if (cacheType == tmosalCacheTypeData)
	{
		tmmlCacheInvalidate(pkAddress, bytesNumber);
	}
#endif

	return TM_OK;
}
EXPORT_SYMBOL(tmosalCacheInvalidateWithoutWB);

#ifdef PERF_TESTING
tmErrorCode_t tmosalGetStatistic( ptmosalStatistic_t pioStat)
{
	if (unlikely(!pioStat))
		return TMOSAL_ERR_BAD_PARAM;

	*pioStat = osal_stat;
	return TM_OK;
}
EXPORT_SYMBOL(tmosalGetStatistic);
#endif
