/*
* Copyright (c) 2010 NVIDIA Corporation.
* 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 the NVIDIA Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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 HOLDER 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.
*
*/
/**
* @file
* @brief nVIDIA Driver Development Kit:
* Cross Proc Communication driver
*
* @b Description: Implements the interface to the NvDdk XPC.
*
*/
#include "nvrm_xpc.h"
#include "nvrm_memmgr.h"
#include "ap15rm_xpc_hw_private.h"
#include "nvrm_hardware_access.h"
#include "nvassert.h"
#include "ap15/ararb_sema.h"
#include "ap15/arictlr_arbgnt.h"
#include "nvrm_avp_shrd_interrupt.h"
// Minimum sdram offset required so that avp can access the address which is
// passed.
// AVP can not access the 0x0000:0000 to 0x0000:0040
enum { MIN_SDRAM_OFFSET = 0x100};
//There are only 32 arb semaphores
#define MAX_ARB_NUM 32
#define ARBSEMA_REG_READ(pArbSemaVirtAdd, reg) \
NV_READ32(pArbSemaVirtAdd + (ARB_SEMA_##reg##_0))
#define ARBSEMA_REG_WRITE(pArbSemaVirtAdd, reg, data) \
NV_WRITE32(pArbSemaVirtAdd + (ARB_SEMA_##reg##_0), (data));
#define ARBGNT_REG_READ(pArbGntVirtAdd, reg) \
NV_READ32(pArbGntVirtAdd + (ARBGNT_##reg##_0))
#define ARBGNT_REG_WRITE(pArbGntVirtAdd, reg, data) \
NV_WRITE32(pArbGntVirtAdd + (ARBGNT_##reg##_0), (data));
static NvOsInterruptHandle s_arbInterruptHandle = NULL;
// Combines the Processor Xpc system details. This contains the details of the
// receive/send message queue and messaging system.
typedef struct NvRmPrivXpcMessageRec
{
NvRmDeviceHandle hDevice;
// Hw mail box register.
CpuAvpHwMailBoxReg HwMailBoxReg;
} NvRmPrivXpcMessage;
typedef struct NvRmPrivXpcArbSemaRec
{
NvRmDeviceHandle hDevice;
NvU8 *pArbSemaVirtAddr;
NvU8 *pArbGntVirtAddr;
NvOsSemaphoreHandle semaphore[MAX_ARB_NUM];
NvOsMutexHandle mutex[MAX_ARB_NUM];
NvOsIntrMutexHandle hIntrMutex;
} NvRmPrivXpcArbSema;
static NvRmPrivXpcArbSema s_ArbSema;
//Forward declarations
static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice);
static void ArbSemaIsr(void *args);
NvU32 GetArbIdFromRmModuleId(NvRmModuleID modId);
/**
* Initialize the cpu avp hw mail box address and map the hw register address
* to virtual address.
* Thread Safety: Caller responsibility
*/
static NvError
InitializeCpuAvpHwMailBoxRegister(NvRmPrivXpcMessageHandle hXpcMessage)
{
NvError e;
NvRmPhysAddr ResourceSemaPhysAddr;
// Get base address of the hw mail box register. This register is in the set
// of resource semaphore module Id.
NvRmModuleGetBaseAddress(hXpcMessage->hDevice,
NVRM_MODULE_ID(NvRmModuleID_ResourceSema, 0),
&ResourceSemaPhysAddr, &hXpcMessage->HwMailBoxReg.BankSize);
// Map the base address to the virtual address.
hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL;
NV_CHECK_ERROR(NvRmPhysicalMemMap(
ResourceSemaPhysAddr, hXpcMessage->HwMailBoxReg.BankSize,
NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached,
(void **)&hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr));
NvRmPrivXpcHwResetOutbox(&hXpcMessage->HwMailBoxReg);
return NvSuccess;
}
/**
* DeInitialize the cpu avp hw mail box address and unmap the hw register address
* virtual address.
* Thread Safety: Caller responsibility
*/
static void DeInitializeCpuAvpHwMailBoxRegister(NvRmPrivXpcMessageHandle hXpcMessage)
{
// Unmap the hw register base virtual address
NvRmPhysicalMemUnmap(hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr,
hXpcMessage->HwMailBoxReg.BankSize);
hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL;
}
/**
* Create the cpu-avp messaging system.
* This function will call other helper function to create the messaging technique
* used for cpu-avp communication.
* Thread Safety: Caller responsibility
*/
static NvError
CreateCpuAvpMessagingSystem(NvRmPrivXpcMessageHandle hXpcMessage)
{
NvError Error = NvSuccess;
Error = InitializeCpuAvpHwMailBoxRegister(hXpcMessage);
#if NV_IS_AVP
hXpcMessage->HwMailBoxReg.IsCpu = NV_FALSE;
#else
hXpcMessage->HwMailBoxReg.IsCpu = NV_TRUE;
#endif
// If error found then destroy all the allocation and initialization,
if (Error)
DeInitializeCpuAvpHwMailBoxRegister(hXpcMessage);
return Error;
}
/**
* Destroy the cpu-avp messaging system.
* This function destroy all the allocation/initialization done for creating
* the cpu-avp messaging system.
* Thread Safety: Caller responsibility
*/
static void DestroyCpuAvpMessagingSystem(NvRmPrivXpcMessageHandle hXpcMessage)
{
// Destroy the cpu-avp hw mail box registers.
DeInitializeCpuAvpHwMailBoxRegister(hXpcMessage);
hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL;
hXpcMessage->HwMailBoxReg.BankSize = 0;
}
NvError
NvRmPrivXpcCreate(
NvRmDeviceHandle hDevice,
NvRmPrivXpcMessageHandle *phXpcMessage)
{
NvError Error = NvSuccess;
NvRmPrivXpcMessageHandle hNewXpcMsgHandle = NULL;
*phXpcMessage = NULL;
// Allocates the memory for the xpc message handle.
hNewXpcMsgHandle = NvOsAlloc(sizeof(*hNewXpcMsgHandle));
if (!hNewXpcMsgHandle)
{
return NvError_InsufficientMemory;
}
// Initialize all the members of the xpc message handle.
hNewXpcMsgHandle->hDevice = hDevice;
hNewXpcMsgHandle->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL;
hNewXpcMsgHandle->HwMailBoxReg.BankSize = 0;
// Create the messaging system between the processors.
Error = CreateCpuAvpMessagingSystem(hNewXpcMsgHandle);
// if error the destroy all allocations done here.
if (Error)
{
NvOsFree(hNewXpcMsgHandle);
hNewXpcMsgHandle = NULL;
}
#if NV_IS_AVP
Error = InitArbSemaSystem(hDevice);
if (Error)
{
NvOsFree(hNewXpcMsgHandle);
hNewXpcMsgHandle = NULL;
}
#endif
// Copy the new xpc message handle into the passed parameter.
*phXpcMessage = hNewXpcMsgHandle;
return Error;
}
/**
* Destroy the Rm Xpc message handle.
* Thread Safety: It is provided inside the function.
*/
void NvRmPrivXpcDestroy(NvRmPrivXpcMessageHandle hXpcMessage)
{
// If not a null pointer then destroy.
if (hXpcMessage)
{
// Destroy the messaging system between processor.
DestroyCpuAvpMessagingSystem(hXpcMessage);
// Free the allocated memory for the xpc message handle.
NvOsFree(hXpcMessage);
}
}
// Set the outbound mailbox with the given data. We might have to spin until
// it's safe to send the message.
NvError
NvRmPrivXpcSendMessage(NvRmPrivXpcMessageHandle hXpcMessage, NvU32 data)
{
NvRmPrivXpcHwSendMessageToTarget(&hXpcMessage->HwMailBoxReg, data);
return NvSuccess;
}
// Get the value currently in the inbox register. This read clears the incoming
// interrupt.
NvU32
NvRmPrivXpcGetMessage(NvRmPrivXpcMessageHandle hXpcMessage)
{
NvU32 data;
NvRmPrivXpcHwReceiveMessageFromTarget(&hXpcMessage->HwMailBoxReg, &data);
return data;
}
NvError NvRmXpcInitArbSemaSystem(NvRmDeviceHandle hDevice)
{
#if NV_IS_AVP
return NvSuccess;
#else
return InitArbSemaSystem(hDevice);
#endif
}
static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice)
{
NvOsInterruptHandler ArbSemaHandler;
NvRmPhysAddr ArbSemaBase, ArbGntBase;
NvU32 ArbSemaSize, ArbGntSize;
NvU32 irq;
NvError e;
NvU32 i = 0;
irq = NvRmGetIrqForLogicalInterrupt(
hDevice, NvRmModuleID_ArbitrationSema, 0);
ArbSemaHandler = ArbSemaIsr;
NV_CHECK_ERROR_CLEANUP(
NvRmInterruptRegister(hDevice, 1, &irq, &ArbSemaHandler,
hDevice, &s_arbInterruptHandle, NV_TRUE)
);
NvRmModuleGetBaseAddress(hDevice,
NVRM_MODULE_ID(NvRmModuleID_ArbitrationSema, 0),
&ArbSemaBase, &ArbSemaSize);
NvRmModuleGetBaseAddress(hDevice,
NVRM_MODULE_ID(NvRmPrivModuleID_InterruptArbGnt, 0),
&ArbGntBase, &ArbGntSize);
NV_CHECK_ERROR_CLEANUP(
NvRmPhysicalMemMap(ArbSemaBase, ArbSemaSize, NVOS_MEM_READ_WRITE,
NvOsMemAttribute_Uncached, (void**)&s_ArbSema.pArbSemaVirtAddr)
);
NV_CHECK_ERROR_CLEANUP(
NvRmPhysicalMemMap(ArbGntBase, ArbGntSize, NVOS_MEM_READ_WRITE,
NvOsMemAttribute_Uncached, (void**)&s_ArbSema.pArbGntVirtAddr)
);
//Initialize all the semaphores and mutexes
for (i=0;i>= 1;
i++;
} while (int_mask);
NvOsIntrMutexUnlock(s_ArbSema.hIntrMutex);
NvRmInterruptDone(s_arbInterruptHandle);
}
NvU32 GetArbIdFromRmModuleId(NvRmModuleID modId)
{
NvU32 arbId;
switch(modId)
{
case NvRmModuleID_BseA:
arbId = NvRmArbSema_Bsea;
break;
case NvRmModuleID_Vde:
default:
arbId = NvRmArbSema_Vde;
break;
}
return arbId;
}
void NvRmXpcModuleAcquire(NvRmModuleID modId)
{
NvU32 RequestedSemaNum;
NvU32 reg;
RequestedSemaNum = GetArbIdFromRmModuleId(modId);
NvOsMutexLock(s_ArbSema.mutex[RequestedSemaNum]);
NvOsIntrMutexLock(s_ArbSema.hIntrMutex);
//Try to grab the lock
ARBSEMA_REG_WRITE(s_ArbSema.pArbSemaVirtAddr, SMP_GET, 1 << RequestedSemaNum);
//Enable arb sema interrupt
#if NV_IS_AVP
reg = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, COP_ENABLE);
reg |= (1 << RequestedSemaNum);
ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, COP_ENABLE, reg);
#else
reg = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE);
reg |= (1 << RequestedSemaNum);
ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE, reg);
#endif
NvOsIntrMutexUnlock(s_ArbSema.hIntrMutex);
NvOsSemaphoreWait(s_ArbSema.semaphore[RequestedSemaNum]);
}
void NvRmXpcModuleRelease(NvRmModuleID modId)
{
NvU32 RequestedSemaNum;
RequestedSemaNum = GetArbIdFromRmModuleId(modId);
//Release the lock
ARBSEMA_REG_WRITE(s_ArbSema.pArbSemaVirtAddr, SMP_PUT, 1 << RequestedSemaNum);
NvOsMutexUnlock(s_ArbSema.mutex[RequestedSemaNum]);
}