diff options
author | Gary King <gking@nvidia.com> | 2010-06-29 15:33:50 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2010-10-13 14:17:39 -0700 |
commit | 6aff948d09d067b92fc9e86bb082499f5de51d61 (patch) | |
tree | 36837329fa104678fb2525622574c50ee7e54b05 /arch/arm/mach-tegra/nv | |
parent | 6d111b7d2fc3f588560846c9261fc5e84021e897 (diff) |
[ARM] tegra: add nvrm transport interface
add nvrm transport kernel APIs and nvrpc device node access to
it, to support basic multimedia and AVP access
add enough nvrmmodule functions to get RM transport bootstrapped
and to support resetting the AVP (which is needed after the AVP
kernel is loaded by nvrm_daemon).
Change-Id: I9b2299db43daceb0c238c3876a8fabf1b6352e34
Signed-off-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/nv')
-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 |
13 files changed, 3189 insertions, 483 deletions
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); |