summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/nvrm/core
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/nvrm/core')
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/Makefile2
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c7
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c432
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c165
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h92
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/Makefile1
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h270
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c39
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h208
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c1655
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);
+}
+