diff options
author | Gary King <gking@nvidia.com> | 2009-12-16 18:15:33 -0800 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2009-12-16 19:00:56 -0800 |
commit | a3b0a82b33de621396c4d232b73fbe99cc59fe7d (patch) | |
tree | c60c8f1f07fa982bb5ae828ff097d7cf07bc0e0c /arch | |
parent | 9e07905c40e642bb8aa47c4e903869f84da9bee8 (diff) |
tegra: use new I2C slave mode for NvEc, enable user-space ioctls for NvEc
implements user-space (ioctl'd) access to NvEc dispatcher
switches the I2C transport on Tegra 2 to use the new slave mode, which should
provide more stable communication
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/include/linux/nvec_ioctls.h | 49 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvec/nvec.c | 27 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvec/nvec_private.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvec/smbus/nvec_i2c_transport.c | 59 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvec_user.c | 146 |
5 files changed, 242 insertions, 40 deletions
diff --git a/arch/arm/mach-tegra/include/linux/nvec_ioctls.h b/arch/arm/mach-tegra/include/linux/nvec_ioctls.h new file mode 100644 index 000000000000..ecc50d0436d0 --- /dev/null +++ b/arch/arm/mach-tegra/include/linux/nvec_ioctls.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008-2009 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NVEC_IOCTLS_H +#define NVEC_IOCTLS_H + + +/* When we trap into the kernel, the majority of the ioctls + * are handled by the Generic handler, which is automatically + * generated by the IDL compiler. + * + */ + +typedef enum +{ + NvECKernelIoctls_Generic = 8000, + NvECKernelIoctls_ForceWord = 0x7FFFFFFF, +} NvECKernelIoctls; + +#endif diff --git a/arch/arm/mach-tegra/nvec/nvec.c b/arch/arm/mach-tegra/nvec/nvec.c index 1592171e24e6..e1ffae6d52e2 100644 --- a/arch/arm/mach-tegra/nvec/nvec.c +++ b/arch/arm/mach-tegra/nvec/nvec.c @@ -235,10 +235,8 @@ NvEcPrivPingThread(void *args) NvEcResponse resp; NvEcPrivState *ec = (NvEcPrivState *)args; - while (!ec->exitThread) + while (!ec->exitPingThread) { - NvStatus = NvOsSemaphoreWaitTimeout(ec->hPingSema, NVEC_PING_TIMEOUT); - if (NvStatus == NvError_Timeout) { // send no-op commands @@ -264,6 +262,7 @@ NvEcPrivPingThread(void *args) DISP_MESSAGE(("NvEcPrivPingThread: no-op command sent\n")); } + NvStatus = NvOsSemaphoreWaitTimeout(ec->hPingSema, NVEC_PING_TIMEOUT); } } @@ -272,7 +271,7 @@ NvEcOpen(NvEcHandle *phEc, NvU32 InstanceId) { NvEc *hEc = NULL; - int i; + NvU32 i; NvEcPrivState *ec = &g_ec; NvOsMutexHandle mutex = NULL; NvError e = NvSuccess; @@ -333,7 +332,8 @@ NvEcOpen(NvEcHandle *phEc, NV_CHECK_ERROR_CLEANUP( NvEcPrivInitHook(ec->hEc) ); // start thread to send "pings" - no-op commands to keep EC "alive" - NV_CHECK_ERROR_CLEANUP(NvOsThreadCreate((NvOsThreadFunction)NvEcPrivPingThread, ec, &ec->hPingThread)); + NV_CHECK_ERROR_CLEANUP(NvOsThreadCreate( + (NvOsThreadFunction)NvEcPrivPingThread, ec, &ec->hPingThread)); } hEc = NvOsAlloc( sizeof(NvEc) ); @@ -373,15 +373,15 @@ clean: fail: if (!s_refcount) { - ec->exitThread = NV_TRUE; - NvOsSemaphoreSignal( ec->sema ); - NvOsThreadJoin( ec->thread ); + ec->exitPingThread = NV_TRUE; if (ec->hPingSema) NvOsSemaphoreSignal( ec->hPingSema ); NvOsThreadJoin( ec->hPingThread ); - ec->hPingThread = NULL; NvOsSemaphoreDestroy(ec->hPingSema); - ec->hPingSema = NULL; + ec->exitThread = NV_TRUE; + if (ec->sema) + NvOsSemaphoreSignal( ec->sema ); + NvOsThreadJoin( ec->thread ); NvOsFree( ec->hEc ); if ( ec->transport ) NvEcTransportClose( ec->transport ); @@ -429,12 +429,12 @@ NvEcClose(NvEcHandle hEc) NV_ASSERT( NULL == ec->requestBegin && NULL == ec->requestEnd ); NV_ASSERT( NULL == ec->responseBegin && NULL == ec->responseEnd ); + ec->exitPingThread = NV_TRUE; + NvOsSemaphoreSignal( ec->hPingSema ); + NvOsThreadJoin( ec->hPingThread ); ec->exitThread = NV_TRUE; NvOsSemaphoreSignal( ec->sema ); NvOsThreadJoin( ec->thread ); - NvOsSemaphoreSignal( ec->hPingSema ); - NvOsThreadJoin( ec->hPingThread ); - ec->hPingThread = NULL; NvEcTransportClose( ec->transport ); NvOsMutexDestroy( ec->requestMutex ); @@ -442,7 +442,6 @@ NvEcClose(NvEcHandle hEc) NvOsMutexDestroy( ec->eventMutex ); NvOsSemaphoreDestroy( ec->sema ); NvOsSemaphoreDestroy( ec->hPingSema ); - ec->hPingSema = NULL; NvOsSemaphoreDestroy( ec->LowPowerEntrySema ); NvOsSemaphoreDestroy( ec->LowPowerExitSema ); destroy = NV_TRUE; diff --git a/arch/arm/mach-tegra/nvec/nvec_private.h b/arch/arm/mach-tegra/nvec/nvec_private.h index b773fe5a815a..ac2fc811b62a 100644 --- a/arch/arm/mach-tegra/nvec/nvec_private.h +++ b/arch/arm/mach-tegra/nvec/nvec_private.h @@ -203,6 +203,7 @@ typedef struct NvEcPrivStateRec /* thread handle which sends "pings" to the EC */ NvOsThreadHandle hPingThread; NvOsSemaphoreHandle hPingSema; + NvBool exitPingThread; } NvEcPrivState; diff --git a/arch/arm/mach-tegra/nvec/smbus/nvec_i2c_transport.c b/arch/arm/mach-tegra/nvec/smbus/nvec_i2c_transport.c index 57b137edddce..1560c04d71fe 100644 --- a/arch/arm/mach-tegra/nvec/smbus/nvec_i2c_transport.c +++ b/arch/arm/mach-tegra/nvec/smbus/nvec_i2c_transport.c @@ -53,7 +53,9 @@ #include "nvrm_hardware_access.h" #include "nvodm_query_discovery.h" -#define ENABLE_NEW_SLAVE 0 +#define ENABLE_NEW_SLAVE 1 +#define ADD_5US_DELAY 1 +#define DELAY_COUNT 0x1E #ifndef __KERNEL__ #define __KERNEL__ 0 @@ -103,6 +105,7 @@ typedef struct { NvU8 Buffer[CAPTURE_DATA_BUFFER_SIZE]; NvU32 sts[CAPTURE_DATA_BUFFER_SIZE]; + NvBool PktStatus[CAPTURE_DATA_BUFFER_SIZE]; NvU32 Index; } I2cSlaveData; volatile I2cSlaveData g_I2cSlaveRxData; @@ -292,7 +295,8 @@ static void HwI2cHandleVariableRead( NvEcTransportHandle t, NvU32 SlaveStatus, - NvBool IsPECSupported) + NvBool IsPECSupported, + NvU64 IsrStartTime) { NvU8 DataToSend = 0xFD; NvBool ValidProtocol = NV_FALSE; @@ -309,6 +313,11 @@ HwI2cHandleVariableRead( if (NV_DRF_VAL(I2C, I2C_SL_STATUS, RCVD, SlaveStatus)) { + #if ADD_5US_DELAY + // Work around for AP20 New Slave Hw Bug. Give 1us extra. + while( (NvOsGetTimeUS() - IsrStartTime) < (5 + 1) ); + #endif + /* * Write the 1st data byte to the master provided the slave read * transaction has re-started immediately after the master wrote @@ -381,28 +390,36 @@ exit: if (g_I2cSlaveTxData.Index >= CAPTURE_DATA_BUFFER_SIZE) g_I2cSlaveTxData.Index = 0; g_I2cSlaveTxData.sts[g_I2cSlaveTxData.Index] = SlaveStatus; + g_I2cSlaveTxData.PktStatus[g_I2cSlaveTxData.Index] = t->SendPending; g_I2cSlaveTxData.Buffer[g_I2cSlaveTxData.Index++] = DataToSend; #endif } -static NvU32 ReadSlaveRcvdRegister(NvRmI2cSlaveController* t) +static NvU32 ReadSlaveRcvdRegister(NvRmI2cSlaveController* t, NvU32 SlaveStatus) { NvU32 Data; + NvU32 ClearReg; - #if (__KERNEL__) && (NVOS_IS_LINUX) - unsigned long flags; - local_irq_save(flags); - #endif - - // Read data byte to release the bus. - Data = (NvU8)I2C_REGR(t, I2C_SL_RCVD); - // Workaround for AP20 New I2C Slave Controller bug #626607. - if (t->I2cSocCaps.IsNewSlaveAvailable) + ClearReg = NV_DRF_VAL(I2C, I2C_SL_STATUS, RCVD, SlaveStatus); + if (t->I2cSocCaps.IsNewSlaveAvailable && ClearReg) + { + #if (__KERNEL__) && (NVOS_IS_LINUX) + unsigned long flags; + local_irq_save(flags); + #endif + // Read data byte to release the bus. + Data = (NvU8)I2C_REGR(t, I2C_SL_RCVD); + // Workaround for AP20 New I2C Slave Controller bug #626607. I2C_REGW(t, I2C_SL_RCVD, 0); - - #if (__KERNEL__) && (NVOS_IS_LINUX) - local_irq_restore(flags); - #endif + #if (__KERNEL__) && (NVOS_IS_LINUX) + local_irq_restore(flags); + #endif + } + else + { + // Read data byte to release the bus. + Data = (NvU8)I2C_REGR(t, I2C_SL_RCVD); + } return Data; } @@ -425,6 +442,7 @@ static void Isr(void* args) // FIXME: Get it from ODM? NvBool IsPECSupported = NV_FALSE; NvRmI2cSlaveController* t = args; + NvU64 IsrStartTime = NvOsGetTimeUS(); NvU8 PECBytes = IsPECSupported ? 1 : 0; NvU32 ExpectedInterruptMask = NV_DRF_DEF(I2C, I2C_SL_STATUS, END_TRANS, DEFAULT_MASK) | @@ -456,13 +474,13 @@ static void Isr(void* args) // Check if Master is requesting data from Slave. i.e Read from Slave. if (NV_DRF_VAL(I2C, I2C_SL_STATUS, RNW, SlaveStatus)) { - HwI2cHandleVariableRead(t, SlaveStatus, IsPECSupported); + HwI2cHandleVariableRead(t, SlaveStatus, IsPECSupported, IsrStartTime); } // Data is sent to Slave by Master. i.e Write to Slave. else if (t->PacketBuffer != NULL) { // Read data byte to release the bus. - Data = ReadSlaveRcvdRegister(t); + Data = ReadSlaveRcvdRegister(t, SlaveStatus); /* * If the 1st Command byte of the new transaction is received, reset the * packet counter regardless of the previous count, and store the @@ -519,6 +537,7 @@ static void Isr(void* args) if (g_I2cSlaveRxData.Index >= CAPTURE_DATA_BUFFER_SIZE) g_I2cSlaveRxData.Index = 0; g_I2cSlaveRxData.sts[g_I2cSlaveRxData.Index] = SlaveStatus; + g_I2cSlaveRxData.PktStatus[g_I2cSlaveRxData.Index] = PktRcvd; g_I2cSlaveRxData.Buffer[g_I2cSlaveRxData.Index++] = Data; #endif } @@ -528,7 +547,7 @@ static void Isr(void* args) SlaveConfig = DISABLE_I2C_SLAVE(t); I2C_REGW(t, I2C_SL_CNFG, SlaveConfig); // Read data byte to release bus. - Data = ReadSlaveRcvdRegister(t); + Data = ReadSlaveRcvdRegister(t, SlaveStatus); PRINT_I2C_MESSAGES(("DR=0x%x ", Data)); #if CAPTURE_TX_RX_DATA if (g_I2cSlaveRxData.Index >= CAPTURE_DATA_BUFFER_SIZE) @@ -628,6 +647,8 @@ static NvError HwI2cInitController(NvRmI2cSlaveController* t) // Set the slave address and 7-bit address mode. I2C_REGW(t, I2C_SL_ADDR1, (t->SlaveAddress >> 1)); I2C_REGW(t, I2C_SL_ADDR2, 0); + // Set Delay count register + I2C_REGW(t, I2C_SL_DELAY_COUNT, DELAY_COUNT); // Enable Ack and disable response to general call. SlaveConfig = ENABLE_I2C_SLAVE(t); //NvOsDebugPrintf("\n***SlaveConfig=0x%x", SlaveConfig); diff --git a/arch/arm/mach-tegra/nvec_user.c b/arch/arm/mach-tegra/nvec_user.c index c2a555baf8db..621ebb61e346 100644 --- a/arch/arm/mach-tegra/nvec_user.c +++ b/arch/arm/mach-tegra/nvec_user.c @@ -23,10 +23,15 @@ #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/miscdevice.h> +#include "linux/nvos_ioctl.h" #include "nvec.h" +#include "linux/nvec_ioctls.h" #include "nvreftrack.h" +#include "nvassert.h" -NvError NvEc_Dispatch(void *InBuffer, NvU32 InSize, void *OutBuffer, +static NvRtHandle s_RtHandle = NULL; + +NvError NvECPackage_Dispatch(void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx); static int nvec_open(struct inode *inode, struct file *file); @@ -34,20 +39,129 @@ static int nvec_close(struct inode *inode, struct file *file); static long nvec_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -int nvec_open(struct inode *inode, struct file *filp) +int nvec_open(struct inode *inode, struct file *file) { + NvRtClientHandle Client; + + if (NvRtRegisterClient(s_RtHandle, &Client) != NvSuccess) + return -ENOMEM; + + file->private_data = (void*)Client; return 0; } -int nvec_close(struct inode *inode, struct file *filp) +int nvec_close(struct inode *inode, struct file *file) { + NvRtClientHandle client = (NvRtClientHandle)file->private_data; + + if (NvRtUnregisterClient(s_RtHandle, client)) { + NvDispatchCtx dctx; + + dctx.Rt = s_RtHandle; + dctx.Client = client; + dctx.PackageIdx = 0; + + // TODO: Enable this code for freeing up leaked handles + #if 0 + for (;;) + { + void* ptr = NvRtFreeObjRef(&dctx, + NvRtObjType_NvEc_NvEcHandle, NULL); + if (!ptr) break; + NVRT_LEAK("NvEc", "NvEcHandle", ptr); + NvEcClose(ptr); + } + #endif + + NvRtUnregisterClient(s_RtHandle, client); + } return 0; } long nvec_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return 0; + NvError err; + NvOsIoctlParams p; + NvU32 size; + NvU32 small_buf[8]; + void *ptr = 0; + long e; + NvBool bAlloc = NV_FALSE; + + switch( cmd ) { + case NvECKernelIoctls_Generic: + { + NvDispatchCtx dctx; + + dctx.Rt = s_RtHandle; + dctx.Client = (NvRtClientHandle)file->private_data; + dctx.PackageIdx = 0; + + err = NvOsCopyIn(&p, (void *)arg, sizeof(p)); + if (err != NvSuccess) { + printk("NvECKernelIoctls_Generic: copy in failed\n"); + goto fail; + } + + size = p.InBufferSize + p.InOutBufferSize + p.OutBufferSize; + if (size <= sizeof(small_buf)) { + ptr = small_buf; + } else { + ptr = NvOsAlloc(size); + if (!ptr) { + printk("NvECKernelIoctls_Generic: alloc err\n"); + goto fail; + } + + bAlloc = NV_TRUE; + } + + err = NvOsCopyIn(ptr, p.pBuffer, p.InBufferSize + + p.InOutBufferSize); + if (err != NvSuccess) { + printk("NvECKernelIoctls_Generic: copy in failure\n"); + goto fail; + } + + err = NvECPackage_Dispatch(ptr, + p.InBufferSize + p.InOutBufferSize, + ((NvU8 *)ptr) + p.InBufferSize, p.InOutBufferSize + + p.OutBufferSize, &dctx); + if (err != NvSuccess) { + printk("NvECKernelIoctls_Generic: dispatch failure\n"); + goto fail; + } + + if (p.InOutBufferSize || p.OutBufferSize) { + err = NvOsCopyOut( + ((NvU8 *)((NvOsIoctlParams *)arg)->pBuffer) + + p.InBufferSize, + ((NvU8 *)ptr) + p.InBufferSize, + p.InOutBufferSize + p.OutBufferSize); + if (err != NvSuccess) { + printk("NvECKernelIoctls_Generic: copyout err\n"); + goto fail; + } + } + + break; + } + default: + printk("unknown ioctl code\n"); + goto fail; + } + e = 0; + goto clean; + +fail: + e = -EINVAL; + +clean: + if (bAlloc) + NvOsFree(ptr); + + return e; } #define DEVICE_NAME "nvec" @@ -73,20 +187,38 @@ static int __init nvec_init( void ) { int e = 0; NvError status; - printk("nvec init\n"); + NvU32 NumTypes = 1; // TODO: must have NvRtObjType_NvEc_Num instead; + + NV_ASSERT(s_RtHandle == NULL); + + if (NvRtCreate(1, &NumTypes, &s_RtHandle) != NvSuccess) { + printk("nvec NvRtCreate returned error\n"); + return -ENOMEM; + } status = NvEcOpen(&s_NvEcHandle, 0); if (status != NvError_Success) { + printk("nvec NvEcOpen returned 0x%x\n", status); return -EINVAL; } - e = misc_register( &nvec_dev ); + + e = misc_register(&nvec_dev); + if (e < 0) { + if (s_RtHandle) { + NvRtDestroy(s_RtHandle); + s_RtHandle = NULL; + } + printk("nvec failed to open\n"); + } return e; } static void __exit nvec_deinit( void ) { NvEcClose(s_NvEcHandle); - misc_deregister( &nvec_dev ); + misc_deregister(&nvec_dev); + NvRtDestroy(s_RtHandle); + s_RtHandle = NULL; } module_init(nvec_init); |