summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2009-12-16 18:15:33 -0800
committerGary King <gking@nvidia.com>2009-12-16 19:00:56 -0800
commita3b0a82b33de621396c4d232b73fbe99cc59fe7d (patch)
treec60c8f1f07fa982bb5ae828ff097d7cf07bc0e0c
parent9e07905c40e642bb8aa47c4e903869f84da9bee8 (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
-rw-r--r--arch/arm/mach-tegra/include/linux/nvec_ioctls.h49
-rw-r--r--arch/arm/mach-tegra/nvec/nvec.c27
-rw-r--r--arch/arm/mach-tegra/nvec/nvec_private.h1
-rw-r--r--arch/arm/mach-tegra/nvec/smbus/nvec_i2c_transport.c59
-rw-r--r--arch/arm/mach-tegra/nvec_user.c146
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);