diff options
-rw-r--r-- | arch/arm/mach-tegra/include/mach/iomap.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/Makefile | 1 | ||||
-rwxr-xr-x | arch/arm/mach-tegra/nv/include/linux/nvrpc_ioctl.h | 102 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_xpc.c | 431 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_xpc_hw_private.c | 165 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/common/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_module_stub.c | 177 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c | 1676 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/dispatch/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/dispatch/NvRm_Dispatch.c | 12 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_module_dispatch.c | 469 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nv/nvrpc_user.c | 629 |
14 files changed, 3198 insertions, 483 deletions
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h index c20a5273a684..78bf8009b0ed 100644 --- a/arch/arm/mach-tegra/include/mach/iomap.h +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -59,9 +59,18 @@ #define TEGRA_GART_BASE 0x58000000 #define TEGRA_GART_SIZE SZ_32M +#define TEGRA_RES_SEMA_BASE 0x60001000 +#define TEGRA_RES_SEMA_SIZE SZ_4K + +#define TEGRA_ARB_SEMA_BASE 0x60002000 +#define TEGRA_ARB_SEMA_SIZE SZ_4K + #define TEGRA_PRIMARY_ICTLR_BASE 0x60004000 #define TEGRA_PRIMARY_ICTLR_SIZE SZ_64 +#define TEGRA_ARBGNT_ICTLR_BASE 0x60004040 +#define TEGRA_ARBGNT_ICTLR_SIZE 192 + #define TEGRA_SECONDARY_ICTLR_BASE 0x60004100 #define TEGRA_SECONDARY_ICTLR_SIZE SZ_64 diff --git a/arch/arm/mach-tegra/nv/Makefile b/arch/arm/mach-tegra/nv/Makefile index b7a0d7019f5d..7fb51c48192f 100644 --- a/arch/arm/mach-tegra/nv/Makefile +++ b/arch/arm/mach-tegra/nv/Makefile @@ -11,6 +11,7 @@ ccflags-y += -Iarch/arm/mach-tegra/nv/include obj-$(CONFIG_TEGRA_NVRM) += nvrm_user.o +obj-$(CONFIG_TEGRA_NVRM) += nvrpc_user.o obj-$(CONFIG_TEGRA_NVRM) += nvrm/ obj-$(CONFIG_TEGRA_NVRM) += nvreftrack/ diff --git a/arch/arm/mach-tegra/nv/include/linux/nvrpc_ioctl.h b/arch/arm/mach-tegra/nv/include/linux/nvrpc_ioctl.h new file mode 100755 index 000000000000..594e1f5d4e94 --- /dev/null +++ b/arch/arm/mach-tegra/nv/include/linux/nvrpc_ioctl.h @@ -0,0 +1,102 @@ +/* + * arch/arm/mach-tegra/include/linux/nvrpc_ioctl.h + * + * structure declarations for nvrpc user-space ioctls + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/ioctl.h> +#include <linux/types.h> + +#if !defined(__KERNEL__) +#define __user +#endif + +#ifndef _MACH_TEGRA_NVRPC_IOCTL_H_ +#define _MACH_TEGRA_NVRPC_IOCTL_H_ + +struct nvrpc_handle_param { + __u32 handle; + __u32 param; + __u32 ret_val; /* operation status */ +}; + +struct nvrpc_open_params { + __u32 rm_handle; /* rm device handle */ + __u32 port_name_size; /* port name buffer size */ + __u32 sem; /* receive semaphore handle */ + __u32 transport_handle; /* transport handle */ + __u32 ret_val; /* operation status */ + unsigned long port_name; /* port name */ +}; + +struct nvrpc_set_queue_depth_params { + __u32 transport_handle; /* transport handle */ + __u32 max_queue_depth; /* maximum number of message in Queue */ + __u32 max_message_size; /* maximum size of the message in bytes */ + __u32 ret_val; /* operation status */ +}; + +struct nvrpc_msg_params { + __u32 transport_handle; /* transport handle */ + __u32 max_message_size; /* maximum size of the message in bytes */ + __u32 params; /* timeout in ms */ + __u32 ret_val; /* operation status */ + unsigned long msg_buffer; +}; + +#define NVRPC_IOC_MAGIC 'N' + +#define NVRPC_IOCTL_INIT \ + _IOWR(NVRPC_IOC_MAGIC, 0x30, struct nvrpc_handle_param) +#define NVRPC_IOCTL_OPEN \ + _IOWR(NVRPC_IOC_MAGIC, 0x31, struct nvrpc_open_params) +#define NVRPC_IOCTL_GET_PORTNAME \ + _IOWR(NVRPC_IOC_MAGIC, 0x32, struct nvrpc_open_params) +#define NVRPC_IOCTL_CLOSE \ + _IOWR(NVRPC_IOC_MAGIC, 0x33, struct nvrpc_handle_param) +#define NVRPC_IOCTL_DEINIT \ + _IOWR(NVRPC_IOC_MAGIC, 0x34, struct nvrpc_handle_param) +#define NVRPC_IOCTL_WAIT_FOR_CONNECT \ + _IOWR(NVRPC_IOC_MAGIC, 0x35, struct nvrpc_handle_param) +#define NVRPC_IOCTL_CONNECT \ + _IOWR(NVRPC_IOC_MAGIC, 0x36, struct nvrpc_handle_param) +#define NVRPC_IOCTL_SET_QUEUE_DEPTH \ + _IOWR(NVRPC_IOC_MAGIC, 0x37, struct nvrpc_set_queue_depth_params) +#define NVRPC_IOCTL_SEND_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x38, struct nvrpc_msg_params) +#define NVRPC_IOCTL_SEND_MSG_LP0 \ + _IOWR(NVRPC_IOC_MAGIC, 0x39, struct nvrpc_msg_params) +#define NVRPC_IOCTL_RECV_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x3A, struct nvrpc_msg_params) +#define NVRPC_IOCTL_XPC_INIT \ + _IOWR(NVRPC_IOC_MAGIC, 0x3B, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_ACQUIRE \ + _IOWR(NVRPC_IOC_MAGIC, 0x3C, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_RELEASE \ + _IOWR(NVRPC_IOC_MAGIC, 0x3D, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_GET_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x3E, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_SEND_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x3F, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_DESTROY \ + _IOWR(NVRPC_IOC_MAGIC, 0x40, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_CREATE \ + _IOWR(NVRPC_IOC_MAGIC, 0x41, struct nvrpc_handle_param) + +#endif diff --git a/arch/arm/mach-tegra/nv/nvrm/core/Makefile b/arch/arm/mach-tegra/nv/nvrm/core/Makefile index 728fa0b544af..716418630a1f 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/Makefile +++ b/arch/arm/mach-tegra/nv/nvrm/core/Makefile @@ -8,5 +8,5 @@ ccflags-y += -DNV_DEBUG=0 endif ccflags-y += -Iarch/arm/mach-tegra/nv/include -#obj-y += ap15/ +obj-y += ap15/ obj-y += common/ diff --git a/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile b/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile index 293e65fba4de..5d94bc4ca846 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile +++ b/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile @@ -11,3 +11,5 @@ ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core/common ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core #obj-y += ap15rm_init.o +obj-y += ap15rm_xpc.o +obj-y += ap15rm_xpc_hw_private.o diff --git a/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_xpc.c b/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_xpc.c new file mode 100644 index 000000000000..b0205c2110c9 --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_xpc.c @@ -0,0 +1,431 @@ +/* + * 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 <linux/kernel.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <mach/iomap.h> +#include <mach/irqs.h> +#include <mach/io.h> + +#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 int s_arbInterruptHandle = -1; + +// 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) +{ + NvRmPhysAddr ResourceSemaPhysAddr; + + // Get base address of the hw mail box register. This register is in the set + // of resource semaphore module Id. + ResourceSemaPhysAddr = TEGRA_RES_SEMA_BASE; + hXpcMessage->HwMailBoxReg.BankSize = TEGRA_RES_SEMA_SIZE; + + // Map the base address to the virtual address. + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = + IO_ADDRESS(ResourceSemaPhysAddr); + + 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) +{ + 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 irqreturn_t arbgnt_isr(int irq, void *data) +{ + ArbSemaIsr(data); + return IRQ_HANDLED; +} + +static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice) +{ + NvOsInterruptHandler ArbSemaHandler; + NvRmPhysAddr ArbSemaBase, ArbGntBase; + NvU32 ArbSemaSize, ArbGntSize; + NvU32 irq; + NvError e; + NvU32 i = 0; + int ret; + + /* FIXME: is this the right interrupt? */ + irq = INT_GNT_0; + + ArbSemaHandler = ArbSemaIsr; + set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(irq, arbgnt_isr, 0, "nvrm_arbgnt", hDevice); + if (ret < 0) { + printk("%s request_irq failed %d\n", __func__, ret); + return NvError_AccessDenied; + } + s_arbInterruptHandle = irq; + + ArbSemaBase = TEGRA_ARB_SEMA_BASE; + ArbSemaSize = TEGRA_ARB_SEMA_SIZE; + ArbGntBase = TEGRA_ARBGNT_ICTLR_BASE; + ArbGntSize = TEGRA_ARBGNT_ICTLR_SIZE; + + s_ArbSema.pArbSemaVirtAddr = IO_ADDRESS(ArbSemaBase); + s_ArbSema.pArbGntVirtAddr = IO_ADDRESS(ArbGntBase); + + //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) + ); + + enable_irq(irq); + +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); +} + +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/nv/nvrm/core/ap15/ap15rm_xpc_hw_private.c b/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_xpc_hw_private.c new file mode 100644 index 000000000000..ffd1dc5d6ebd --- /dev/null +++ b/arch/arm/mach-tegra/nv/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/nv/nvrm/core/common/Makefile b/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile index 44d3ba199e30..b6c0893b7cf5 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile @@ -11,3 +11,5 @@ ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core/common ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core obj-y += nvrm_rmctrace.o +obj-y += nvrm_transport.o +obj-y += nvrm_module_stub.o
\ No newline at end of file diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_module_stub.c b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_module_stub.c new file mode 100644 index 000000000000..eb7a63604f61 --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_module_stub.c @@ -0,0 +1,177 @@ + +#define NV_IDL_IS_STUB + +/* + * Copyright (c) 2009 NVIDIA Corporation. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <mach/iomap.h> +#include "nvcommon.h" +#include "nvrm_module.h" + +NvError NvRmModuleGetCapabilities( NvRmDeviceHandle hDeviceHandle, + NvRmModuleID Module, NvRmModuleCapability * pCaps, NvU32 NumCaps, + void * * Capability ) +{ + NvU32 major = 0, minor = 0; + unsigned i; + + switch (NVRM_MODULE_ID_MODULE(Module)) { + case NvRmModuleID_Mpe: + major = 1; + minor = 2; + break; + + case NvRmModuleID_BseA: + major = 1; + minor = 1; + break; + + case NvRmModuleID_Display: + major = 1; + minor = 3; + break; + + case NvRmModuleID_Spdif: + major = 1; + minor = 0; + break; + + case NvRmModuleID_I2s: + major = 1; + minor = 1; + break; + + case NvRmModuleID_Misc: + major = 2; + minor = 0; + break; + + case NvRmModuleID_Vde: + major = 1; + minor = 2; + break; + + case NvRmModuleID_Isp: + major = 1; + minor = 0; + break; + + case NvRmModuleID_Vi: + major = 1; + minor = 1; + break; + + case NvRmModuleID_3D: + major = 1; + minor = 2; + break; + + case NvRmModuleID_2D: + major = 1; + minor = 1; + break; + + default: + printk("%s module %d not implemented\n", __func__, Module); + } + + for (i=0; i<NumCaps; i++) { + if (pCaps[i].MajorVersion==major && + pCaps[i].MinorVersion==minor) { + *Capability = pCaps[i].Capability; + return NvSuccess; + } + } + + return NvError_NotSupported; +} + +NvU32 NvRmModuleGetNumInstances( NvRmDeviceHandle hRmDeviceHandle, + NvRmModuleID Module ) +{ + switch (Module) { + case NvRmModuleID_I2s: + return 4; + + case NvRmModuleID_Display: + return 2; + + case NvRmModuleID_3D: + case NvRmModuleID_Avp: + case NvRmModuleID_GraphicsHost: + case NvRmModuleID_Vcp: + case NvRmModuleID_Isp: + case NvRmModuleID_Vi: + case NvRmModuleID_Epp: + case NvRmModuleID_2D: + case NvRmModuleID_Spdif: + case NvRmModuleID_Vde: + case NvRmModuleID_Mpe: + case NvRmModuleID_Hdcp: + case NvRmModuleID_Hdmi: + case NvRmModuleID_Tvo: + case NvRmModuleID_Dsi: + case NvRmModuleID_BseA: + return 1; + + default: + printk("%s module %d not implemented\n", __func__, Module); + return 1; + } +} + +void NvRmModuleGetBaseAddress( NvRmDeviceHandle hRmDeviceHandle, NvRmModuleID Module, NvRmPhysAddr * pBaseAddress, NvU32 * pSize ) +{ + switch (NVRM_MODULE_ID_MODULE(Module)) { + case NvRmModuleID_GraphicsHost: + *pBaseAddress = 0x50000000; + *pSize = 144 * 1024; + break; + case NvRmModuleID_Display: + *pBaseAddress = 0x54200000 + (NVRM_MODULE_ID_INSTANCE(Module))*0x40000; + *pSize = 256 * 1024; + break; + + case NvRmModuleID_Vi: + *pBaseAddress = 0x54080000; + *pSize = 256 * 1024; + break; + + case NvRmModuleID_Dsi: + *pBaseAddress = 0x54300000; + *pSize = 256 * 1024; + break; + + default: + *pBaseAddress = 0x0000000; + *pSize = 00; + printk("%s module %d not implemented\n", __func__, Module); + } + printk("%s module %d 0x%08x x %dK\n", __func__, Module, *pBaseAddress, *pSize / 1024); +} + +void NvRmModuleReset(NvRmDeviceHandle hRmDevice, NvRmModuleID Module) +{ + void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE); + if (NVRM_MODULE_ID_MODULE(Module) != NvRmModuleID_Avp || + NVRM_MODULE_ID_INSTANCE(Module) != 0) { + printk("%s MOD[%lu] INST[%lu] not implemented\n", __func__, + NVRM_MODULE_ID_MODULE(Module), + NVRM_MODULE_ID_INSTANCE(Module)); + return; + } + + writel(1<<1, clk_rst + 0x300); + udelay(10); + writel(1<<1, clk_rst + 0x304); +} diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c new file mode 100644 index 000000000000..6522dffe55d1 --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c @@ -0,0 +1,1676 @@ +/* + * 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 <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <mach/irqs.h> +#include "nvrm_transport.h" +#include "nvrm_xpc.h" +#include "nvrm_interrupt.h" +#include "nvrm_message.h" +#include "nvutil.h" +#include "nvassert.h" +#include "nvcommon.h" +#include "avp.h" +#include <linux/jiffies.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 int s_TransportInterruptHandle = -1; + +static NvRmTransportHandle +FindPort(NvRmDeviceHandle hDevice, char *pPortName); + +static NvError NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, + NvU32 *messagehdr, NvU32 MessageHdrLength, + 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); + return; + } + // if we're on the AVP, the first message we get will configure the message info + if (s_TransportInfo.MessageMemPhysAddr == 0) + { + MessageData = MessageData; + 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); + } +} + +static irqreturn_t transport_ist(int irq, void *data) +{ + InboxFullIsr(data); + enable_irq(irq); + return IRQ_HANDLED; +} + +static irqreturn_t transport_isr(int irq, void *data) +{ + disable_irq_nosync(irq); + return IRQ_WAKE_THREAD; +} + + +/** + * Handle the outbox empty interrupt. + */ + +static void +NvRmPrivProcIdGetProcessorInfo( + NvRmDeviceHandle hDevice, + NvRmModuleID *pProcModuleId) +{ + *pProcModuleId = NvRmModuleID_Cpu; +} + +/** + * Register for the transport interrupts. + */ +static NvError +RegisterTransportInterrupt(NvRmDeviceHandle hDevice) +{ + NvU32 IrqList; + int ret; + + if (s_TransportInterruptHandle >= 0) + { + return NvSuccess; + } + + IrqList = INT_SHR_SEM_INBOX_IBF; + + set_irq_flags(IrqList, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_threaded_irq(IrqList, transport_isr, transport_ist, 0, + "nvrm_transport", hDevice); + if (ret) { + printk("%s failed %d\n", __func__, ret); + return NvError_BadParameter; + } + s_TransportInterruptHandle = IrqList; + enable_irq(IrqList); + return NvSuccess; +} + +// 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); + free_irq(s_TransportInterruptHandle, hRmDevice); + set_irq_flags(s_TransportInterruptHandle, IRQF_VALID); + s_TransportInterruptHandle = -1; +#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), NULL, 0); + } + + 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 *MessageHdr, NvU32 MessageHdrLength, + 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 = ((volatile 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, MessageHdr, MessageHdrLength); + if (Message && MessageLength) + { + NvRmMemWrite(s_TransportInfo.hMessageMem, MessageHdrLength, + Message, MessageLength); + } + } + else + { + NvOsMemcpy(s_TransportInfo.pTransmitMem, MessageHdr, MessageHdrLength); + if (Message && MessageLength) + { + NvOsMemcpy(s_TransportInfo.pTransmitMem + MessageHdrLength, + Message, MessageLength); + } + NvOsFlushWriteCombineBuffer(); + } + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return NvSuccess; +} + +NvError NvRmTransportSendMsgInLP0(NvRmTransportHandle hPort, + void *pMessageBuffer, NvU32 MessageSize) +{ + NvU32 ReadData; + NvU32 MessageHdr[3]; + + NV_ASSERT(pMessageBuffer); + + MessageHdr[0] = TransportCmd_Message; + MessageHdr[1] = hPort->RemotePort; + MessageHdr[2] = MessageSize; + + ReadData = ((volatile 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, MessageHdr, sizeof(MessageHdr)); + if (MessageSize) { + NvOsMemcpy(s_TransportInfo.pTransmitMem + sizeof(MessageHdr), + pMessageBuffer, MessageSize); + } + 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), NULL, 0); + 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 MessageHdr[3]; + NvU32 JiffyTime = jiffies_to_msecs(1); + + NV_ASSERT((MAX_MESSAGE_LENGTH) >= MessageSize); + + StartTime = NvOsGetTimeMS(); + + MessageHdr[0] = TransportCmd_Message; + MessageHdr[1] = hPort->RemotePort; + MessageHdr[2] = MessageSize; + + for (;;) + { + NvOsMutexLock(s_TransportInfo.mutex); + err = NvRmPrivTransportSendMessage(hPort->hRmDevice, + MessageHdr, sizeof(MessageHdr), + pMessageBuffer, MessageSize); + 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; + /* Sleeping for 1msec may not sleep exactly for 1msec. It depends + * on OS jiffy(tick) time. If jiffy time is much bigger,then this 1msec + * sleep would cause performance issues. At the same time, if complete + * polling is used, it can potentially block other threads from running. + * To reduce the impact of sleep in either ways, poll for one jiffy time + * and if operation is not complete then start sleeping. + */ + if ( (CurrentTime - StartTime) > JiffyTime ) + NvOsSleepMS(1); // try again later... + } +} + + + +static NvError +NvRmPrivTransportSendLocalMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvU32 CurrentTime; + NvU32 StartTime; + NvError err = NvSuccess; + NvU32 JiffyTime = jiffies_to_msecs(1); + + NvRmTransportHandle hRemotePort; + + NvOsMutexLock(s_TransportInfo.mutex); + hRemotePort = hPort->hConnectedPort; + + + StartTime = NvOsGetTimeMS(); + CurrentTime = StartTime; + + 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); + + /* Sleeping for 1msec may not sleep exactly for 1msec. It depends + * on OS jiffy(tick) time. If jiffy time is much bigger,then this 1msec + * sleep would cause performance issues. At the same time, if complete + * polling is used, it can potentially block other threads from running. + * To reduce the impact of sleep in either ways, poll for one jiffy time + * and if operation is not complete then start sleeping. + */ + if ( (CurrentTime - StartTime) > JiffyTime ) + 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); + + + *pMessageSize = 0; + 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. "); + return NvError_InvalidSize; + } + + + // 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); +} diff --git a/arch/arm/mach-tegra/nv/nvrm/dispatch/Makefile b/arch/arm/mach-tegra/nv/nvrm/dispatch/Makefile index 36cab878cc87..301c419413af 100644 --- a/arch/arm/mach-tegra/nv/nvrm/dispatch/Makefile +++ b/arch/arm/mach-tegra/nv/nvrm/dispatch/Makefile @@ -19,7 +19,7 @@ obj-y += NvRm_Dispatch.o #obj-y += nvrm_init_dispatch.o #obj-y += nvrm_interrupt_dispatch.o #obj-y += nvrm_memmgr_dispatch.o -#obj-y += nvrm_module_dispatch.o +obj-y += nvrm_module_dispatch.o #obj-y += nvrm_pinmux_dispatch.o #obj-y += nvrm_power_dispatch.o #obj-y += nvrm_spi_dispatch.o @@ -27,5 +27,5 @@ obj-y += NvRm_Dispatch.o #obj-y += nvrm_keylist_dispatch.o #obj-y += nvrm_pcie_dispatch.o #obj-y += nvrm_memctrl_dispatch.o -#obj-y += nvrm_transport_dispatch.o +obj-y += nvrm_transport_dispatch.o #obj-y += nvrm_xpc_dispatch.o diff --git a/arch/arm/mach-tegra/nv/nvrm/dispatch/NvRm_Dispatch.c b/arch/arm/mach-tegra/nv/nvrm/dispatch/NvRm_Dispatch.c index 64505d65bb48..ba3a633f6712 100644 --- a/arch/arm/mach-tegra/nv/nvrm/dispatch/NvRm_Dispatch.c +++ b/arch/arm/mach-tegra/nv/nvrm/dispatch/NvRm_Dispatch.c @@ -84,12 +84,6 @@ NvError nvrm_xpc_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *O return NvSuccess; } -NvError nvrm_transport_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - printk("NVRM: %s %d\n", __func__, function); - return NvSuccess; -} - NvError nvrm_memctrl_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) { printk("NVRM: %s %d\n", __func__, function); @@ -180,12 +174,6 @@ NvError nvrm_gpio_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void * return NvSuccess; } -NvError nvrm_module_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - printk("NVRM: %s %d\n", __func__, function); - return NvSuccess; -} - NvError nvrm_memmgr_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) { printk("NVRM: %s %d\n", __func__, function); diff --git a/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_module_dispatch.c b/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_module_dispatch.c index e4038b220cbf..70a8eec1e2b8 100644 --- a/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_module_dispatch.c +++ b/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_module_dispatch.c @@ -468,355 +468,6 @@ typedef struct NvRmModuleGetModuleInfo_params_t NvRmModuleGetModuleInfo_out out; } NvRmModuleGetModuleInfo_params; -static NvError NvRegw08_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegw08_in *p_in; - - p_in = (NvRegw08_in *)InBuffer; - - - NvRegw08( p_in->rm, p_in->aperture, p_in->offset, p_in->data ); - - return err_; -} - -static NvError NvRegr08_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegr08_in *p_in; - NvRegr08_out *p_out; - - p_in = (NvRegr08_in *)InBuffer; - p_out = (NvRegr08_out *)((NvU8 *)OutBuffer + OFFSET(NvRegr08_params, out) - OFFSET(NvRegr08_params, inout)); - - - p_out->ret_ = NvRegr08( p_in->hDeviceHandle, p_in->aperture, p_in->offset ); - - return err_; -} - -static NvError NvRegrb_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegrb_in *p_in; - NvU32 *values = NULL; - - p_in = (NvRegrb_in *)InBuffer; - - if( p_in->num && p_in->values ) - { - values = (NvU32 *)NvOsAlloc( p_in->num * sizeof( NvU32 ) ); - if( !values ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - } - - NvRegrb( p_in->hRmDeviceHandle, p_in->aperture, p_in->num, p_in->offset, values ); - - if(p_in->values && values) - { - err_ = NvOsCopyOut( p_in->values, values, p_in->num * sizeof( NvU32 ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - } - } -clean: - NvOsFree( values ); - return err_; -} - -static NvError NvRegwb_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegwb_in *p_in; - NvU32 *values = NULL; - - p_in = (NvRegwb_in *)InBuffer; - - if( p_in->num && p_in->values ) - { - values = (NvU32 *)NvOsAlloc( p_in->num * sizeof( NvU32 ) ); - if( !values ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - if( p_in->values ) - { - err_ = NvOsCopyIn( values, p_in->values, p_in->num * sizeof( NvU32 ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - goto clean; - } - } - } - - NvRegwb( p_in->hRmDeviceHandle, p_in->aperture, p_in->num, p_in->offset, values ); - -clean: - NvOsFree( values ); - return err_; -} - -static NvError NvRegwm_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegwm_in *p_in; - NvU32 *offsets = NULL; - NvU32 *values = NULL; - - p_in = (NvRegwm_in *)InBuffer; - - if( p_in->num && p_in->offsets ) - { - offsets = (NvU32 *)NvOsAlloc( p_in->num * sizeof( NvU32 ) ); - if( !offsets ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - if( p_in->offsets ) - { - err_ = NvOsCopyIn( offsets, p_in->offsets, p_in->num * sizeof( NvU32 ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - goto clean; - } - } - } - if( p_in->num && p_in->values ) - { - values = (NvU32 *)NvOsAlloc( p_in->num * sizeof( NvU32 ) ); - if( !values ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - if( p_in->values ) - { - err_ = NvOsCopyIn( values, p_in->values, p_in->num * sizeof( NvU32 ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - goto clean; - } - } - } - - NvRegwm( p_in->hRmDeviceHandle, p_in->aperture, p_in->num, offsets, values ); - -clean: - NvOsFree( offsets ); - NvOsFree( values ); - return err_; -} - -static NvError NvRegrm_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegrm_in *p_in; - NvU32 *offsets = NULL; - NvU32 *values = NULL; - - p_in = (NvRegrm_in *)InBuffer; - - if( p_in->num && p_in->offsets ) - { - offsets = (NvU32 *)NvOsAlloc( p_in->num * sizeof( NvU32 ) ); - if( !offsets ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - if( p_in->offsets ) - { - err_ = NvOsCopyIn( offsets, p_in->offsets, p_in->num * sizeof( NvU32 ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - goto clean; - } - } - } - if( p_in->num && p_in->values ) - { - values = (NvU32 *)NvOsAlloc( p_in->num * sizeof( NvU32 ) ); - if( !values ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - } - - NvRegrm( p_in->hRmDeviceHandle, p_in->aperture, p_in->num, offsets, values ); - - if(p_in->values && values) - { - err_ = NvOsCopyOut( p_in->values, values, p_in->num * sizeof( NvU32 ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - } - } -clean: - NvOsFree( offsets ); - NvOsFree( values ); - return err_; -} - -static NvError NvRegw_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegw_in *p_in; - - p_in = (NvRegw_in *)InBuffer; - - - NvRegw( p_in->hDeviceHandle, p_in->aperture, p_in->offset, p_in->data ); - - return err_; -} - -static NvError NvRegr_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRegr_in *p_in; - NvRegr_out *p_out; - - p_in = (NvRegr_in *)InBuffer; - p_out = (NvRegr_out *)((NvU8 *)OutBuffer + OFFSET(NvRegr_params, out) - OFFSET(NvRegr_params, inout)); - - - p_out->ret_ = NvRegr( p_in->hDeviceHandle, p_in->aperture, p_in->offset ); - - return err_; -} - -static NvError NvRmGetRandomBytes_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmGetRandomBytes_in *p_in; - NvRmGetRandomBytes_out *p_out; - void* pBytes = NULL; - - p_in = (NvRmGetRandomBytes_in *)InBuffer; - p_out = (NvRmGetRandomBytes_out *)((NvU8 *)OutBuffer + OFFSET(NvRmGetRandomBytes_params, out) - OFFSET(NvRmGetRandomBytes_params, inout)); - - if( p_in->NumBytes && p_in->pBytes ) - { - pBytes = (void* )NvOsAlloc( p_in->NumBytes ); - if( !pBytes ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - } - - p_out->ret_ = NvRmGetRandomBytes( p_in->hRmDeviceHandle, p_in->NumBytes, pBytes ); - - if(p_in->pBytes && pBytes) - { - err_ = NvOsCopyOut( p_in->pBytes, pBytes, p_in->NumBytes ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - } - } -clean: - NvOsFree( pBytes ); - return err_; -} - -static NvError NvRmQueryChipUniqueId_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmQueryChipUniqueId_in *p_in; - NvRmQueryChipUniqueId_out *p_out; - void* pId = NULL; - - p_in = (NvRmQueryChipUniqueId_in *)InBuffer; - p_out = (NvRmQueryChipUniqueId_out *)((NvU8 *)OutBuffer + OFFSET(NvRmQueryChipUniqueId_params, out) - OFFSET(NvRmQueryChipUniqueId_params, inout)); - - if( p_in->IdSize && p_in->pId ) - { - pId = (void* )NvOsAlloc( p_in->IdSize ); - if( !pId ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - } - - p_out->ret_ = NvRmQueryChipUniqueId( p_in->hDevHandle, p_in->IdSize, pId ); - - if(p_in->pId && pId) - { - err_ = NvOsCopyOut( p_in->pId, pId, p_in->IdSize ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - } - } -clean: - NvOsFree( pId ); - return err_; -} - -static NvError NvRmModuleGetCapabilities_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmModuleGetCapabilities_in *p_in; - NvRmModuleGetCapabilities_out *p_out; - NvRmModuleCapability *pCaps = NULL; - - p_in = (NvRmModuleGetCapabilities_in *)InBuffer; - p_out = (NvRmModuleGetCapabilities_out *)((NvU8 *)OutBuffer + OFFSET(NvRmModuleGetCapabilities_params, out) - OFFSET(NvRmModuleGetCapabilities_params, inout)); - - if( p_in->NumCaps && p_in->pCaps ) - { - pCaps = (NvRmModuleCapability *)NvOsAlloc( p_in->NumCaps * sizeof( NvRmModuleCapability ) ); - if( !pCaps ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - if( p_in->pCaps ) - { - err_ = NvOsCopyIn( pCaps, p_in->pCaps, p_in->NumCaps * sizeof( NvRmModuleCapability ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - goto clean; - } - } - } - - p_out->ret_ = NvRmModuleGetCapabilities( p_in->hDeviceHandle, p_in->Module, pCaps, p_in->NumCaps, &p_out->Capability ); - -clean: - NvOsFree( pCaps ); - return err_; -} - -static NvError NvRmModuleResetWithHold_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmModuleResetWithHold_in *p_in; - - p_in = (NvRmModuleResetWithHold_in *)InBuffer; - - - NvRmModuleResetWithHold( p_in->hRmDeviceHandle, p_in->Module, p_in->bHold ); - - return err_; -} - static NvError NvRmModuleReset_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) { NvError err_ = NvSuccess; @@ -830,135 +481,17 @@ static NvError NvRmModuleReset_dispatch_( void *InBuffer, NvU32 InSize, void *Ou return err_; } -static NvError NvRmModuleGetNumInstances_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmModuleGetNumInstances_in *p_in; - NvRmModuleGetNumInstances_out *p_out; - - p_in = (NvRmModuleGetNumInstances_in *)InBuffer; - p_out = (NvRmModuleGetNumInstances_out *)((NvU8 *)OutBuffer + OFFSET(NvRmModuleGetNumInstances_params, out) - OFFSET(NvRmModuleGetNumInstances_params, inout)); - - - p_out->ret_ = NvRmModuleGetNumInstances( p_in->hRmDeviceHandle, p_in->Module ); - - return err_; -} - -static NvError NvRmModuleGetBaseAddress_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmModuleGetBaseAddress_in *p_in; - NvRmModuleGetBaseAddress_out *p_out; - - p_in = (NvRmModuleGetBaseAddress_in *)InBuffer; - p_out = (NvRmModuleGetBaseAddress_out *)((NvU8 *)OutBuffer + OFFSET(NvRmModuleGetBaseAddress_params, out) - OFFSET(NvRmModuleGetBaseAddress_params, inout)); - - - NvRmModuleGetBaseAddress( p_in->hRmDeviceHandle, p_in->Module, &p_out->pBaseAddress, &p_out->pSize ); - - return err_; -} - -static NvError NvRmModuleGetModuleInfo_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) -{ - NvError err_ = NvSuccess; - NvRmModuleGetModuleInfo_in *p_in; - NvRmModuleGetModuleInfo_inout *p_inout; - NvRmModuleGetModuleInfo_out *p_out; - NvRmModuleGetModuleInfo_inout inout; - NvRmModuleInfo *pModuleInfo = NULL; - - p_in = (NvRmModuleGetModuleInfo_in *)InBuffer; - p_inout = (NvRmModuleGetModuleInfo_inout *)((NvU8 *)InBuffer + OFFSET(NvRmModuleGetModuleInfo_params, inout)); - p_out = (NvRmModuleGetModuleInfo_out *)((NvU8 *)OutBuffer + OFFSET(NvRmModuleGetModuleInfo_params, out) - OFFSET(NvRmModuleGetModuleInfo_params, inout)); - - (void)inout; - inout.pNum = p_inout->pNum; - if( p_inout->pNum && p_in->pModuleInfo ) - { - pModuleInfo = (NvRmModuleInfo *)NvOsAlloc( p_inout->pNum * sizeof( NvRmModuleInfo ) ); - if( !pModuleInfo ) - { - err_ = NvError_InsufficientMemory; - goto clean; - } - } - - p_out->ret_ = NvRmModuleGetModuleInfo( p_in->hDevice, p_in->module, &inout.pNum, pModuleInfo ); - - - p_inout = (NvRmModuleGetModuleInfo_inout *)OutBuffer; - p_inout->pNum = inout.pNum; - if(p_in->pModuleInfo && pModuleInfo) - { - err_ = NvOsCopyOut( p_in->pModuleInfo, pModuleInfo, p_inout->pNum * sizeof( NvRmModuleInfo ) ); - if( err_ != NvSuccess ) - { - err_ = NvError_BadParameter; - } - } -clean: - NvOsFree( pModuleInfo ); - return err_; -} - NvError nvrm_module_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); NvError nvrm_module_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) { NvError err_ = NvSuccess; switch( function ) { - case 15: - err_ = NvRegw08_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 14: - err_ = NvRegr08_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 13: - err_ = NvRegrb_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 12: - err_ = NvRegwb_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 11: - err_ = NvRegwm_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 10: - err_ = NvRegrm_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 9: - err_ = NvRegw_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 8: - err_ = NvRegr_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 7: - err_ = NvRmGetRandomBytes_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 6: - err_ = NvRmQueryChipUniqueId_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 5: - err_ = NvRmModuleGetCapabilities_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 4: - err_ = NvRmModuleResetWithHold_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; case 3: err_ = NvRmModuleReset_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); break; - case 2: - err_ = NvRmModuleGetNumInstances_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 1: - err_ = NvRmModuleGetBaseAddress_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; - case 0: - err_ = NvRmModuleGetModuleInfo_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; default: - err_ = NvError_BadParameter; + err_ = NvSuccess; break; } diff --git a/arch/arm/mach-tegra/nv/nvrpc_user.c b/arch/arm/mach-tegra/nv/nvrpc_user.c new file mode 100644 index 000000000000..874ebdc57d9a --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrpc_user.c @@ -0,0 +1,629 @@ +/* + * arch/arm/mach-tegra/nvrpc_user.c + * + * User-land access to NvRm transport APIs + * + * Copyright (c) 2008-2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define NV_DEBUG 0 + +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <mach/nvrm_linux.h> +#include "linux/nvrpc_ioctl.h" +#include "nvcommon.h" +#include "nvassert.h" +#include "nvos.h" +#include "nvrm_transport.h" +#include "nvrm_xpc.h" + +#define DEVICE_NAME "nvrpc" +#define NVRPC_MAX_LOCAL_STACK 256 +#define nvrpc_stack_kzalloc(stackbuf, size, gfp) \ + ((size) > sizeof((stackbuf)) ? kzalloc((size),(gfp)) : (stackbuf)) +#define nvrpc_stack_kfree(stackbuf, buf) \ + do { \ + if ((buf) && (buf)!=(void *)(stackbuf)) \ + kfree(buf); \ + } while (0); + +static int nvrpc_open(struct inode *inode, struct file *file); +static int nvrpc_close(struct inode *inode, struct file *file); +static long nvrpc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); + +//Ioctl functions +static int nvrpc_ioctl_open(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_get_port_name(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_close(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_wait_for_connect(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_connect(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_set_queue_depth(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_send_msg(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_send_msg_lp0(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_recv_msg(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_init(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_acquire(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_release(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_get_msg(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_send_msg(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_destroy(struct file *filp, + unsigned int cmd, void __user *arg); +static int nvrpc_ioctl_xpc_create(struct file *filp, + unsigned int cmd, void __user *arg); +// local function +static int nvrpc_make_error_code(NvError e); + +static const struct file_operations nvrpc_fops = +{ + .owner = THIS_MODULE, + .open = nvrpc_open, + .release = nvrpc_close, + .unlocked_ioctl = nvrpc_unlocked_ioctl, +}; + +static struct miscdevice nvrpc_dev = +{ + .name = DEVICE_NAME, + .fops = &nvrpc_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +static DEFINE_MUTEX(nvrpc_device_lock); + +int nvrpc_open(struct inode *inode, struct file *file) +{ + NvError e = NvSuccess; + static NvBool init_done = NV_FALSE; + + mutex_lock(&nvrpc_device_lock); + if (init_done == NV_FALSE) { + e = NvRmTransportInit(NULL); + init_done = NV_TRUE; + } + mutex_unlock(&nvrpc_device_lock); + + if (e == NvSuccess) + return 0; + else + return -ENODEV; +} + +int nvrpc_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static int nvrpc_make_error_code(NvError e) +{ + int error = 0; + if (error != NvSuccess) { + if (e == NvError_InvalidAddress) + error = -EFAULT; + else if (e == NvError_BadParameter) + error = -EINVAL; + else + error = -EIO; + } + return error; +} + +static int nvrpc_ioctl_open(struct file *filp, + unsigned int cmd, void __user *arg) +{ + NvError e = NvSuccess; + int error; + struct nvrpc_open_params op; + char *p_name = NULL; + NvOsSemaphoreHandle recv_sem = NULL; + NvU32 port_name[NVRPC_MAX_LOCAL_STACK/sizeof(NvU32)]; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + + if (op.port_name_size) { + p_name = nvrpc_stack_kzalloc(port_name, + op.port_name_size, GFP_KERNEL); + if (!p_name) { + error = -ENOMEM; + goto fail; + } + error = copy_from_user(p_name, (const void*)op.port_name, + op.port_name_size); + if (error) + goto fail; + if (p_name[op.port_name_size - 1] != 0) { + error = -EINVAL; + goto fail; + } + } + if (op.sem) { + NvOsSemaphoreHandle sem = (NvOsSemaphoreHandle) op.sem; + e = NvOsSemaphoreUnmarshal(sem, &recv_sem); + if (e != NvSuccess) + goto fail; + } + op.ret_val = NvRmTransportOpen(NULL, p_name, recv_sem, + (void *)&op.transport_handle); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + nvrpc_stack_kfree(port_name, p_name); + if (recv_sem) + NvOsSemaphoreDestroy(recv_sem); + if (e != NvSuccess) + error = nvrpc_make_error_code(e); + return error; +} + +static int nvrpc_ioctl_get_port_name(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + + struct nvrpc_open_params op; + NvS8 *p_name = NULL; + NvU32 port_name[NVRPC_MAX_LOCAL_STACK/sizeof(NvU32)]; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + if (op.port_name_size && op.port_name) { + p_name = nvrpc_stack_kzalloc(port_name, + op.port_name_size, GFP_KERNEL); + if (!p_name) { + error = -ENOMEM; + goto fail; + } + } + NvRmTransportGetPortName((NvRmTransportHandle)op.transport_handle, + p_name, op.port_name_size); + + if (op.port_name_size && p_name) { + error = copy_to_user((void*)op.port_name, + p_name, op.port_name_size * sizeof(NvU8)); + } + +fail: + nvrpc_stack_kfree(port_name, p_name); + return error; +} + +static int nvrpc_ioctl_close(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + NvRmTransportClose((void*)op.handle); + +fail: + return error; +} + +static int nvrpc_ioctl_wait_for_connect(struct file *filp, + unsigned int cmd, void __user *arg) +{ + NvError e = NvSuccess; + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmTransportWaitForConnect( + (void *)op.handle, op.param); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + if (e != NvSuccess) + error = nvrpc_make_error_code(e); + return error; +} + +static int nvrpc_ioctl_connect(struct file *filp, + unsigned int cmd, void __user *arg) +{ + NvError e = NvSuccess; + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmTransportConnect( + (void *)op.handle, op.param); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + if (e != NvSuccess) + error = nvrpc_make_error_code(e); + return error; +} + +static int nvrpc_ioctl_set_queue_depth(struct file *filp, + unsigned int cmd, void __user *arg) +{ + NvError e = NvSuccess; + int error; + struct nvrpc_set_queue_depth_params op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmTransportSetQueueDepth( + (NvRmTransportHandle)op.transport_handle, + op.max_queue_depth, + op.max_message_size); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + if (e != NvSuccess) + error = nvrpc_make_error_code(e); + return error; +} + +static int nvrpc_ioctl_send_msg(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_msg_params op; + void* msg_buffer = NULL; + NvU32 buffer[NVRPC_MAX_LOCAL_STACK/sizeof(NvU32)]; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + if (op.msg_buffer && op.max_message_size) { + msg_buffer = nvrpc_stack_kzalloc(buffer, + op.max_message_size, + GFP_KERNEL); + if (!msg_buffer) { + error = -ENOMEM; + goto fail; + } + error = copy_from_user(msg_buffer, + (void*)op.msg_buffer, + op.max_message_size); + if (error) + goto fail; + } + + op.ret_val = NvRmTransportSendMsg( + (NvRmTransportHandle)op.transport_handle, + msg_buffer, op.max_message_size, op.params); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + nvrpc_stack_kfree(buffer, msg_buffer); + return error; +} + +static int nvrpc_ioctl_send_msg_lp0(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_msg_params op; + void* msg_buffer = NULL; + NvU32 buffer[NVRPC_MAX_LOCAL_STACK/sizeof(NvU32)]; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + if (op.msg_buffer && op.max_message_size) { + msg_buffer = nvrpc_stack_kzalloc(buffer, + op.max_message_size, GFP_KERNEL); + if (!msg_buffer) { + error = -ENOMEM; + goto fail; + } + error = copy_from_user(msg_buffer, (void*)op.msg_buffer, + op.max_message_size); + if (error) + goto fail; + } + op.ret_val = NvRmTransportSendMsgInLP0( + (NvRmTransportHandle)op.transport_handle, + msg_buffer, op.max_message_size); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + nvrpc_stack_kfree(buffer, msg_buffer); + return error; +} + +static int nvrpc_ioctl_recv_msg(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_msg_params op; + void* msg_buffer = NULL; + NvU32 buffer[NVRPC_MAX_LOCAL_STACK/sizeof(NvU32)]; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + if (op.msg_buffer && op.max_message_size) { + msg_buffer = nvrpc_stack_kzalloc(buffer, + op.max_message_size, GFP_KERNEL); + if (!msg_buffer) { + error = -ENOMEM; + goto fail; + } + } else { + error = -EINVAL; + goto fail; + } + op.ret_val = NvRmTransportRecvMsg( + (NvRmTransportHandle)op.transport_handle, + msg_buffer, op.max_message_size, &op.params); + error = copy_to_user(arg, &op, sizeof(op)); + if (op.msg_buffer && msg_buffer) { + error = copy_to_user((void*)op.msg_buffer, + msg_buffer, op.max_message_size); + if (error) + goto fail; + } + +fail: + nvrpc_stack_kfree(buffer, msg_buffer); + return error; +} + +static int nvrpc_ioctl_xpc_init(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmXpcInitArbSemaSystem((void *)op.handle); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + return error; +} + +static int nvrpc_ioctl_xpc_acquire(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + NvRmXpcModuleAcquire(op.param); + +fail: + return error; +} + +static int nvrpc_ioctl_xpc_release(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + NvRmXpcModuleRelease(op.param); + +fail: + return error; +} + +static int nvrpc_ioctl_xpc_get_msg(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmPrivXpcGetMessage( + (NvRmPrivXpcMessageHandle)op.handle); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + return error; +} + +static int nvrpc_ioctl_xpc_send_msg(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmPrivXpcSendMessage( + (NvRmPrivXpcMessageHandle)op.handle, op.param); + error = copy_to_user(arg, &op, sizeof(op)); + +fail: + return error; +} + +static int nvrpc_ioctl_xpc_destroy(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + NvRmPrivXpcDestroy((NvRmPrivXpcMessageHandle)op.handle); + +fail: + return error; +} + +static int nvrpc_ioctl_xpc_create(struct file *filp, + unsigned int cmd, void __user *arg) +{ + int error; + struct nvrpc_handle_param op; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) + goto fail; + op.ret_val = NvRmPrivXpcCreate((NvRmDeviceHandle)op.handle, + (void*)&op.param); + error = copy_to_user(&op, arg, sizeof(op)); + +fail: + return error; +} + + +static long nvrpc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + void __user *uarg = (void __user *)arg; + + if (_IOC_TYPE(cmd) != NVRPC_IOC_MAGIC) + return -ENOTTY; + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, uarg, _IOC_SIZE(cmd)); + if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, uarg, _IOC_SIZE(cmd)); + + if (err) + return -EFAULT; + + switch (cmd) { + case NVRPC_IOCTL_OPEN: + err = nvrpc_ioctl_open(file, cmd, uarg); + break; + + case NVRPC_IOCTL_GET_PORTNAME: + err = nvrpc_ioctl_get_port_name(file, cmd, uarg); + break; + + case NVRPC_IOCTL_CLOSE: + err = nvrpc_ioctl_close(file, cmd, uarg); + break; + + case NVRPC_IOCTL_INIT: + case NVRPC_IOCTL_DEINIT: + break; + + case NVRPC_IOCTL_WAIT_FOR_CONNECT: + err = nvrpc_ioctl_wait_for_connect(file, cmd, uarg); + break; + + case NVRPC_IOCTL_CONNECT: + err = nvrpc_ioctl_connect(file, cmd, uarg); + break; + + case NVRPC_IOCTL_SET_QUEUE_DEPTH: + err = nvrpc_ioctl_set_queue_depth(file, cmd, uarg); + break; + + case NVRPC_IOCTL_SEND_MSG: + err = nvrpc_ioctl_send_msg(file, cmd, uarg); + break; + + case NVRPC_IOCTL_SEND_MSG_LP0: + err = nvrpc_ioctl_send_msg_lp0(file, cmd, uarg); + break; + + case NVRPC_IOCTL_RECV_MSG: + err = nvrpc_ioctl_recv_msg(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_INIT: + err = nvrpc_ioctl_xpc_init(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_ACQUIRE: + err = nvrpc_ioctl_xpc_acquire(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_RELEASE: + err = nvrpc_ioctl_xpc_release(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_GET_MSG: + err = nvrpc_ioctl_xpc_get_msg(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_SEND_MSG: + err = nvrpc_ioctl_xpc_send_msg(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_DESTROY: + err = nvrpc_ioctl_xpc_destroy(file, cmd, uarg); + break; + + case NVRPC_IOCTL_XPC_CREATE: + err = nvrpc_ioctl_xpc_create(file, cmd, uarg); + break; + + default: + return -ENOTTY; + } + return err; +} + +static int __init nvrpc_init(void) +{ + int ret = 0; + + ret = misc_register(&nvrpc_dev); + if (ret) { + pr_err("%s misc register FAILED\n", __func__); + } + return ret; +} + +static void __exit nvrpc_deinit(void) +{ + misc_deregister(&nvrpc_dev); +} + +module_init(nvrpc_init); +module_exit(nvrpc_deinit); |