diff options
Diffstat (limited to 'arch/arm/mach-tegra/nvrm/core')
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap15/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c | 432 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c | 165 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h | 92 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/common/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h | 270 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c | 39 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h | 208 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c | 1655 |
10 files changed, 2858 insertions, 13 deletions
diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/Makefile b/arch/arm/mach-tegra/nvrm/core/ap15/Makefile index 34a7465bd6f9..85675a55c0fa 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap15/Makefile +++ b/arch/arm/mach-tegra/nvrm/core/ap15/Makefile @@ -32,3 +32,5 @@ obj-y += ap16rm_reloctable.o obj-y += ap15rm_init.o obj-y += ap15rm_init_common.o obj-y += ap15rm_interrupt.o +obj-y += ap15rm_xpc.o +obj-y += ap15rm_xpc_hw_private.o diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c index c5f849c6da98..9392cdab7679 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c @@ -42,6 +42,7 @@ #include "nvrm_heap.h" #include "nvrm_pmu_private.h" #include "nvrm_processor.h" +#include "nvrm_xpc.h" #include "ap15rm_private.h" #include "nvrm_structure.h" #include "ap15rm_private.h" @@ -428,6 +429,11 @@ NvRmOpenNew(NvRmDeviceHandle *pHandle) } } } + err = NvRmXpcInitArbSemaSystem(rm); + if( err != NvSuccess ) + { + goto fail; + } /* assign the handle pointer */ *pHandle = rm; @@ -730,3 +736,4 @@ void NvRmPrivMcErrorMonitorStop( NvRmDeviceHandle rm ) } } + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c new file mode 100644 index 000000000000..8b92ae4b5933 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c @@ -0,0 +1,432 @@ +/* + * 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 <b>nVIDIA Driver Development Kit: + * Cross Proc Communication driver </b> + * + * @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<MAX_ARB_NUM;i++) + { + NV_CHECK_ERROR_CLEANUP( + NvOsSemaphoreCreate(&s_ArbSema.semaphore[i], 0) + ); + + NV_CHECK_ERROR_CLEANUP( + NvOsMutexCreate(&s_ArbSema.mutex[i]) + ); + } + + NV_CHECK_ERROR_CLEANUP( + NvOsIntrMutexCreate(&s_ArbSema.hIntrMutex) + ); + +fail: + + return e; +} + + +static void ArbSemaIsr(void *args) +{ + NvU32 int_mask, proc_int_enable, arb_gnt, i = 0; + + NvOsIntrMutexLock(s_ArbSema.hIntrMutex); + //Check which arb semaphores have been granted to this processor + arb_gnt = ARBSEMA_REG_READ(s_ArbSema.pArbSemaVirtAddr, SMP_GNT_ST); + + //Figure out which arb semaphores were signalled and then disable them. +#if NV_IS_AVP + proc_int_enable = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, COP_ENABLE); + int_mask = arb_gnt & proc_int_enable; + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, + COP_ENABLE, (proc_int_enable & ~int_mask)); +#else + proc_int_enable = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE); + int_mask = arb_gnt & proc_int_enable; + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, + CPU_ENABLE, (proc_int_enable & ~int_mask)); +#endif + + //Signal all the required semaphores + do + { + if (int_mask & 0x1) + { + NvOsSemaphoreSignal(s_ArbSema.semaphore[i]); + } + int_mask >>= 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]); +} diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c new file mode 100644 index 000000000000..ffd1dc5d6ebd --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c @@ -0,0 +1,165 @@ +/* + * 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 <b>nVIDIA Driver Development Kit: + * Cross Processor Communication driver </b> + * + * @b Description: Implements the cross processor communication Hw Access APIs + * + */ + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_hardware_access.h" +#include "ap15rm_xpc_hw_private.h" +#include "ap15/arres_sema.h" + +enum {MESSAGE_BOX_MESSAGE_LENGTH_BITS = 28}; +#define RESSEMA_REG_READ32(pResSemaHwRegVirtBaseAdd, reg) \ + NV_READ32((pResSemaHwRegVirtBaseAdd) + (RES_SEMA_##reg##_0)/4) + +#define RESSEMA_REG_WRITE32(pResSemaHwRegVirtBaseAdd, reg, val) \ + do { \ + NV_WRITE32(((pResSemaHwRegVirtBaseAdd) + ((RES_SEMA_##reg##_0)/4)), (val)); \ + } while(0) + +void NvRmPrivXpcHwResetOutbox(CpuAvpHwMailBoxReg *pHwMailBoxReg) +{ + NvU32 OutboxMessage; + NvU32 OutboxVal; + + OutboxMessage = 0; + + // Write Outbox in the message box + // Enable the Valid tag + // Enable interrupt +#if NV_IS_AVP + OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_DATA, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_CMD, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IE_IBE, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, TAG, 0, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, OutboxVal); +#else + OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_CMD, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_DATA, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, IE_OBE, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, TAG, 0, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, OutboxVal); +#endif +} + + +/** + * Send message to the target. + */ +void +NvRmPrivXpcHwSendMessageToTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr MessageAddress) +{ + NvU32 OutboxMessage; + NvU32 OutboxVal = 0; + + OutboxMessage = ((NvU32)(MessageAddress)) >> (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS); + + // Write Outbox in the message box + // Enable the Valid tag + // Enable interrupt +#if NV_IS_AVP + // !!! not sure why this would need to be read/modify/write +// OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_DATA, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_CMD, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, IE_IBF, FULL, OutboxVal); +// OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, IE_IBE, EMPTY, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, TAG, VALID, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, OutboxVal); +#else + // !!! not sure why this would need to be read/modify/write +// OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_CMD, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_DATA, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, IE_OBF, FULL, OutboxVal); +// OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, IE_OBE, EMPTY, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, TAG, VALID, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, OutboxVal); +#endif +} + + + +/** + * Receive message from the target. + */ +void +NvRmPrivXpcHwReceiveMessageFromTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr *pMessageAddress) +{ + NvU32 InboxMessage = 0; + NvU32 InboxVal; + + // Read the inbox. Lower 28 bit contains the message. +#if NV_IS_AVP + InboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, 0); +#else + InboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, 0); +#endif + if (InboxVal & NV_DRF_DEF(RES_SEMA, SHRD_INBOX, TAG, VALID)) + { + pHwMailBoxReg->MailBoxData = InboxVal; + } + + InboxVal = (pHwMailBoxReg->MailBoxData) & (0xFFFFFFFFUL >> (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS)); + InboxMessage = (InboxVal << (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS)); + + *pMessageAddress = InboxMessage; +} + + + + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h new file mode 100644 index 000000000000..c5822526b9c8 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h @@ -0,0 +1,92 @@ +/* + * 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 <b>nVIDIA Driver Development Kit: + * Priate Hw access function for XPC driver </b> + * + * @b Description: Defines the private interface functions for the xpc + * + */ + +#ifndef INCLUDED_RM_XPC_HW_PRIVATE_H +#define INCLUDED_RM_XPC_HW_PRIVATE_H + + +#include "nvcommon.h" +#include "nvrm_init.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Combines the cpu avp hw mail baox system information. +typedef struct CpuAvpHwMailBoxRegRec +{ + // Hw mail box register virtual base address. + NvU32 *pHwMailBoxRegBaseVirtAddr; + + // Bank size of the hw regsiter. + NvU32 BankSize; + + // Tells whether this is on cpu or on Avp + NvBool IsCpu; + + // Mail box data which was read last time. + NvU32 MailBoxData; +} CpuAvpHwMailBoxReg; + +void NvRmPrivXpcHwResetOutbox(CpuAvpHwMailBoxReg *pHwMailBoxReg); + +/** + * Send message to the target. + */ +void +NvRmPrivXpcHwSendMessageToTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr MessageAddress); + +/** + * Receive message from the target. + */ +void +NvRmPrivXpcHwReceiveMessageFromTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr *pMessageAddress); + + +#if defined(__cplusplus) + } +#endif + +#endif // INCLUDED_RM_XPC_HW_PRIVATE_H diff --git a/arch/arm/mach-tegra/nvrm/core/common/Makefile b/arch/arm/mach-tegra/nvrm/core/common/Makefile index fcce9a5c566f..55c7f3042dbd 100644 --- a/arch/arm/mach-tegra/nvrm/core/common/Makefile +++ b/arch/arm/mach-tegra/nvrm/core/common/Makefile @@ -28,3 +28,4 @@ obj-y += nvrm_power.o obj-y += nvrm_power_dfs.o obj-y += nvrm_rmctrace.o obj-y += nvrm_relocation_table.o +obj-y += nvrm_transport.o diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h b/arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h new file mode 100644 index 000000000000..e32033f6aa05 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h @@ -0,0 +1,270 @@ +/* + * 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. + * + */ + +#ifndef INCLUDED_NVRM_MESSAGE_H +#define INCLUDED_NVRM_MESSAGE_H + +#include "nvrm_memmgr.h" +#include "nvrm_module.h" +#include "nvrm_transport.h" +#include "nvrm_power.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +// Maximum message queue depth +enum {MAX_QUEUE_DEPTH = 5}; +// Maximum message length +enum {MAX_MESSAGE_LENGTH = 256}; +// Maximum argument size +enum {MAX_ARGS_SIZE = 220}; +// Max String length +enum {MAX_STRING_LENGTH = 200}; + +typedef struct NvRmRPCRec +{ + NvRmTransportHandle svcTransportHandle; + NvOsSemaphoreHandle TransportRecvSemId; + NvOsMutexHandle RecvLock; + NvRmDeviceHandle hRmDevice; + NvBool isConnected; +} NvRmRPC; + +typedef struct NvRmRPCRec *NvRmRPCHandle; + +void NvRmPrivProcessMessage(NvRmRPCHandle hRPCHandle, char *pRecvMessage, int messageLength); + +typedef enum +{ + NvRmMsg_MemHandleCreate = 0x0, + NvRmMsg_MemHandleCreate_Response, + NvRmMsg_MemHandleOpen, + NvRmMsg_MemHandleFree, + NvRmMsg_MemAlloc, + NvRmMsg_MemAlloc_Response, + NvRmMsg_MemPin, + NvRmMsg_MemPin_Response, + NvRmMsg_MemUnpin, + NvRmMsg_MemUnpin_Response, + NvRmMsg_MemGetAddress, + NvRmMsg_MemGetAddress_Response, + NvRmMsg_HandleFromId, + NvRmMsg_HandleFromId_Response, + NvRmMsg_PowerModuleClockControl, + NvRmMsg_PowerModuleClockControl_Response, + NvRmMsg_ModuleReset, + NvRmMsg_ModuleReset_Response, + NvRmMsg_PowerRegister, + NvRmMsg_PowerUnRegister, + NvRmMsg_PowerStarvationHint, + NvRmMsg_PowerBusyHint, + NvRmMsg_PowerBusyMultiHint, + NvRmMsg_PowerDfsGetState, + NvRmMsg_PowerDfsGetState_Response, + NvRmMsg_PowerResponse, + NvRmMsg_PowerModuleGetMaxFreq, + NvRmMsg_InitiateLP0, + NvRmMsg_InitiateLP0_Response, + NvRmMsg_RemotePrintf, + NvRmMsg_AttachModule, + NvRmMsg_AttachModule_Response, + NvRmMsg_DetachModule, + NvRmMsg_DetachModule_Response, + NvRmMsg_AVP_Reset, + NvRmMsg_Force32 = 0x7FFFFFFF +}NvRmMsg; + +typedef struct{ + NvRmMsg msg; + NvU32 size; +}NvRmMessage_HandleCreat; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; + NvError error; +}NvRmMessage_HandleCreatResponse; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; +}NvRmMessage_HandleFree; + +typedef struct{ + NvRmMsg msg; + NvError error; +}NvRmMessage_Response; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; + NvRmHeap Heaps[NvRmHeap_Num]; + NvU32 NumHeaps; + NvU32 Alignment; + NvOsMemAttribute Coherency; +}NvRmMessage_MemAlloc; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; + NvU32 Offset; +}NvRmMessage_GetAddress; + +typedef struct{ + NvRmMsg msg; + NvU32 address; +}NvRmMessage_GetAddressResponse; + +typedef struct{ + NvRmMsg msg; + NvU32 id; +}NvRmMessage_HandleFromId; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; +}NvRmMessage_Pin; + +typedef struct{ + NvRmMsg msg; + NvU32 address; +}NvRmMessage_PinResponse; + +typedef struct{ + NvRmMsg msg; + NvRmModuleID ModuleId; + NvU32 ClientId; + NvBool Enable; +}NvRmMessage_Module; + +typedef struct{ + NvRmMsg msg; + NvU32 clientId; + NvOsSemaphoreHandle eventSema; +}NvRmMessage_PowerRegister; + +typedef struct{ + NvRmMsg msg; + NvU32 clientId; +}NvRmMessage_PowerUnRegister; + +typedef struct{ + NvRmMsg msg; + NvRmDfsClockId clockId; + NvU32 clientId; + NvBool starving; +}NvRmMessage_PowerStarvationHint; + +typedef struct{ + NvRmMsg msg; + NvRmDfsClockId clockId; + NvU32 clientId; + NvU32 boostDurationMS; + NvRmFreqKHz boostKHz; +}NvRmMessage_PowerBusyHint; + +typedef struct{ + NvRmMsg msg; + NvU32 numHints; + NvU8 busyHints[MAX_STRING_LENGTH]; +}NvRmMessage_PowerBusyMultiHint; + +typedef struct{ + NvRmMsg msg; +}NvRmMessage_PowerDfsGetState; + +typedef struct{ + NvRmMsg msg; + NvError error; + NvU32 clientId; +}NvRmMessage_PowerRegister_Response; + +typedef struct{ + NvRmMsg msg; + NvRmDfsRunState state; +}NvRmMessage_PowerDfsGetState_Response; + +typedef struct{ + NvRmMsg msg; + NvRmModuleID moduleID; +}NvRmMessage_PowerModuleGetMaxFreq; + +typedef struct{ + NvRmMsg msg; + NvRmFreqKHz freqKHz; +}NvRmMessage_PowerModuleGetMaxFreq_Response; + +typedef struct{ + NvRmMsg msg; + NvU32 sourceAddr; + NvU32 bufferAddr; + NvU32 bufferSize; +} NvRmMessage_InitiateLP0; + +typedef struct{ + NvRmMsg msg; + const char string[MAX_STRING_LENGTH]; +} NvRmMessage_RemotePrintf; + + +typedef struct{ + NvRmMsg msg; + NvU32 entryAddress; + NvU32 size; + char args[MAX_ARGS_SIZE]; + NvU32 reason; +}NvRmMessage_AttachModule; + +typedef struct{ + NvRmMsg msg; + NvError error; +}NvRmMessage_AttachModuleResponse; + +typedef struct{ + NvRmMsg msg; + NvU32 reason; + NvU32 entryAddress; +}NvRmMessage_DetachModule; + +typedef struct{ + NvRmMsg msg; + NvError error; +}NvRmMessage_DetachModuleResponse; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c index 38b68621cd3f..756c806cc759 100644 --- a/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c @@ -271,6 +271,22 @@ do\ /*****************************************************************************/ +#if NVRM_DTT_RANGE_CHANGE_PRINTF + +#define DttRangeReport(T, pDtt) \ +do\ +{\ + NvOsDebugPrintf("DTT: T = %d, Range = %d (%d : %d)\n", \ + (T), (pDtt)->TcorePolicy.PolicyRange, \ + (pDtt)->TcorePolicy.LowLimit, (pDtt)->TcorePolicy.HighLimit); \ +} while(0) + +#else +#define DttRangeReport(T, pDtt) +#endif + +/*****************************************************************************/ + // DFS object static NvRmDfs s_Dfs; @@ -1698,8 +1714,6 @@ DttPolicyUpdate( NvS32 TemperatureC, NvRmDtt* pDtt) { - NvU32 Range = pDtt->TcorePolicy.PolicyRange; - if (hRm->ChipId.Id == 0x20) { NvRmPrivAp20DttPolicyUpdate(hRm, TemperatureC, pDtt); @@ -1716,14 +1730,6 @@ DttPolicyUpdate( pDtt->TcorePolicy.UpdateIntervalUs = NV_WAIT_INFINITE; pDtt->TcorePolicy.PolicyRange = 0; } - if (pDtt->UseIntr || (pDtt->TcorePolicy.PolicyRange != Range)) - { -#if NVRM_DTT_RANGE_CHANGE_PRINTF - NvOsDebugPrintf("DTT: T = %d, Range = %d (%d : %d)\n", - TemperatureC, pDtt->TcorePolicy.PolicyRange, - pDtt->TcorePolicy.LowLimit, pDtt->TcorePolicy.HighLimit); -#endif - } } static NvBool @@ -1734,6 +1740,7 @@ DttClockUpdate( { NvS32 TemperatureC; NvS32 LowLimit, HighLimit; + NvU32 OldRange; NvRmTzonePolicy Policy; // Check if thermal throttling is supported @@ -1749,6 +1756,7 @@ DttClockUpdate( NvOdmTmonTemperatureGet(pDtt->hOdmTcore, &TemperatureC)) { DttPolicyUpdate(pDfs->hRm, TemperatureC, pDtt); + DttRangeReport(TemperatureC, pDtt); LowLimit = pDtt->TcorePolicy.LowLimit; HighLimit = pDtt->TcorePolicy.HighLimit; @@ -1769,6 +1777,7 @@ DttClockUpdate( } // Update temperature monitoring policy + OldRange = pDtt->TcorePolicy.PolicyRange; if (!pDtt->UseIntr && NvOdmTmonTemperatureGet(pDtt->hOdmTcore, &TemperatureC)) { @@ -1787,6 +1796,12 @@ DttClockUpdate( pDtt->TcorePolicy.UpdateFlag = NV_FALSE; } NvOsIntrMutexUnlock(pDfs->hIntrMutex); + + // Report range change + if (!pDtt->UseIntr && (OldRange != pDtt->TcorePolicy.PolicyRange)) + { + DttRangeReport(TemperatureC, pDtt); + } } else { @@ -1828,10 +1843,8 @@ static void DttIntrCallback(void* args) NvOdmTmonConfigParam_IntrLimitLow, &LowLimit); (void)NvOdmTmonParameterConfig(pDtt->hOdmTcore, NvOdmTmonConfigParam_IntrLimitHigh, &HighLimit); + DttRangeReport(TemperatureC, pDtt); } - - NVRM_DFS_PRINTF(("Dtt Intr: T = %d, LowLimit = %d, HighLimit = %d\n", - TemperatureC, LowLimit, HighLimit)); } /*****************************************************************************/ diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h b/arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h new file mode 100644 index 000000000000..d74ab4e30776 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h @@ -0,0 +1,208 @@ +/* + * 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. + * + */ + +#ifndef NVRM_RPC_H +#define NVRM_RPC_H + +/* + * nvrm_cpu_avp_rpc_private.h defines the private implementation functions to facilitate + * communication between processors (cpu and avp). + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "nvrm_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** + * Initialize RPC + * + * Init the RPC. Both the service and client + * to the service must call this API before calling to create each endpoint of the connection + * via NvRmPrivRPCConnect + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hDeviceHandle rm device handle + * @param rpcPortName the port name + * @param hRPCHandle the RPC transport handle + * + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + */ + NvError NvRmPrivRPCInit( NvRmDeviceHandle hDeviceHandle, char* rpcPortName, NvRmRPCHandle *hRPCHandle ); +/** + * De-intialize the RPC and other resources. + * @param hRPCHandle the RPC transport handle + * + */ +void NvRmPrivRPCDeInit( NvRmRPCHandle hRPCHandle ); + +/** + * Connect to RPC port + * + * Creates one end of a RPC connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hRPCHandle the RPC transport handle + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + NvError NvRmPrivRPCConnect( NvRmRPCHandle hRPCHandle ); + + /** + * Connect to RPC port + * + * Creates one end of a RPC connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hRPCHandle the RPC transport handle + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + NvError NvRmPrivRPCWaitForConnect( NvRmRPCHandle hRPCHandle ); + /** + * Receive the message from the port. This will read the message if it is + * available for this port otherwise it will return the + * NvError_TransportMessageBoxEmpty error. + * + * @param hRPCHandle the RPC transport handle + * @param pMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * + * @retval NvSuccess Message received successfully. + * @retval NvError_NotInitialized hTransport is not open. + * @retval NvError_InvalidState The port is not connection state. + * @retval NvError_TransportMessageBoxEmpty The message box empty and not able + * to receive the message. + * @retval NvError_TransportIncompleteMessage The received message for this + * port is longer than the configured message length for this port. It copied + * the maximm size of the configured length of the message for this port and + * return the incomplete message buffer. + * @retval NvError_TransportMessageOverflow The port receives the message more + * than the configured queue depth count for this port and hence message + * overflow has been ocuured. + */ + + NvError NvRmPrivRPCRecvMsg( NvRmRPCHandle hRPCHandle, void* pMessageBuffer, NvU32 * pMessageSize ); + + /** + * Send Message. + * + * Sends a message to the other port which is connected to this port. + * Its a wrapper to rm transport send message + * + * @param hRPCHandle the RPC transport handle + * @param pMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * + */ +void +NvRmPrivRPCSendMsg(NvRmRPCHandle hRPCHandle, + void* pMessageBuffer, + NvU32 MessageSize); + +/** + * Send and Recieve message. + * + * Send and Recieve a message between port. + * Its a wrapper to rm transport send message with response + * + * @param hRPCHandle the RPC transport handle + * @param pRecvMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param MaxSize The maximum size in bytes that may be copied to the buffer + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * @param pSendMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * + */ +void +NvRmPrivRPCSendMsgWithResponse(NvRmRPCHandle hRPCHandle, + void* pRecvMessageBuffer, + NvU32 MaxSize, + NvU32 *pMessageSize, + void* pSendMessageBuffer, + NvU32 MessageSize); + + +/** + * Closes a transport connection. Proper closure of this connection requires + * that both the client and service call this API. Therefore, it is expected + * that the client and service message one another to coordinate the close. + * + */ +void NvRmPrivRPCClose(NvRmRPCHandle hRPCHandle); + +NvError NvRmPrivInitService(NvRmDeviceHandle hDeviceHandle); + +void NvRmPrivServiceDeInit(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c new file mode 100644 index 000000000000..ffc4f83196fc --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c @@ -0,0 +1,1655 @@ +/* + * 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 <b>NVIDIA Driver Development Kit: + * Transport API</b> + * + * @b Description: This is the implementation of Transport API, which + * implements a simple means to pass messages across a port name regardless of + * port exist in what processor (on same processor or other processor). + */ + +#include "nvrm_transport.h" +#include "nvrm_xpc.h" +#include "nvrm_interrupt.h" +#include "nvrm_rpc.h" +#include "nvutil.h" +#include "nvassert.h" +#include "nvcommon.h" +#include "avp.h" + +#define LOOPBACK_PROFILE 0 + +// indices where to save data for the loopback test +#define LOOP_CPU_SEND_INDEX 0 +#define LOOP_AVP_ISR_INDEX 1 +#define LOOP_AVP_RECV_INDEX 2 +#define LOOP_AVP_SEND_INDEX 3 +#define LOOP_CPU_ISR_INDEX 4 +#define LOOP_CPU_RECV_INDEX 5 + +#define SEMAPHORE_BASED_MUTUAL_EXCLUSION 0 + +enum {MAX_INT_FOR_TRANSPORT = 2}; + +// Interrupt bit index in the interrupt controller relocation table. +enum {CPU_TRANSPORT_INT_OBE = 1}; +enum {CPU_TRANSPORT_INT_IBF = 0}; +enum {AVP_TRANSPORT_INT_OBE = 0}; +enum {AVP_TRANSPORT_INT_IBF = 1}; + +// Some constraints parameter to develop the transport APIs. + +// Maximum port name length +enum {MAX_PORT_NAME_LENGTH = 16}; + +// Maximum possible message length between the ports +#define MAX_COMMAND_SIZE 16 + +// Message header size MessageCommand + port Name + message Length (24 Bytes) +enum {MESSAGE_HEADER_SIZE = 0x20}; + +// Maximum receive message queue depth +enum {MAX_MESSAGE_DEPTH = 30}; + +// Maximum time to wait for the response when open the port. +enum {MAX_OPEN_TIMEOUT_MS = 200}; + +// Try to resend the message after this time. +enum {MESSAGE_RETRY_AFTER_MS = 500 }; + +// Connection message transfer and response wait timeout. +enum {MAX_CONNECTION_TIMEOUT_MS = 500 }; + + + +// Transport Commands which uses to do the handshaking and message transfer +// between the processor. This commands are send to the remote processor +// when any type if transaction happens. +typedef enum +{ + TransportCmd_None = 0x0, + + // The first transport command from the cpu->avp will inform the + // avp of size of the buffer. + TransportCmd_SetBufferInfo, + + // Transport command for staring the connection process. + TransportCmd_Connect, + + // Transport command for disconnecting the port and deleting the port entry. + TransportCmd_Disconnect, + + // Transport command which used for normal message transfer to the port. + TransportCmd_Message, + + // When a command requires a response, the value in the command field will + // be changed by the called processor here to indicate that the response is ready. + TransportCmd_Response, + + TransportCmd_Force32 = 0x7FFFFFFF + +} TransportCmd; + + + +// Ports (endpoint) state. +typedef enum +{ + // Port is opened only. + PortState_Open = 0x1, + + // Port is waiting for connection. + PortState_Waiting, + + // Port is connected. + PortState_Connected, + + // Port has been disconnected from other side. You can pop out messages + // but you can't send anymore + PortState_Disconnected, + + // Set to destroy when there is someone waiting for a connection, but + // and a different thread calls to kill close the port. + PortState_Destroy, + + PortState_Force32 = 0x7FFFFFFF +} PortState; + + + +// Message list which will be queued in the port receive message queue. +typedef struct RmReceiveMessageRec +{ + // Length of message. + NvU32 MessageLength; + + // Fixed size message buffer where the receiving message will be store. + NvU8 MessageBuffer[MAX_MESSAGE_LENGTH]; +} RmReceiveMessage; + + +// Combines the information for keeping the received messages to the +// corresponding ports. +typedef struct MessageQueueRec +{ + // Receive message Q details to receive the message. We make the queue 1 extra bigger than the + // requested size, and then we can do lockless updates because only the Recv function modifies + // ReadIndex, and only the ISR modifies the WriteIndex + RmReceiveMessage *pReceiveMsg; + + volatile NvU16 ReadIndex; + volatile NvU16 WriteIndex; + + NvU16 QueueSize; + +} MessageQueue; + + + +// Combines all required information for the transport port. +// The port information contains the state, recv message q, message depth and +// message length. +typedef struct NvRmTransportRec +{ + // Name of the port, 1 exra byte for NULL termination + char PortName[MAX_PORT_NAME_LENGTH+1]; + + // The state of port whether this is open or connected or waiting for + // connection. + PortState State; + + // Receive message Box which contains the receive messages for this port. + MessageQueue RecvMessageQueue; + + // Semaphore which is signal after getting the message for that port. + // This is the client passed semaphore. + NvOsSemaphoreHandle hOnPushMsgSem; + + // Pointer to the partner port. If the connect is to a remote partner, + // then this pointer is NULL + NvRmTransportHandle hConnectedPort; + + // If this is a remote connection, this holds the remote ports "name" + NvU32 RemotePort; + + // save a copy of the rm handle. + NvRmDeviceHandle hRmDevice; + + struct NvRmTransportRec *pNext; + + // unlikely to be used members at the end + + // to be signalled when someone waits for a connector. + NvOsSemaphoreHandle hOnConnectSem; + +#if LOOPBACK_PROFILE + NvBool bLoopTest; +#endif + +} NvRmTransport; + + + +// Combines the common information for keeping the transport information and +// sending and receiving the messages. +typedef struct NvRmPrivPortsRec +{ + // Device handle. + NvRmDeviceHandle hDevice; + + // List of port names of the open ports in the system. + NvRmTransport *pPortHead; + + // Mutex for transport + NvOsMutexHandle mutex; + + NvRmMemHandle hMessageMem; + void *pTransmitMem; + void *pReceiveMem; + NvU32 MessageMemPhysAddr; + + NvRmPrivXpcMessageHandle hXpc; + + // if a message comes in, but the receiver's queue is full, + // then we don't clear the inbound message to allow another message + // and set this flag. We use 2 variables here, so we don't need a lock. + volatile NvU8 ReceiveBackPressureOn; + NvU8 ReceiveBackPressureOff; + +#if LOOPBACK_PROFILE + volatile NvU32 *pTimer; +#endif +} NvRmPrivPorts; + + +// !!! Fixme, this should be part of the rm handle. +static NvRmPrivPorts s_TransportInfo; + +extern NvU32 NvRmAvpPrivGetUncachedAddress(NvU32 addr); + +#define MESSAGE_QUEUE_SIZE_IN_BYTES ( sizeof(RmReceiveMessage) * (MAX_MESSAGE_DEPTH+1) ) +static NvU32 s_RpcAvpQueue[ (MESSAGE_QUEUE_SIZE_IN_BYTES + 3) / 4 ]; +static NvU32 s_RpcCpuQueue[ (MESSAGE_QUEUE_SIZE_IN_BYTES + 3) / 4 ]; +static struct NvRmTransportRec s_RpcAvpPortStruct; +static struct NvRmTransportRec s_RpcCpuPortStruct; + +static NvOsInterruptHandle s_TransportInterruptHandle = NULL; + +static NvRmTransportHandle +FindPort(NvRmDeviceHandle hDevice, char *pPortName); + +static NvError +NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, NvU32 *message, NvU32 MessageLength); + +static void HandleAVPResetMessage(NvRmDeviceHandle hDevice); + +// expect caller to handle mutex +static char *NvRmPrivTransportUniqueName(void) +{ + static char UniqueName[] = "aaaaaaaa+"; + NvU32 len = 8; + NvU32 i; + + // this will roll a new name until we hit zzzz:zzzz + // it's not unbounded, but it is a lot of names... + // Unique names end in a '+' which won't be allowed in supplied names, to avoid + // collision. + for (i=0; i < len; ++i) + { + ++UniqueName[i]; + if (UniqueName[i] != 'z') + { + break; + } + UniqueName[i] = 'a'; + + } + + return UniqueName; +} + + +/* Returns NV_TRUE if the message was inserted ok + * Returns NV_FALSE if message was not inserted because the queue is already full + + */static NvBool +InsertMessage(NvRmTransportHandle hPort, const NvU8 *message, const NvU32 MessageSize) +{ + NvU32 index; + NvU32 NextIndex; + + index = (NvU32)hPort->RecvMessageQueue.WriteIndex; + NextIndex = index + 1; + if (NextIndex == hPort->RecvMessageQueue.QueueSize) + NextIndex = 0; + + // check for full condition + if (NextIndex == hPort->RecvMessageQueue.ReadIndex) + return NV_FALSE; + + // copy in the message + NvOsMemcpy(hPort->RecvMessageQueue.pReceiveMsg[index].MessageBuffer, + message, + MessageSize); + hPort->RecvMessageQueue.pReceiveMsg[index].MessageLength = MessageSize; + + hPort->RecvMessageQueue.WriteIndex = (NvU16)NextIndex; + return NV_TRUE; +} + + +static void +ExtractMessage(NvRmTransportHandle hPort, NvU8 *message, NvU32 *pMessageSize, NvU32 MaxSize) +{ + NvU32 NextIndex; + NvU32 index = (NvU32)hPort->RecvMessageQueue.ReadIndex; + NvU32 size = hPort->RecvMessageQueue.pReceiveMsg[index].MessageLength; + + NextIndex = index + 1; + if (NextIndex == hPort->RecvMessageQueue.QueueSize) + NextIndex = 0; + + NV_ASSERT(index != hPort->RecvMessageQueue.WriteIndex); // assert on empty condition + NV_ASSERT(size <= MaxSize); + + *pMessageSize = size; + + // only do the copy and update if there is sufficient room, otherwise + // the caller will propogate an error up. + if (size > MaxSize) + { + return; + } + NvOsMemcpy(message, + hPort->RecvMessageQueue.pReceiveMsg[index].MessageBuffer, + size); + + hPort->RecvMessageQueue.ReadIndex = (NvU16)NextIndex; +} + + + +static void *s_TmpIsrMsgBuffer; + +/** + * Connect message + * [ Transport Command ] + * [ Remote Handle ] + * [ Port Name ] + * + * Response: + * [ Remote Handle ] <- [ Local Handle ] + */ + +static void +HandleConnectMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage) +{ + char PortName[MAX_PORT_NAME_LENGTH+1]; + NvU32 RemotePort; + NvRmTransportHandle hPort; + + RemotePort = pMessage[1]; + NvOsMemcpy(PortName, (void*)&pMessage[2], MAX_PORT_NAME_LENGTH); + PortName[MAX_PORT_NAME_LENGTH] = 0; + + // See if there is a local port with that name + hPort = FindPort(hDevice, PortName); + if (hPort && hPort->State == PortState_Waiting) + { + NvOsAtomicCompareExchange32((NvS32 *)&hPort->State, PortState_Waiting, PortState_Connected); + if (hPort->State == PortState_Connected) + { + hPort->RemotePort = RemotePort; + NvOsSemaphoreSignal(hPort->hOnConnectSem); + pMessage[1] = (NvU32)hPort; + } + else + { + pMessage[1] = 0; + } + } + else + { + pMessage[1] = 0; + } + pMessage[0] = TransportCmd_Response; +} + + + +/** + * Disconnect message + * [ Transport Command ] + * [ Local Handle ] + * + * Response: + * [ Local Handle ] <- 0 + */ +static void +HandleDisconnectMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage) +{ + NvRmTransportHandle hPort; + hPort = (NvRmTransportHandle)pMessage[1]; + + // !!! For sanity we should walk the list of open ports to make sure this is a valid port! + if (hPort && hPort->State == PortState_Connected) + { + hPort->State = PortState_Disconnected; + hPort->RemotePort = 0; + } + pMessage[1] = 0; + pMessage[0] = TransportCmd_None; +} + + +/** + * Disconnect message + * [ Transport Command ] + * [ Local Handle ] + * [ Message Length ] + * [ Message ] + * + * Response: + * [ Message Length ] <- NvSuccess + * [ Transport Command ] <- When we can accept a new message + */ + +static void +HandlePortMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage) +{ + NvRmTransportHandle hPort; + NvU32 MessageLength; + NvBool bSuccess; + + hPort = (NvRmTransportHandle)pMessage[1]; + MessageLength = pMessage[2]; + +#if LOOPBACK_PROFILE + if (hPort && hPort->bLoopTest) + { +# if NV_IS_AVP + pMessage[LOOP_AVP_ISR_INDEX + 3] = *s_TransportInfo.pTimer; +# else + pMessage[LOOP_CPU_ISR_INDEX + 3] = *s_TransportInfo.pTimer; +# endif + } +#endif + + + // !!! For sanity we should walk the list of open ports to make sure this is a valid port! + // Queue the message even if in the open state as presumably this should only have happened if + // due to a race condition with the transport connected messages. + if (hPort && (hPort->State == PortState_Connected || hPort->State == PortState_Open)) + { + bSuccess = InsertMessage(hPort, (NvU8*)&pMessage[3], MessageLength); + if (bSuccess) + { + if (hPort->hOnPushMsgSem) + NvOsSemaphoreSignal(hPort->hOnPushMsgSem); + pMessage[0] = TransportCmd_None; + } + else + { + ++s_TransportInfo.ReceiveBackPressureOn; + } + } +} + +static void +HandleAVPResetMessage(NvRmDeviceHandle hDevice) +{ + NvRmTransportHandle hPort; + + hPort = FindPort(hDevice,(char*)"RPC_CPU_PORT"); + if (hPort && (hPort->State == PortState_Connected || hPort->State == PortState_Open)) + { + NvU32 message; + message = NvRmMsg_AVP_Reset; + InsertMessage(hPort, (NvU8*)&message, sizeof(NvU32)); + if (hPort->hOnPushMsgSem) + NvOsSemaphoreSignal(hPort->hOnPushMsgSem); + else + NV_ASSERT(0); + } + else + NV_ASSERT(0); + +} + + +/** + * Handle the Inbox full interrupt. + */ +static void +InboxFullIsr(void *args) +{ + NvRmDeviceHandle hDevice = (NvRmDeviceHandle)args; + NvU32 MessageData; + NvU32 MessageCommand; + volatile NvU32 *pMessage; + + MessageData = NvRmPrivXpcGetMessage(s_TransportInfo.hXpc); + if(MessageData == AVP_WDT_RESET) + { + HandleAVPResetMessage(hDevice); + NvRmInterruptDone(s_TransportInterruptHandle); + return; + } + // if we're on the AVP, the first message we get will configure the message info + if (s_TransportInfo.MessageMemPhysAddr == 0) + { +#if NV_IS_AVP + MessageData = NvRmAvpPrivGetUncachedAddress(MessageData); +#else + MessageData = MessageData; +#endif + s_TransportInfo.MessageMemPhysAddr = MessageData; + s_TransportInfo.pReceiveMem = (void*)MessageData; + s_TransportInfo.pTransmitMem = (void *) (MessageData + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE); + // ack the message and return. + *(NvU32*)s_TransportInfo.pReceiveMem = TransportCmd_None; + return; + } + + // otherwise decode and dispatch the message. + + + if (s_TransportInfo.pReceiveMem == NULL) + { + /* QT/EMUTRANS takes this path. */ + NvRmMemRead(s_TransportInfo.hMessageMem, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, s_TmpIsrMsgBuffer, MAX_MESSAGE_LENGTH); + pMessage = s_TmpIsrMsgBuffer; + NvRmMemWrite(s_TransportInfo.hMessageMem, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, s_TmpIsrMsgBuffer, 2*sizeof(NvU32)); + } + else + { + pMessage = (NvU32*)s_TransportInfo.pReceiveMem; + } + + MessageCommand = pMessage[0]; + + switch (MessageCommand) + { + case TransportCmd_Connect: + HandleConnectMessage(hDevice, pMessage); + break; + + case TransportCmd_Disconnect: + HandleDisconnectMessage(hDevice, pMessage); + break; + + case TransportCmd_Message: + HandlePortMessage(hDevice, pMessage); + break; + + default: + NV_ASSERT(0); + } + + NvRmInterruptDone(s_TransportInterruptHandle); +} + + +/** + * Handle the outbox empty interrupt. + */ +static void +OutboxEmptyIsr(void *args) +{ + // !!! This is not currently used... ignore for now. Might be required if we find that we + // need to spin for long periods of time waiting for other end of the connection to consume + // messages. + // + NvRmInterruptDone(s_TransportInterruptHandle); +} + +static void +NvRmPrivProcIdGetProcessorInfo( + NvRmDeviceHandle hDevice, + NvRmModuleID *pProcModuleId) +{ +#if NV_IS_AVP + *pProcModuleId = NvRmModuleID_Avp; +#else + *pProcModuleId = NvRmModuleID_Cpu; +#endif +} + +/** + * Register for the transport interrupts. + */ +static NvError +RegisterTransportInterrupt(NvRmDeviceHandle hDevice) +{ + NvOsInterruptHandler DmaIntHandlers[MAX_INT_FOR_TRANSPORT]; + NvU32 IrqList[MAX_INT_FOR_TRANSPORT]; + NvRmModuleID ProcModuleId; + + if (s_TransportInterruptHandle) + { + return NvSuccess; + } + NvRmPrivProcIdGetProcessorInfo(hDevice, &ProcModuleId); + + if (ProcModuleId == NvRmModuleID_Cpu) + { + IrqList[0] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, CPU_TRANSPORT_INT_IBF); + IrqList[1] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, CPU_TRANSPORT_INT_OBE); + } + else + { + IrqList[0] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, AVP_TRANSPORT_INT_IBF); + IrqList[1] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, AVP_TRANSPORT_INT_OBE); + } + + /* There is no need for registering the OutboxEmptyIsr, so we only register + * one interrupt i.e. InboxFullIsr */ + DmaIntHandlers[0] = InboxFullIsr; + DmaIntHandlers[1] = OutboxEmptyIsr; + return NvRmInterruptRegister(hDevice, 1, IrqList, DmaIntHandlers, + hDevice, &s_TransportInterruptHandle, NV_TRUE); +} + +// allocate buffers to be used for sending/receiving messages. +static void +NvRmPrivTransportAllocBuffers(NvRmDeviceHandle hRmDevice) +{ +#if !NV_IS_AVP + // These buffers are always allocated on the CPU side. We'll pass the address over the AVP + // + + NvError Error = NvSuccess; + NvRmMemHandle hNewMemHandle = NULL; + + // Create memory handle + Error = NvRmMemHandleCreate(hRmDevice, &hNewMemHandle, (MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE)*2); + if (Error) + goto fail; + + // Allocates the memory from the Heap + Error = NvRmMemAlloc(hNewMemHandle, NULL, 0, + XPC_MESSAGE_ALIGNMENT_SIZE, NvOsMemAttribute_Uncached); + if (Error) + goto fail; + + s_TransportInfo.MessageMemPhysAddr = NvRmMemPin(hNewMemHandle); + + // If it is success to create the memory handle. + // We have to be able to get a mapping to this, because it is used at interrupt time! + s_TransportInfo.hMessageMem = hNewMemHandle; + Error = NvRmMemMap(hNewMemHandle, 0, + (MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE)*2, + NVOS_MEM_READ_WRITE, + &s_TransportInfo.pTransmitMem); + if (Error) + { + s_TransportInfo.pTransmitMem = NULL; + s_TransportInfo.pReceiveMem = NULL; + } + else + { + s_TransportInfo.pReceiveMem = (void *) (((NvUPtr)s_TransportInfo.pTransmitMem) + + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE); + } + + s_TransportInfo.hMessageMem = hNewMemHandle; + NvRmMemWr32(hNewMemHandle, 0, 0xdeadf00d); // set this non-zero to throttle messages to the avp till avp is ready. + NvRmMemWr32(hNewMemHandle, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, 0); + + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return; + + +fail: + NvRmMemHandleFree(hNewMemHandle); + s_TransportInfo.hMessageMem = NULL; + return; +#else + return; +#endif +} + + +static void +NvRmPrivTransportFreeBuffers(NvRmDeviceHandle hRmDevice) +{ +#if !NV_IS_AVP + NvRmMemHandleFree(s_TransportInfo.hMessageMem); +#endif +} + +static volatile NvBool s_Transport_Inited = NV_FALSE; + +/** + * Initialize the transport structures, this is callled once + * at NvRmOpen time. + */ +NvError NvRmTransportInit(NvRmDeviceHandle hRmDevice) +{ + NvError err; + + NvOsMemset(&s_TransportInfo, 0, sizeof(s_TransportInfo)); + s_TransportInfo.hDevice = hRmDevice; + + err = NvOsMutexCreate(&s_TransportInfo.mutex); + if (err) + goto fail; + +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + err = NvRmPrivXpcCreate(hRmDevice, &s_TransportInfo.hXpc); + if (err) + goto fail; + + NvRmPrivTransportAllocBuffers(hRmDevice); +#endif + + if (1) // Used in EMUTRANS mode where the buffers cannot be mapped. + { + s_TmpIsrMsgBuffer = NvOsAlloc(MAX_MESSAGE_LENGTH); + if (!s_TmpIsrMsgBuffer) + goto fail; + } + +#if LOOPBACK_PROFILE + { + NvU32 TimerAddr; + NvU32 TimerSize; + + NvRmModuleGetBaseAddress(hRmDevice, NvRmModuleID_TimerUs, &TimerAddr, &TimerSize); + // map the us counter + err = NvRmPhysicalMemMap(TimerAddr, TimerSize, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void*)&s_TransportInfo.pTimer); + if (err) + goto fail; + } + +#endif + +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + err = RegisterTransportInterrupt(hRmDevice); + if (err) + goto fail; +#endif + s_Transport_Inited = NV_TRUE; + return NvSuccess; + + +fail: +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + NvRmPrivXpcDestroy(s_TransportInfo.hXpc); + NvRmPrivTransportFreeBuffers(hRmDevice); +#endif + NvOsFree(s_TmpIsrMsgBuffer); + NvOsMutexDestroy(s_TransportInfo.mutex); + return err; +} + +/** + * DeInitialize the transport structures. + */ +void NvRmTransportDeInit(NvRmDeviceHandle hRmDevice) +{ + // Unregister the interrupts. +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + NvRmPrivXpcDestroy(s_TransportInfo.hXpc); + NvRmPrivTransportFreeBuffers(hRmDevice); + NvRmInterruptUnregister(hRmDevice, s_TransportInterruptHandle); + s_TransportInterruptHandle = NULL; +#endif + NvOsFree(s_TmpIsrMsgBuffer); + NvOsMutexDestroy(s_TransportInfo.mutex); +} + + +static void +InsertPort(NvRmDeviceHandle hDevice, NvRmTransportHandle hPort) +{ + hPort->pNext = s_TransportInfo.pPortHead; + s_TransportInfo.pPortHead = hPort; +} + + +static NvRmTransportHandle +FindPort(NvRmDeviceHandle hDevice, char *pPortName) +{ + NvRmTransportHandle hPort = NULL; + NvRmTransportHandle hIter = NULL; + + hIter = s_TransportInfo.pPortHead; + while (hIter) + { + if ( NvOsStrcmp(pPortName, hIter->PortName) == 0) + { + hPort = hIter; + break; + } + hIter = hIter->pNext; + } + + return hPort; +} + + +// Remove the given hPort from the list of ports +static void +DeletePort(NvRmDeviceHandle hRmDevice, const NvRmTransportHandle hPort) +{ + // Pointer to the pointer alleviates all special cases in linked list walking. + // I wish I was clever enough to have figured this out myself. + + NvRmTransportHandle *hIter; + + hIter = &s_TransportInfo.pPortHead; + while (*hIter) + { + if ( *hIter == hPort ) + { + *hIter = (*hIter)->pNext; + break; + } + hIter = &(*hIter)->pNext; + } +} + + + + +/** + * Open the port handle with a given port name. With the same name, only two + * port can be open. + * Thread Safety: It is done inside the function. + */ + +NvError +NvRmTransportOpen( + NvRmDeviceHandle hRmDevice, + char *pPortName, + NvOsSemaphoreHandle RecvMessageSemaphore, + NvRmTransportHandle *phTransport) +{ + NvU32 PortNameLen; + NvRmTransportHandle hPartner = NULL; + NvRmTransportHandle hPort = NULL; + NvError err = NvError_InsufficientMemory; + char TmpName[MAX_PORT_NAME_LENGTH+1]; + + while (!s_Transport_Inited) { + // This can happen, if this API is called before avp init. + NvOsSleepMS(500); + } + // Look and see if this port exists anywhere. + if (pPortName == NULL) + { + NvOsMutexLock(s_TransportInfo.mutex); + + pPortName = NvRmPrivTransportUniqueName(); + PortNameLen = NvOsStrlen(pPortName); + NvOsStrncpy(TmpName, pPortName, sizeof(TmpName) ); + pPortName = TmpName; + + NvOsMutexUnlock(s_TransportInfo.mutex); + } + else + { + PortNameLen = NvOsStrlen(pPortName); + NV_ASSERT(PortNameLen <= MAX_PORT_NAME_LENGTH); + } + + NvOsMutexLock(s_TransportInfo.mutex); + hPartner = FindPort(hRmDevice, pPortName); + + if (hPartner && hPartner->hConnectedPort != NULL) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvError_TransportPortAlreadyExist; + } + + // check if this is one of the special RPC ports used by the rm + if ( NvOsStrcmp(pPortName, "RPC_AVP_PORT") == 0) + { + //If someone else wants to open this port + //just return the one already created. + if (hPartner) + { + hPort = hPartner; + goto success; + } + else + { + hPort = &s_RpcAvpPortStruct; + hPort->RecvMessageQueue.pReceiveMsg = (void *)&s_RpcAvpQueue[0]; + } + } + else if (NvOsStrcmp(pPortName, "RPC_CPU_PORT") == 0) + { + hPort = &s_RpcCpuPortStruct; + hPort->RecvMessageQueue.pReceiveMsg = (void *)&s_RpcCpuQueue[0]; + } + else + { + // Create a new TransportPort + hPort = NvOsAlloc( sizeof(*hPort) ); + if (!hPort) + goto fail; + + NvOsMemset(hPort, 0, sizeof(*hPort) ); + + // Allocate the receive queue + hPort->RecvMessageQueue.pReceiveMsg = NvOsAlloc( sizeof(RmReceiveMessage) * (MAX_MESSAGE_DEPTH+1)); + if (!hPort->RecvMessageQueue.pReceiveMsg) + goto fail; + } + + NvOsStrncpy(hPort->PortName, pPortName, PortNameLen); + hPort->State = PortState_Open; + hPort->hConnectedPort = hPartner; + + if (RecvMessageSemaphore) + { + err = NvOsSemaphoreClone(RecvMessageSemaphore, &hPort->hOnPushMsgSem); + if (err) + goto fail; + } + + hPort->RecvMessageQueue.QueueSize = MAX_MESSAGE_DEPTH+1; + hPort->hRmDevice = hRmDevice; + + if (hPort->hConnectedPort != NULL) + { + hPort->hConnectedPort->hConnectedPort = hPort; + } + InsertPort(hRmDevice, hPort); + + + // !!! loopback info +#if LOOPBACK_PROFILE + if (NvOsStrcmp(hPort->PortName, "LOOPTEST") == 0) + hPort->bLoopTest = 1; +#endif + +success: + NvOsMutexUnlock(s_TransportInfo.mutex); + *phTransport = hPort; + return NvSuccess; + +fail: + if (hPort) + { + NvOsFree(hPort->RecvMessageQueue.pReceiveMsg); + NvOsSemaphoreDestroy(hPort->hOnPushMsgSem); + NvOsFree(hPort); + hPort = NULL; + } + NvOsMutexUnlock(s_TransportInfo.mutex); + + return err; +} + + +/** + * Close the transport handle + * Thread Safety: It is done inside the function. + */ +void NvRmTransportClose(NvRmTransportHandle hPort) +{ + NvU32 RemoteMessage[4]; + + if (!hPort) + return; + + // Look and see if this port exists anywhere. + NV_ASSERT(hPort); + + + NvOsMutexLock(s_TransportInfo.mutex); + DeletePort(hPort->hRmDevice, hPort); // unlink this port + + // Check if there is already a port waiting to connect, and if there is + // switch the port state to _Destroy, and signal the waiters semaphore. + // The "State" member is not protected by the mutex because it can be + // updated by the ISR. + while (hPort->State == PortState_Waiting) + { + NvOsAtomicCompareExchange32((NvS32*)&hPort->State, PortState_Waiting, PortState_Destroy); + if (hPort->State == PortState_Destroy) + { + NvOsSemaphoreSignal(hPort->hOnConnectSem); + + // in this case, we can't complete the destroy, the signalled thread will + // have to complete. We just return now + NvOsMutexUnlock(s_TransportInfo.mutex); + return; + } + } + + if (hPort->hConnectedPort) + { + // unlink this port from the other side of the connection. + hPort->hConnectedPort->hConnectedPort = NULL; + } + + if (hPort->RemotePort) + { + RemoteMessage[0] = TransportCmd_Disconnect; + RemoteMessage[1] = hPort->RemotePort; + NvRmPrivTransportSendMessage(hPort->hRmDevice, RemoteMessage, 2*sizeof(NvU32)); + } + + NvOsSemaphoreDestroy(hPort->hOnPushMsgSem); + + + if (hPort == &s_RpcAvpPortStruct || + hPort == &s_RpcCpuPortStruct) + { + // don't free these.. + NvOsMemset(hPort, 0, sizeof(*hPort)); + } + else + { + NvOsFree(hPort->RecvMessageQueue.pReceiveMsg); + NvOsFree(hPort); + } + + NvOsMutexUnlock(s_TransportInfo.mutex); +} + + +/** + * Wait for the connection to the other end. + * Thread Safety: It is done inside the function. + */ +NvError +NvRmTransportWaitForConnect( + NvRmTransportHandle hPort, + NvU32 TimeoutMS) +{ + NvOsSemaphoreHandle hSem = NULL; + NvError err = NvSuccess; + + NvOsMutexLock(s_TransportInfo.mutex); + if (hPort->State != PortState_Open) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + err = NvError_TransportPortAlreadyExist; + goto exit_gracefully; + } + + err = NvOsSemaphoreCreate(&hSem, 0); + if (err) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + goto exit_gracefully; + } + + hPort->hOnConnectSem = hSem; + hPort->State = PortState_Waiting; + NvOsMutexUnlock(s_TransportInfo.mutex); + + err = NvOsSemaphoreWaitTimeout(hSem, TimeoutMS); + if (err) + { + // we have to be careful here, the ISR _might_ happen just after the semaphore + // times out. + NvOsAtomicCompareExchange32((NvS32 *)&hPort->State, PortState_Waiting, PortState_Open); + NV_ASSERT(hPort->State == PortState_Open || hPort->State == PortState_Connected); + if (hPort->State == PortState_Connected) + { + err = NvSuccess; + } + } + + NvOsMutexLock(s_TransportInfo.mutex); + hPort->hOnConnectSem = NULL; + NvOsMutexUnlock(s_TransportInfo.mutex); + + if (hPort->State == PortState_Destroy) + { + // finish the destroy process + NvRmTransportClose(hPort); + err = NvError_TransportConnectionFailed; + } + +exit_gracefully: + NvOsSemaphoreDestroy(hSem); + return err; +} + + + +static NvError +NvRmPrivTransportWaitResponse(NvRmDeviceHandle hDevice, NvU32 *response, NvU32 ResponseLength, NvU32 TimeoutMS) +{ + NvU32 CurrentTime; + NvU32 StartTime; + NvU32 Response; + NvBool GotResponse = NV_TRUE; + NvError err = NvError_Timeout; + volatile NvU32 *pXpcMessage = (volatile NvU32*)s_TransportInfo.pTransmitMem; + + if (pXpcMessage == NULL) + { + if (!NV_IS_AVP) + { + Response = NvRmMemRd32(s_TransportInfo.hMessageMem, 0); + } else + { + NV_ASSERT(0); + return NvSuccess; + } + } + else + { + Response = pXpcMessage[0]; + } + + if (Response != TransportCmd_Response) + { + GotResponse = NV_FALSE; + + // response is not back yet, so spin till its here. + StartTime = NvOsGetTimeMS(); + CurrentTime = StartTime; + while ( (CurrentTime - StartTime) < TimeoutMS ) + { + if ( pXpcMessage && (pXpcMessage[0] == TransportCmd_Response) ) + { + GotResponse = NV_TRUE; + break; + } + else if ( !pXpcMessage ) + { + NV_ASSERT(!"Invalid pXpcMessage pointer is accessed"); + } + CurrentTime = NvOsGetTimeMS(); + } + } + + if ( pXpcMessage && GotResponse ) + { + err = NvSuccess; + NvOsMemcpy(response, (void *)pXpcMessage, ResponseLength); + } + + return err; +} + + +static NvError +NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, NvU32 *message, NvU32 MessageLength) +{ + NvU32 ReadData; + + if (s_TransportInfo.pTransmitMem == NULL) + { + /* QT/EMUTRANS takes this code path */ + if (!NV_IS_AVP) + { + ReadData = NvRmMemRd32(s_TransportInfo.hMessageMem, 0); + } else + { + NV_ASSERT(0); + return NvSuccess; + } + } + else + { + ReadData = ((NvU32*)s_TransportInfo.pTransmitMem)[0]; + } + + // Check for clear to send + if ( ReadData != 0) + return NvError_TransportMessageBoxFull; // someone else is sending a message + + if (s_TransportInfo.pTransmitMem == NULL) + { + /* QT/EMUTRANS takes this code path */ + NvRmMemWrite(s_TransportInfo.hMessageMem, 0, message, MessageLength); + } + else + { + NvOsMemcpy(s_TransportInfo.pTransmitMem, message, MessageLength); + NvOsFlushWriteCombineBuffer(); + } + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return NvSuccess; +} + +NvError +NvRmTransportSendMsgInLP0(NvRmTransportHandle hPort, void *pMessageBuffer, NvU32 MessageSize) +{ + NvU32 ReadData; + NvU32 Message[3 + ((MAX_MESSAGE_LENGTH) / sizeof(NvU32))]; + + NV_ASSERT(pMessageBuffer); + + Message[0] = TransportCmd_Message; + Message[1] = hPort->RemotePort; + Message[2] = MessageSize; + + if (MessageSize) + NvOsMemcpy(&Message[3], pMessageBuffer, MessageSize); + + ReadData = ((NvU32*)s_TransportInfo.pTransmitMem)[0]; + + // Check for clear to send + if ( ReadData != 0) + return NvError_TransportMessageBoxFull; // someone else is sending a message + + NvOsMemcpy(s_TransportInfo.pTransmitMem, Message, MessageSize + 3*sizeof(NvU32)); + NvOsFlushWriteCombineBuffer(); + + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return NvSuccess; +} + +static void +NvRmPrivTransportClearSend(NvRmDeviceHandle hDevice) +{ + if (s_TransportInfo.pTransmitMem == NULL) + { + /* QT/EMUTRANS take this path */ + if (!NV_IS_AVP) + { + NvRmMemWr32(s_TransportInfo.hMessageMem, 0, TransportCmd_None); + } else + { + NV_ASSERT(0); + } + } + else + { + ((NvU32*)s_TransportInfo.pTransmitMem)[0] = TransportCmd_None; + } +} + +/** + * Make the connection to the other end. + * Thread Safety: It is done inside the function. + */ +NvError NvRmTransportConnect(NvRmTransportHandle hPort, NvU32 TimeoutMS) +{ + NvRmTransportHandle hPartnerPort; + NvU32 StartTime; + NvU32 CurrentTime; + NvU32 ConnectMessage[ MAX_PORT_NAME_LENGTH/4 + 3]; + NvError err; + + + // Look and see if there is a local port with the same name that is currently waiting, if there is + // mark both ports as connected. + + NV_ASSERT(hPort); + NV_ASSERT(hPort->hRmDevice); + NV_ASSERT(hPort->State == PortState_Open); + + + StartTime = NvOsGetTimeMS(); + for (;;) + { + // Someone is waiting for a connection here locally. + NvOsMutexLock(s_TransportInfo.mutex); + + hPartnerPort = hPort->hConnectedPort; + if (hPartnerPort) + { + // Found a local connection + if (hPartnerPort->State == PortState_Waiting) + { + + hPartnerPort->State = PortState_Connected; + hPartnerPort->hConnectedPort = hPort; + + hPort->State = PortState_Connected; + NvOsSemaphoreSignal(hPartnerPort->hOnConnectSem); + break; + } + } + else if (s_TransportInfo.hMessageMem || s_TransportInfo.pReceiveMem) // if no shared buffer, then we can't create a remote connection. + { + ConnectMessage[0] = TransportCmd_Connect; + ConnectMessage[1] = (NvU32)hPort; + NvOsMemcpy(&ConnectMessage[2], hPort->PortName, MAX_PORT_NAME_LENGTH); + + err = NvRmPrivTransportSendMessage(hPort->hRmDevice, ConnectMessage, sizeof(ConnectMessage)); + if (!err) + { + // should send back 2 words of data. Give remote side 1000ms to respond, which should be about 100x more + // than it needs. + NvU32 WaitTime = NV_MAX(1000, TimeoutMS); + if (TimeoutMS == NV_WAIT_INFINITE) + TimeoutMS = NV_WAIT_INFINITE; + + // !!! Note, we can do this without holding the mutex... + err = NvRmPrivTransportWaitResponse(hPort->hRmDevice, ConnectMessage, 2*sizeof(NvU32), WaitTime); + NvRmPrivTransportClearSend(hPort->hRmDevice); + if (err) + { + // the other side is not responding to messages, doh! + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvError_TransportConnectionFailed; + } + + // check the response + hPort->RemotePort = ConnectMessage[1]; + if (hPort->RemotePort != 0) + { + hPort->State = PortState_Connected; + break; + } + } + } + NvOsMutexUnlock(s_TransportInfo.mutex); + NV_ASSERT(hPort->State == PortState_Open); // it better still be open + + // Didn't find a connection, wait a few ms and then try again + CurrentTime = NvOsGetTimeMS(); + if ( (CurrentTime - StartTime) > TimeoutMS ) + return NvError_Timeout; + + NvOsSleepMS(10); + } + + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvSuccess; +} + + +/** + * Set the queue depth and message size of the transport handle. + * Thread Safety: It is done inside the function. + */ +NvError NvRmTransportSetQueueDepth( + NvRmTransportHandle hPort, + NvU32 MaxQueueDepth, + NvU32 MaxMessageSize) +{ + RmReceiveMessage *pNewReceiveMsg = NULL; + + NV_ASSERT(hPort != NULL); + NV_ASSERT(MaxQueueDepth != 0); + NV_ASSERT(MaxMessageSize != 0); + + // You cannot change the queue after a connection has been opened + NV_ASSERT(hPort->State == PortState_Open); + + // !!! FIXME + // Xpc does not allow changing the base message size, so we can't change the message size here (yet!) + // Once we have per port message buffers we can set this. + NV_ASSERT(MaxMessageSize <= MAX_MESSAGE_LENGTH); + + // These are statically allocated ports, they cannot be modified! + // !!! FIXME: this is just a sanity check. Remove this and make it so that + // cpu/avp rpc doesn't call this function and just knows that the + // transport will give it a port with a large enough queue to support + // rpc, since rpc ports and queue are statically allocated this has to be true. + if (hPort == &s_RpcAvpPortStruct || + hPort == &s_RpcCpuPortStruct) + { + if (MaxMessageSize <= MAX_MESSAGE_LENGTH && + MaxQueueDepth <= MAX_MESSAGE_DEPTH) + { + return NvSuccess; + } + + NV_ASSERT(!" Illegal meesage length or queue depth. "); + } + + // Freeing default allocated message queue. + NvOsFree(hPort->RecvMessageQueue.pReceiveMsg); + hPort->RecvMessageQueue.pReceiveMsg = NULL; + // create a new message queue struct, one longer than requested on purpose. + pNewReceiveMsg = NvOsAlloc( sizeof(RmReceiveMessage) * (MaxQueueDepth+1)); + if (pNewReceiveMsg == NULL) + return NvError_InsufficientMemory; + + hPort->RecvMessageQueue.pReceiveMsg = pNewReceiveMsg; + hPort->RecvMessageQueue.QueueSize = (NvU16)(MaxQueueDepth+1); + + return NvSuccess; +} + + +static NvError +NvRmPrivTransportSendRemoteMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvError err; + NvU32 StartTime; + NvU32 CurrentTime; + NvU32 Message[3 + ((MAX_MESSAGE_LENGTH) / sizeof(NvU32))]; + + NV_ASSERT((MAX_MESSAGE_LENGTH) >= MessageSize); + + StartTime = NvOsGetTimeMS(); + + Message[0] = TransportCmd_Message; + Message[1] = hPort->RemotePort; + Message[2] = MessageSize; + + NvOsMemcpy(&Message[3], pMessageBuffer, MessageSize); + + for (;;) + { + NvOsMutexLock(s_TransportInfo.mutex); + err = NvRmPrivTransportSendMessage(hPort->hRmDevice, Message, MessageSize + 3*sizeof(NvU32)); + NvOsMutexUnlock(s_TransportInfo.mutex); + if (err == NvSuccess) + { + return NvSuccess; + } + + // Sleep and then try again in a few ms to send again + CurrentTime = NvOsGetTimeMS(); + if ( TimeoutMS != NV_WAIT_INFINITE && (CurrentTime - StartTime) > TimeoutMS ) + return NvError_Timeout; + + NvOsSleepMS(1); // try again later... + } +} + + + +static NvError +NvRmPrivTransportSendLocalMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvU32 CurrentTime; + NvU32 StartTime; + NvError err = NvSuccess; + + NvRmTransportHandle hRemotePort; + + NvOsMutexLock(s_TransportInfo.mutex); + hRemotePort = hPort->hConnectedPort; + + + StartTime = NvOsGetTimeMS(); + + for (;;) + { + // try to insert into the message into the receivers queue. + NvBool bSuccess = InsertMessage(hRemotePort, (NvU8*)pMessageBuffer, MessageSize); + if (bSuccess) + { + if (hRemotePort->hOnPushMsgSem) + NvOsSemaphoreSignal(hRemotePort->hOnPushMsgSem); + break; + } + + // The destination port is full. + if (TimeoutMS == 0) + { + err = NvError_TransportMessageBoxFull; + break; + } + + // The user wants a timeout, so we just sleep a short time so the + // other thread can pop a message. It would be better to use another semaphore + // to indicate that the box is not full, but that just seems overkill since this + // should rarely happen anyhow. + // unlock the mutex, and wait a small amount of time. + NvOsMutexUnlock(s_TransportInfo.mutex); + + NvOsSleepMS(1); + NvOsMutexLock(s_TransportInfo.mutex); + if (TimeoutMS != NV_WAIT_INFINITE) + { + // check for a timeout condition. + CurrentTime = NvOsGetTimeMS(); + if ( (CurrentTime - StartTime) >= TimeoutMS) + { + err = NvError_Timeout; + break; + } + } + } + NvOsMutexUnlock(s_TransportInfo.mutex); + + return err; +} + + +/** + * Send the message to the other end port. + * Thread Safety: It is done inside the function. + */ +NvError +NvRmTransportSendMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvError err; + + NV_ASSERT(hPort); + NV_ASSERT(hPort->State == PortState_Connected); + NV_ASSERT(pMessageBuffer); + +#if LOOPBACK_PROFILE + if (hPort->bLoopTest) + { +# if NV_IS_AVP + ((NvU32*)pMessageBuffer)[LOOP_AVP_SEND_INDEX] = *s_TransportInfo.pTimer; +# else + ((NvU32*)pMessageBuffer)[LOOP_CPU_SEND_INDEX] = *s_TransportInfo.pTimer; +# endif + } +#endif + + if (hPort->hConnectedPort) + { + err = NvRmPrivTransportSendLocalMsg(hPort, pMessageBuffer, MessageSize, TimeoutMS); + } + else if (hPort->State == PortState_Connected) + { + err = NvRmPrivTransportSendRemoteMsg(hPort, pMessageBuffer, MessageSize, TimeoutMS); + } + else + { + NV_ASSERT(0); // someone did something naughty + err = NvError_TransportNotConnected; + } + + return err; +} + + + +/** + * Receive the message from the other end port. + * Thread Safety: It is done inside the function. + */ +NvError +NvRmTransportRecvMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MaxSize, + NvU32 *pMessageSize) +{ + NvU8 TmpMessage[MAX_MESSAGE_LENGTH]; + + NV_ASSERT(hPort); + NV_ASSERT( (hPort->State == PortState_Connected) || (hPort->State == PortState_Disconnected) ); + NV_ASSERT(pMessageBuffer); + NV_ASSERT(pMessageSize); + + + NvOsMutexLock(s_TransportInfo.mutex); + if (hPort->RecvMessageQueue.ReadIndex == hPort->RecvMessageQueue.WriteIndex) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvError_TransportMessageBoxEmpty; + } + + ExtractMessage(hPort, (NvU8*)pMessageBuffer, pMessageSize, MaxSize); + if (*pMessageSize > MaxSize) + { + // not enough room to copy the message + NvOsMutexUnlock(s_TransportInfo.mutex); + NV_ASSERT(!" RM Transport: Illegal message size. "); + } + + + // if there was backpressure asserted, try to handle the currently posted message, and re-enable messages + if (s_TransportInfo.ReceiveBackPressureOn != s_TransportInfo.ReceiveBackPressureOff) + { + NV_ASSERT( ((NvU8)s_TransportInfo.ReceiveBackPressureOn) == ((NvU8)(s_TransportInfo.ReceiveBackPressureOff+1)) ); + ++s_TransportInfo.ReceiveBackPressureOff; + + if (s_TransportInfo.pReceiveMem == NULL) + { + /* QT/EMUTRANS takes this path. */ + NvRmMemRead(s_TransportInfo.hMessageMem, + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, + TmpMessage, + MAX_MESSAGE_LENGTH); + HandlePortMessage(hPort->hRmDevice, (volatile void *)TmpMessage); + NvRmMemWrite(s_TransportInfo.hMessageMem, + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, + TmpMessage, + 2*sizeof(NvU32) ); + } + else + { + HandlePortMessage(hPort->hRmDevice, (NvU32*)s_TransportInfo.pReceiveMem); + } + } + +#if LOOPBACK_PROFILE + if (hPort->bLoopTest) + { +# if NV_IS_AVP + ((NvU32*)pMessageBuffer)[LOOP_AVP_RECV_INDEX] = *s_TransportInfo.pTimer; +# else + ((NvU32*)pMessageBuffer)[LOOP_CPU_RECV_INDEX] = *s_TransportInfo.pTimer; +# endif + } +#endif + + NvOsMutexUnlock(s_TransportInfo.mutex); + + return NvSuccess; +} + +void +NvRmTransportGetPortName( + NvRmTransportHandle hPort, + NvU8 *PortName, + NvU32 PortNameSize ) +{ + NvU32 len; + + NV_ASSERT(hPort); + NV_ASSERT(PortName); + + len = NvOsStrlen(hPort->PortName); + if (len >= PortNameSize) + { + NV_ASSERT(!" RM Transport: Port Name too long. "); + } + + NvOsStrncpy((char *)PortName, hPort->PortName, PortNameSize); +} + |