NvError NvEcPowerSuspend(
    NvEcPowerState PowerState)
{
    NvError e = NvSuccess;
    NvEcPrivState   *ec = &g_ec;

    NvOsMutexLock(ec->mutex);
    
    // Call transport's power off only if it's in ON state
    if (ec->powerState == NV_TRUE)
    {
        // Perform pre-suspend EC operations
        NV_CHECK_ERROR_CLEANUP( NvEcPrivPowerSuspendHook(ec->hEc, PowerState) );
        // Enter low power state
        ec->EnterLowPowerState = NV_TRUE;
        // Signal priv thread to get ready for power suspend.
        NvOsSemaphoreSignal(ec->sema);
        // Wait till priv thread is ready for power suspend.
        NvOsSemaphoreWait(ec->LowPowerEntrySema);
        e = NvEcTransportPowerSuspend(ec->transport);
        ec->powerState = NV_FALSE;
    }

fail:
    NvOsMutexUnlock(ec->mutex);
    return e;
}
Esempio n. 2
0
static void nvec_battery_remove(struct nvec_device *pdev)
{
	unsigned int i = 0;

	if (batt_dev) {
		batt_dev->exitThread = NV_TRUE;
		if (batt_dev->hOdmSemaphore) {
			NvOsSemaphoreSignal(batt_dev->hOdmSemaphore);
			NvOsSemaphoreDestroy(batt_dev->hOdmSemaphore);
			batt_dev->hOdmSemaphore = NULL;
		}

		if (batt_dev->hBattEventThread) {
			NvOsThreadJoin(batt_dev->hBattEventThread);
		}

		if (batt_dev->hOdmBattDev) {
			device_remove_file(&pdev->dev, &tegra_battery_attr);
			device_remove_file(&pdev->dev, &tegra_battery_version);  //Daniel 20100823, put ec version to fs

			del_timer_sync(&(batt_dev->battery_poll_timer));

			NvOdmBatteryDeviceClose(batt_dev->hOdmBattDev);
			batt_dev->hOdmBattDev = NULL;

			for (i = 0; i < ARRAY_SIZE(tegra_power_supplies); i++) {
				power_supply_unregister(&tegra_power_supplies[i]);
			}
		}

		kfree(batt_dev);
		batt_dev = NULL;

	}
}
NvError NvEcPowerResume(void)
{
    NvError e = NvSuccess;
    NvEcPrivState   *ec = &g_ec;
    
    NvOsMutexLock(ec->mutex);

    // Call transport's power on if it's OFF state
    if (ec->powerState == NV_FALSE)
    {
NvOsDebugPrintf("ec_rs NvEcPowerResume 1\n");
        NV_CHECK_ERROR_CLEANUP( NvEcTransportPowerResume(ec->transport) );

        ec->powerState = NV_TRUE;
        ec->EnterLowPowerState = NV_FALSE;
        // Signal priv thread to get out of power suspend.
        NvOsSemaphoreSignal(ec->LowPowerExitSema);
        // Perform post-resume EC operations
        NvEcPrivPowerResumeHook(ec->hEc);
NvOsDebugPrintf("ec_rs NvEcPowerResume 2\n");
    }

fail:
    NvOsMutexUnlock(ec->mutex);
    return e;
}
/* Process receive response and update individual timeout */
static NvError
NvEcPrivProcessReceiveResponse( NvEcPrivState *ec,
                                NvError       transportStatus )
{
    NvError             e = NvSuccess;
    NvEcResponse        packet;
    NvEcResponseNode    *responseNode = NULL;
    NvU32               size;

    // FIXME: remove this extra copying and buffering for optimization
    NV_CHECK_ERROR( NvEcTransportGetReceivePacket( ec->transport,
                                &packet, sizeof(NvEcResponse) ) );
                                // nothing we can do here if error!

    NvEcPrivFindAndDequeueResponse( ec, &packet, &responseNode);
    if ( responseNode )
    {
        size = responseNode->size;
        if ( size > sizeof(NvEcResponse) )
            size = sizeof(NvEcResponse);
        NvOsMemcpy( &responseNode->response, &packet, size );
        responseNode->status = transportStatus;
        NvOsSemaphoreSignal(responseNode->sema);
            // all response stuff should be completed before signalling
    }
    else
        e = NvError_BadParameter;
            // will return Bad Param if can't find waiting response

    return e;
}
Esempio n. 5
0
void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context)
{
	FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier;

	if (m_FxNotifier->Connected) {
		m_FxNotifier->Exit = 1;
		NvOsSemaphoreSignal(m_FxNotifier->hTransportSemaphore);
		NvOsThreadJoin(m_FxNotifier->hTransportThread);
		m_FxNotifier->hTransportThread = 0;
		tegra_transport_set_property(
			(NvAudioFxObjectHandle)m_FxNotifier->hNotifier,
			NvAudioFxNotifierProperty_Disconnect,
			0,
			0);
	}

	if (m_FxNotifier->hTransport) {
		NvRmTransportClose(m_FxNotifier->hTransport);
		m_FxNotifier->hTransport = 0;
	}

	if (m_FxNotifier->hNotifier) {
		tegra_transport_mixer_destroy_object(
		        (NvAudioFxObjectHandle)m_FxNotifier->hNotifier);
		m_FxNotifier->hNotifier = 0;
	}

	if (m_FxNotifier->hTransportSemaphore) {
		NvOsSemaphoreDestroy(m_FxNotifier->hTransportSemaphore);
		m_FxNotifier->hTransportSemaphore = 0;
	}

	return;
}
void
NvEcPrivProcessSendRequest( NvEcPrivState *ec )
{
    NvEcRequestNode *requestNode;
    
    NvOsMutexLock( ec->requestMutex );
    if ( ec->requestBegin )
    {
        DISP_MESSAGE(("\r\nProcessSendReq requestBegin=0x%x", ec->requestBegin));
        requestNode = ec->requestBegin;
        // Update timeouts
        requestNode->timeout = NVEC_REQUEST_TIMEOUT_DEFAULT;
        ec->timeout[NVEC_IDX_REQUEST] = requestNode->timeout;
        ec->timeoutBase[NVEC_IDX_REQUEST] = ec->lastTime;
        DISP_MESSAGE(("\r\nec->timeout[NVEC_IDX_REQUEST] is set to=%d", 
            ec->timeout[NVEC_IDX_REQUEST]));
        if ( NvSuccess != NvEcTransportAsyncSendPacket( ec->transport,
                    &requestNode->request, requestNode->size ) )
        {
            // transport send fail: de-queue, set status and signal
            ec->requestBegin = requestNode->next;
            if ( NULL == ec->requestBegin )
                ec->requestEnd = NULL;
            requestNode->status = NvError_I2cWriteFailed;
            DISP_MESSAGE(("\r\nProcessSendReq Async Send failed"));
            DISP_MESSAGE(("\r\nProcessSendReq requestBegin=0x%x", ec->requestBegin));
            NvOsSemaphoreSignal( requestNode->sema );
        }
    }
    NvOsMutexUnlock( ec->requestMutex );
}
/* We could potentially re-adjust the internal eventNode queue when there
   is no ready queue to base on accumulated NumEventPacket hint. */
NvError
NvEcGetEvent(
    NvEcEventRegistrationHandle hEcEventRegistration,
    NvEcEvent *pEvent,
    NvU32 EventSize)
{
    NvEcHandle      hEc = hEcEventRegistration->hEc;
    NvEcPrivState   *ec = hEc->ec;
    NvError         e = NvError_InvalidState;
    NvEcEventNode   *eventNode, *t;
    NvU32           tagMask = (1UL << hEc->tag);

    // FIXME: Should change these to assert ???
    if ( !pEvent )
        return NvError_InvalidAddress;

    if ( !hEcEventRegistration )
        return NvError_BadParameter;

    if ( EventSize > sizeof(NvEcEvent) )
        EventSize = sizeof(NvEcEvent);
    else if ( EventSize < NVEC_MIN_EVENT_SIZE )
        return NvError_InvalidSize;

    NvOsMutexLock( ec->eventMutex );
 
    eventNode = ec->eventReadyBegin;
    while ( eventNode )
    {
        // pre advance eventNode since current one could be removed
        t = eventNode;
        eventNode = eventNode->next;
        if ( (hEcEventRegistration->eventBitmap &
                            (1UL << t->event.EventType)) &&
                (t->tagBitmap & tagMask) )
        {
            // only return event with matching client tag set
            NvOsMemcpy( pEvent, &(t->event), EventSize );
            e = NvSuccess;
            t->tagBitmap &= ~tagMask;
            // removed if all registered client has GetEvent(t)
            if ( !t->tagBitmap )
            {
                // In case transport has been nack'ing due to eventFree
                // queue is all used up, signal thread to retrieve
                // queued up event in transport.  eventMutex should
                // synchronize NvEcPrivProcessReceiveEvent properly.
                if ( NULL == ec->eventFreeBegin )
                    NvOsSemaphoreSignal( ec->sema );
                NvEcPrivRemoveEventFromReady( ec, t );
            }
            break;
        }
    }
    
    NvOsMutexUnlock( ec->eventMutex );
    return e;
}
/*
 * Since send request is blocking, only need to check requestBegin:
 *  - returns the requestBegin if asked
 *  - check if requestBegin timeout (will signal back too)
 *  - Update requestBegin's timeout by rebasing to
 *    EcPrivThread-global time (hEc->lastTime).
 *  - reset queue timeout to NV_WAIT_INFINITE
 */
static void
NvEcPrivFindAndDequeueRequest( NvEcPrivState    *ec,
                               NvEcRequestNode  **pRequestNode,
                               NvError          transportStatus,
                               NvBool           skipTimeout )
{
    NvEcRequestNode *t = NULL;
    NvBool          remove = skipTimeout;
                            // always remove node with success if skipTimeout

    NvOsMutexLock( ec->requestMutex );
    NV_ASSERT(ec->requestBegin);
    t = ec->requestBegin;
    DISP_MESSAGE(("\r\nFindDQReq requestBegin=0x%x", ec->requestBegin));
    if ( t )
    {
        if ( pRequestNode )
            *pRequestNode = t;

        if ( skipTimeout )
            t->status = transportStatus;
        else if ( t->timeout <= NVEC_TIMEDIFF_WITH_BASE(ec, NVEC_IDX_REQUEST) )
        {
            NvEcTransportAbortSendPacket( ec->transport, &t->request, t->size );
            t->status = NvError_Timeout;
            remove = NV_TRUE;
            DISP_MESSAGE(("RQ time out Reqnode=0x%x", t));
        }
 
        if ( remove )
        {
            ec->requestBegin = t->next;
            if ( NULL == ec->requestBegin )
                ec->requestEnd = NULL;
            DISP_MESSAGE(("\r\nFindDQReq removed=0x%x, removed->next=0x%x, "
                "ec->requestBegin=0x%x, t->status=0x%x", 
                t, t->next, ec->requestBegin, t->status));
            if ( pRequestNode == NULL )
                NvOsSemaphoreSignal( t->sema );
        }
        else
        {
            t->timeout -= NVEC_TIMEDIFF_WITH_BASE(ec, NVEC_IDX_REQUEST);
                        // update this request timeout with lastTime base
        }

        // update with per-queue timeout and timeoutBase
        ec->timeout[NVEC_IDX_REQUEST] = NV_WAIT_INFINITE;
        ec->timeoutBase[NVEC_IDX_REQUEST] = ec->lastTime;
        DISP_MESSAGE(("\r\nec->timeout[NVEC_IDX_REQUEST] is set to=%d",
            ec->timeout[NVEC_IDX_REQUEST]));
    }
    NvOsMutexUnlock( ec->requestMutex );
}
Esempio n. 9
0
static void
PowerEventNotify(
    NvRmDeviceHandle hRmDeviceHandle,
    NvRmPowerEvent Event)
{
    NvU32 i;
    NvRmPowerClient* pPowerClient = NULL;
    NvRmPowerRegistry* pRegistry = &s_PowerRegistry;

    NVRM_POWER_PRINTF(("%s is reported to RM clients\n",
        (Event == NvRmPowerEvent_WakeLP0)? "Wake from LP0" : "Wake from LP1"));

    // Restore clocks after LP0
    if (Event == NvRmPowerEvent_WakeLP0)
        NvRmPrivClocksResume(hRmDeviceHandle);

    // Store event for all registered clients, and signal only those, that
    // have provided valid semaphore handle; on wake from low power states
    // set power cycled indicators
    for (i = 0; i < pRegistry->UsedIndexRange; i++)
    {
        pPowerClient = pRegistry->pPowerClients[i];
        if (pPowerClient != NULL)
        {
            ModuleVoltageReq* pVoltageReq = pPowerClient->pVoltageReqHead;
            while (pVoltageReq != NULL)
            {
                if (Event == NvRmPowerEvent_WakeLP0)
                {
                    //LP0: all power groups, except AO group, are powered down
                    // when core power is down
                    if (pVoltageReq->PowerGroup != NV_POWERGROUP_AO)
                        pVoltageReq->PowerCycled = NV_TRUE;
                }
                else if (Event == NvRmPowerEvent_WakeLP1)
                {
                    // LP1: core power is preserved; but all  power groups
                    // except AO and NPG group are power gated
                    if ((pVoltageReq->PowerGroup != NV_POWERGROUP_AO) &&
                        (pVoltageReq->PowerGroup != NV_POWERGROUP_NPG))
                        pVoltageReq->PowerCycled = NV_TRUE;
                }
                pVoltageReq = pVoltageReq->pNext;
            }
            pPowerClient->Event = Event;
            if (pPowerClient->hEventSemaphore != NULL)
            {
                NvOsSemaphoreSignal(pPowerClient->hEventSemaphore);
            }
        }
    }
}
Esempio n. 10
0
static ssize_t tegra_battery_store_property(
		struct device *dev,
		struct device_attribute *attr,
		const char *buf,
		size_t count)
{
	unsigned int value = 0;
	value = simple_strtoul(buf, NULL, 0);
    NvOsMutexLock(batt_dev->hBattEventMutex);
	batt_dev->batt_status_poll_period = value;
    NvOsMutexUnlock(batt_dev->hBattEventMutex);
	NvOsSemaphoreSignal(batt_dev->hOdmSemaphore);
	return count;
}
Esempio n. 11
0
static void PmuThreadTerminate(NvRmPmu* pPmu)
{
    /*
     * Request thread abort, signal semaphore to make sure the thread is
     * awaken and wait for its self-termination. Do nothing if invalid PMU
     * structure
     */
    if (pPmu)
    {
        if (pPmu->hSemaphore && pPmu->hThread)
        {
            pPmu->AbortThread = NV_TRUE;
            NvOsSemaphoreSignal(pPmu->hSemaphore);
            NvOsThreadJoin(pPmu->hThread);
        }
        pPmu->AbortThread = NV_FALSE;
    }
}
static void
NvEcPrivProcessPostSendRequest( NvEcPrivState *ec,
                                NvError transportStatus )
{
    NvEcRequestNode     *requestNode = NULL;
    NvEcResponseNode    *responseNode;

    NvEcPrivFindAndDequeueRequest(ec, &requestNode, transportStatus, NV_TRUE);

    // update corresponding responseNode timeout
    if ( requestNode )
    {
        requestNode->completed = NV_TRUE;
        responseNode = requestNode->responseNode;
        if ( responseNode )
        {
            NvOsMutexLock( ec->responseMutex );
            NV_ASSERT(ec->responseBegin);
            if ( NV_WAIT_INFINITE == ec->timeout[NVEC_IDX_RESPONSE] )
            {
                // no current pending response on timeout watch.
                // Update response queue timeout.
                responseNode->timeout = NVEC_RESPONSE_TIMEOUT_DEFAULT;
                ec->timeout[NVEC_IDX_RESPONSE] = responseNode->timeout;
                ec->timeoutBase[NVEC_IDX_RESPONSE] = ec->lastTime;
                DISP_MESSAGE(("\r\nec->timeout[NVEC_IDX_RESPONSE] is set to=%d", 
                    ec->timeout[NVEC_IDX_RESPONSE]));
            }
            else
            {
                // Update this response timeout with current lastTime as base
                responseNode->timeout = NVEC_RESPONSE_TIMEOUT_DEFAULT +
                                        NVEC_TIME_BASE(ec, NVEC_IDX_RESPONSE);
                                    // wraparound time difference will work too.
            }
            NvOsMutexUnlock( ec->responseMutex );
        }
        NvOsSemaphoreSignal( requestNode->sema );
            // all request stuff should be done before signal
    }
}
Esempio n. 13
0
void tegra_transport_deinit(void)
{
	if (!atrans)
		goto EXIT;

	spin_lock(&atrans->lock);
	atrans->RefCount--;

	if (atrans->RefCount > 0){
		spin_unlock(&atrans->lock);
		goto EXIT;
	}
	spin_unlock(&atrans->lock);

	atrans->ShutDown = 1;

	if (atrans->hRmTransport) {
		NvRmTransportClose(atrans->hRmTransport);
		atrans->hRmTransport = 0;
		atrans->TransportConnected = 0;
	}

	if (atrans->hServiceThread) {
		NvOsSemaphoreSignal(atrans->hServiceSema);
		NvOsThreadJoin(atrans->hServiceThread);
		atrans->hServiceThread = 0;
	}

	if (atrans->hServiceSema) {
		NvOsSemaphoreDestroy(atrans->hServiceSema);
		atrans->hServiceSema = 0;
	}
	atrans->hRmDevice = 0;
	kfree(atrans);
	atrans = 0;

EXIT:
	return;
}
Esempio n. 14
0
static void nvec_battery_remove(struct nvec_device *pdev)
{
	unsigned int i = 0;

	if (batt_dev) {
		batt_dev->exitThread = NV_TRUE;
		if (batt_dev->hOdmSemaphore) {
			NvOsSemaphoreSignal(batt_dev->hOdmSemaphore);
			NvOsSemaphoreDestroy(batt_dev->hOdmSemaphore);
			batt_dev->hOdmSemaphore = NULL;
		}

		if (batt_dev->hBattEventThread) {
			NvOsThreadJoin(batt_dev->hBattEventThread);
		}

	    if (batt_dev->hBattEventMutex) {    
		    NvOsMutexDestroy(batt_dev->hBattEventMutex);
		    batt_dev->hBattEventMutex = NULL;
	    }

		if (batt_dev->hOdmBattDev) {
			device_remove_file(&pdev->dev, &tegra_battery_attr);

			NvOdmBatteryDeviceClose(batt_dev->hOdmBattDev);
			batt_dev->hOdmBattDev = NULL;

			for (i = 0; i < ARRAY_SIZE(tegra_power_supplies); i++) {
				power_supply_unregister(&tegra_power_supplies[i]);
			}
		}

		kfree(batt_dev);
		batt_dev = NULL;

	}
}
Esempio n. 15
0
void NvOdmOsSemaphoreSignal(NvOdmOsSemaphoreHandle semaphore)
{
    NvOsSemaphoreSignal( (NvOsSemaphoreHandle)semaphore );
}
Esempio n. 16
0
static int nvec_battery_probe(struct nvec_device *pdev)
{
	int i, rc;
	NvError ErrorStatus = NvSuccess;
	NvBool result = NV_FALSE;

	batt_dev = kzalloc(sizeof(struct tegra_battery_dev), GFP_KERNEL);
	if (!batt_dev) {
		pr_err("nvec_battery_probe:NOMEM\n");
		return -ENOMEM;
	}

	ErrorStatus = NvOsSemaphoreCreate(&batt_dev->hOdmSemaphore, 0);
	if (NvSuccess != ErrorStatus) {
		pr_err("NvOsSemaphoreCreate Failed!\n");
		goto Cleanup;
	}

	batt_dev->exitThread = NV_FALSE;
	ErrorStatus = NvOsThreadCreate(NvBatteryEventHandlerThread,
					batt_dev,
					&(batt_dev->hBattEventThread));
	if (NvSuccess != ErrorStatus) {
		pr_err("NvOsThreadCreate FAILED\n");
		goto Cleanup;
	}

	result = NvOdmBatteryDeviceOpen(&(batt_dev->hOdmBattDev),
		(NvOdmOsSemaphoreHandle *)&batt_dev->hOdmSemaphore,
		&(batt_dev->ec_version)); //Daniel 20100823, put ec version to fs.
	if (!result || !batt_dev->hOdmBattDev) {
		pr_err("NvOdmBatteryDeviceOpen FAILED\n");
		goto Cleanup;
	}

	for (i = 0; i < ARRAY_SIZE(tegra_power_supplies); i++) {
		if (power_supply_register(&pdev->dev, &tegra_power_supplies[i]))
			pr_err("Failed to register power supply\n");
	}

	batt_dev->batt_status_poll_period = NVBATTERY_POLLING_INTERVAL_MILLISECS;
	setup_timer(&(batt_dev->battery_poll_timer), tegra_battery_poll_timer_func, 0);
	mod_timer(&(batt_dev->battery_poll_timer),
		jiffies + msecs_to_jiffies(batt_dev->batt_status_poll_period));

	rc = device_create_file(&pdev->dev, &tegra_battery_attr);
	rc = device_create_file(&pdev->dev, &tegra_battery_version); //Daniel 20100823, put ec version to fs
	if (rc) {
		for (i = 0; i < ARRAY_SIZE(tegra_power_supplies); i++) {
			power_supply_unregister(&tegra_power_supplies[i]);
		}

		del_timer_sync(&(batt_dev->battery_poll_timer));

		pr_err("nvec_battery_probe:device_create_file FAILED");
		goto Cleanup;
	}

	return 0;

Cleanup:
	batt_dev->exitThread = NV_TRUE;
	if (batt_dev->hOdmSemaphore) {
		NvOsSemaphoreSignal(batt_dev->hOdmSemaphore);
		NvOsSemaphoreDestroy(batt_dev->hOdmSemaphore);
		batt_dev->hOdmSemaphore = NULL;
	}

	if (batt_dev->hBattEventThread) {
		NvOsThreadJoin(batt_dev->hBattEventThread);
	}

	if (batt_dev->hOdmBattDev) {
		NvOdmBatteryDeviceClose(batt_dev->hOdmBattDev);
		batt_dev->hOdmBattDev = NULL;
	}

	kfree(batt_dev);
	batt_dev = NULL;
	return -1;
}
Esempio n. 17
0
void
NvEcClose(NvEcHandle hEc)
{
    NvEcPrivState   *ec;
    NvBool          destroy = NV_FALSE;

    if ( NULL == hEc )
        return;

    NV_ASSERT( s_refcount );

    ec = hEc->ec;
    NvOsMutexLock( ec->mutex );

    // FIXME: handle client still with outstanding event types
    if ( !--s_refcount )
    {
        NvEcPrivDeinitHook(ec->hEc);

        NV_ASSERT( NULL == ec->eventReg[hEc->tag].regBegin &&
                    NULL == ec->eventReg[hEc->tag].regEnd );
        NV_ASSERT( NULL == ec->requestBegin && NULL == ec->requestEnd );
        NV_ASSERT( NULL == ec->responseBegin && NULL == ec->responseEnd );
#ifndef CONFIG_TEGRA_ODM_BETELGEUSE
        ec->exitPingThread = NV_TRUE;
        NvOsSemaphoreSignal( ec->hPingSema );
        NvOsThreadJoin( ec->hPingThread );
#endif
        ec->exitThread = NV_TRUE;
        NvOsSemaphoreSignal( ec->sema );
        NvOsThreadJoin( ec->thread );

        NvEcTransportClose( ec->transport );
        NvOsMutexDestroy( ec->requestMutex );
        NvOsMutexDestroy( ec->responseMutex );
        NvOsMutexDestroy( ec->eventMutex );
        NvOsSemaphoreDestroy( ec->sema );
#ifndef CONFIG_TEGRA_ODM_BETELGEUSE
        NvOsSemaphoreDestroy( ec->hPingSema );
#endif
        NvOsSemaphoreDestroy( ec->LowPowerEntrySema );
        NvOsSemaphoreDestroy( ec->LowPowerExitSema );
        destroy = NV_TRUE;

        NvOsFree( ec->eventNodes );
        NvOsFree( ec->hEc );
    }

    // Set this flag as FALSE to indicate power is disabled
    //Daniel 20100723, if we change power state to NV_FALSE, we won't be able to suspend/poweroff it.
    //Is there any side effect ????? 
    //ec->powerState = NV_FALSE;

    NV_ASSERT( hEc->tag < NVEC_MAX_REQUESTOR_TAG );
    ec->tagAllocated[hEc->tag] = NV_FALSE;      // to be recycled

    NvOsFree( hEc );
    NvOsMutexUnlock( ec->mutex );

    if ( destroy )
    {
        NvOsMutexDestroy( ec->mutex );
        NvOsMemset( ec, 0, sizeof(NvEcPrivState) );
        ec->mutex = NULL;
    }
}
/*
 * Process receive (response & event) and update individual timeout.
 *
 * Return NvError_InsufficientMemory due to 2 conditions:
 * - internal event queue (ec->eventNodes) too small.
 * - client did NvEcRegisterForEvents.
 * Skip TransportGetReceivePacket and transport will keep NACK'ing EC in this
 * error case.
 */
static NvError
NvEcPrivProcessReceiveEvent( NvEcPrivState *ec,
                             NvError       transportStatus )
{
    NvError                 e = NvSuccess;
    NvEcEventNode           *eventNode = NULL;
    NvEcEventRegistration   *reg = NULL;
    NvEcEvent               *packet = NULL;
    NvEcEventType           eventType;
    NvU32                   i, tagBitmap;

    NvOsMutexLock( ec->eventMutex );
    if ( ec->eventFreeBegin )
    {
        eventNode = ec->eventFreeBegin;
        packet = &eventNode->event;
    }
    else
    {
        e = NvError_InsufficientMemory;
        goto fail;
    }
    NV_CHECK_ERROR( NvEcTransportGetReceivePacket( ec->transport,
                    (NvEcResponse *)packet, sizeof(NvEcEvent) ) );
                    // nothing we can do here if error!

    eventType = packet->EventType;
    NV_ASSERT( eventType < NvEcEventType_Num );

    e = NvError_InvalidState;  // init to event type never registered
    i = 0;
    tagBitmap = ec->eventTagBitmap[eventType];
    while( tagBitmap )
    {
        NV_ASSERT( i < NvEcEventType_Num );
        if ( tagBitmap & 1 )
        {
            reg = ec->eventMap[i][eventType];
            NV_ASSERT( reg );

            if ( NvSuccess != e )
            {
                // dequeue from free and enqueue into ready if not done yet
                ec->eventFreeBegin = eventNode->next;
                if ( ec->eventFreeBegin == NULL )
                    ec->eventFreeEnd = NULL;
                eventNode->timeout = NVEC_EVENT_TIMEOUT_DEFAULT;    // ???
                eventNode->tagBitmap = ec->eventTagBitmap[eventType];
                eventNode->next = NULL;
                NVEC_ENQ( ec->eventReady, eventNode );
                e = NvSuccess;
            }
            NvOsSemaphoreSignal( reg->sema );
        }
        i++;
        tagBitmap = tagBitmap >> 1;
    }

fail:
    NvOsMutexUnlock( ec->eventMutex );
    return e;
}
Esempio n. 19
0
static void PmuIsr(void* args)
{
    NvRmPmu* pPmu = (NvRmPmu*)args;
    NvOsSemaphoreSignal(pPmu->hSemaphore);
}
/*
 * Traverse response nodes with these:
 *  - one response matching the tag param (returns the node).  If bypassing
 *    tag checking, use INVALID tag as parameter.
 *  - all responses timeout (will signal back too)
 *  - Update individual responseNode's timeout by rebasing to
 *    EcPrivThread-global time (hEc->lastTime).
 *  - Update shortest timeout value for response queue.
 */
static void
NvEcPrivFindAndDequeueResponse( NvEcPrivState    *ec,
                                NvEcResponse     *response,
                                NvEcResponseNode **pResponseNode )
{
    NvEcResponseNode    *t = NULL, *p = NULL, *temp;
    NvU32               timeout = NV_WAIT_INFINITE;
    NvBool              remove = NV_FALSE, found = NV_FALSE;
    NvBool              SignalSema;
    
    NvOsMutexLock( ec->responseMutex );
    NV_ASSERT(ec->responseBegin);
    DISP_MESSAGE(("\r\nFindDQRes responseBegin=0x%x", ec->responseBegin));
    if ( ec->responseBegin )
    {
        t = ec->responseBegin;
        while( t )
        {
            SignalSema = NV_FALSE;
            /* FIXME: just match tag?  more to match?
             * There may be the cases where spurious response is received from EC.
             * Response should not be removed from the queue until req is complete.
             */
            DISP_MESSAGE(("t->tag=0x%x\n", t->tag));

            if (response)
                DISP_MESSAGE(("response->RequestorTag=0x%x\n", response->RequestorTag));

            if ( response && !found && (t->tag == response->RequestorTag) && 
                 t->requestNode->completed )
            {
                if ( pResponseNode )
                    *pResponseNode = t;
                found = NV_TRUE;
                remove = NV_TRUE;
            }
            else
            {
#if ENABLE_TIMEOUT
                if ( t->timeout <=
                            NVEC_TIMEDIFF_WITH_BASE(ec, NVEC_IDX_RESPONSE) )
                {
                    t->status = NvError_Timeout;
                    SignalSema = NV_TRUE;
                    remove = NV_TRUE;
                    DISP_MESSAGE(("Resp Timeout Respnode=0x%x", t));
                }
                else
                {
                    // This check is needed for spurious response case handling.
                    if (t->timeout != NV_WAIT_INFINITE)
                        t->timeout -=
                                NVEC_TIMEDIFF_WITH_BASE(ec, NVEC_IDX_RESPONSE);
                            // update this response timeout w/ lastTime as base
                }
#endif
            }
            
            if ( remove )
            {
                temp = t;
                NVEC_UNLINK( ec->response, t, p );
                DISP_MESSAGE(("\r\nFindDQRes removed=0x%x, removed->next=0x%x, "
                    "prev=0x%x  ec->responseBegin=0x%x", t, t->next, p, 
                    ec->responseBegin));
                remove = NV_FALSE;
                if (p)
                    t = p->next;
                else
                    t = ec->responseBegin;
                if (SignalSema == NV_TRUE)
                    NvOsSemaphoreSignal( temp->sema );
            }
            else
            {
                if ( timeout > t->timeout )
                    timeout = t->timeout;
                p = t;
                t = t->next;
            }
        }
        
        // update with per-queue timeout and timeoutBase
        ec->timeout[NVEC_IDX_RESPONSE] = timeout;
        ec->timeoutBase[NVEC_IDX_RESPONSE] = ec->lastTime;
        DISP_MESSAGE(("\r\nec->timeout[NVEC_IDX_RESPONSE] is set to=%d",
            ec->timeout[NVEC_IDX_RESPONSE]));
    }

    if (found == NV_FALSE)
        NvOsDebugPrintf("\r\n***NVEC:Received Spurious Response from EC.");
    NvOsMutexUnlock( ec->responseMutex );
}
void
NvEcClose(NvEcHandle hEc)
{
    NvEcPrivState   *ec;
    NvBool          destroy = NV_FALSE;

    if ( NULL == hEc )
        return;

    NV_ASSERT( s_refcount );

    ec = hEc->ec;
    NvOsMutexLock( ec->mutex );

    // FIXME: handle client still with outstanding event types
    if ( !--s_refcount )
    {
        NvEcPrivDeinitHook(ec->hEc);

        NV_ASSERT( NULL == ec->eventReg[hEc->tag].regBegin &&
                    NULL == ec->eventReg[hEc->tag].regEnd );
        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 );

        NvEcTransportClose( ec->transport );
        NvOsMutexDestroy( ec->requestMutex );
        NvOsMutexDestroy( ec->responseMutex );
        NvOsMutexDestroy( ec->eventMutex );
        NvOsSemaphoreDestroy( ec->sema );
        NvOsSemaphoreDestroy( ec->hPingSema );
        NvOsSemaphoreDestroy( ec->LowPowerEntrySema );
        NvOsSemaphoreDestroy( ec->LowPowerExitSema );
        destroy = NV_TRUE;

        NvOsFree( ec->eventNodes );
        NvOsFree( ec->hEc );
    }

    // Set this flag as FALSE to indicate power is disabled
    ec->powerState = NV_FALSE;

    NV_ASSERT( hEc->tag < NVEC_MAX_REQUESTOR_TAG );
    ec->tagAllocated[hEc->tag] = NV_FALSE;      // to be recycled

    NvOsFree( hEc );
    NvOsMutexUnlock( ec->mutex );

    if ( destroy )
    {
        NvOsMutexDestroy( ec->mutex );
        NvOsMemset( ec, 0, sizeof(NvEcPrivState) );
        ec->mutex = NULL;
    }
}
NvError
NvEcOpen(NvEcHandle *phEc,
         NvU32 InstanceId)
{
    NvEc            *hEc = NULL;
    NvU32           i;
    NvEcPrivState   *ec = &g_ec;
    NvOsMutexHandle mutex = NULL;
    NvError         e = NvSuccess;

    NV_ASSERT( phEc );

    if ( NULL == ec->mutex )
    {
        e = NvOsMutexCreate(&mutex);
        if (NvSuccess != e)
            return e;
        if (0 != NvOsAtomicCompareExchange32((NvS32*)&ec->mutex, 0,
                                                        (NvS32)mutex) )
            NvOsMutexDestroy( mutex );
    }

    NvOsMutexLock(ec->mutex);

    if ( !s_refcount )
    {
        mutex = ec->mutex;
        NvOsMemset( ec, 0, sizeof(NvEcPrivState) );
        ec->mutex = mutex;
        
        NV_CHECK_ERROR_CLEANUP( NvOsMutexCreate( &ec->requestMutex ));
        NV_CHECK_ERROR_CLEANUP( NvOsMutexCreate( &ec->responseMutex ));
        NV_CHECK_ERROR_CLEANUP( NvOsMutexCreate( &ec->eventMutex ));
        
        NV_CHECK_ERROR_CLEANUP( NvOsSemaphoreCreate( &ec->sema, 0));
        NV_CHECK_ERROR_CLEANUP( NvOsSemaphoreCreate( &ec->LowPowerEntrySema, 0));
        NV_CHECK_ERROR_CLEANUP( NvOsSemaphoreCreate( &ec->LowPowerExitSema, 0));
        
        NV_CHECK_ERROR_CLEANUP( NvEcTransportOpen( &ec->transport, InstanceId,
            ec->sema, 0 ) );
    }

    // Set this flag as TRUE to indicate power is enabled
    ec->powerState = NV_TRUE;

    // create private handle for internal communications between NvEc driver
    // and EC
    if ( !s_refcount )
    {
        ec->hEc = NvOsAlloc( sizeof(NvEc) );
        if ( NULL == ec->hEc )
            goto clean;
        
        // reserve the zero tag for internal use by the nvec driver; this ensures
        // that the driver always has a requestor tag available and can therefore
        // always talk to the EC
        ec->tagAllocated[0] = NV_TRUE;
        ec->hEc->ec = ec;
        ec->hEc->tag = 0;

        NV_CHECK_ERROR_CLEANUP(NvOsSemaphoreCreate(&ec->hPingSema, 0));

        // perform startup operations before mutex is unlocked
        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));
    }

    hEc = NvOsAlloc( sizeof(NvEc) );
    if ( NULL == hEc )
        goto clean;

    NvOsMemset(hEc, 0x00, sizeof(NvEc));

    hEc->ec = ec;

    hEc->tag = NVEC_REQUESTOR_TAG_INVALID;
    for ( i = 0; i < NVEC_MAX_REQUESTOR_TAG; i++ )
    {
        if ( !ec->tagAllocated[i] )
        {
            ec->tagAllocated[i] = NV_TRUE;
            hEc->tag = i;
            break;
        }
    }
    if ( NVEC_REQUESTOR_TAG_INVALID == hEc->tag )
        goto clean;      // run out of tag, clean it up!

    *phEc = hEc;
    s_refcount++;

    NvOsMutexUnlock( ec->mutex );

    ec->IsEcActive = NV_FALSE;

    return NvSuccess;

clean:
    NvOsFree( hEc );
    NvOsMutexUnlock( ec->mutex );

    return NvError_InsufficientMemory;

fail:
    if (!s_refcount)
    {
        ec->exitPingThread = NV_TRUE;
        if (ec->hPingSema)
            NvOsSemaphoreSignal( ec->hPingSema );
        NvOsThreadJoin( ec->hPingThread );
        NvOsSemaphoreDestroy(ec->hPingSema);
        ec->exitThread = NV_TRUE;
        if (ec->sema)
            NvOsSemaphoreSignal( ec->sema );
        NvOsThreadJoin( ec->thread );
        NvOsFree( ec->hEc );
        if ( ec->transport )
            NvEcTransportClose( ec->transport );
        NvOsMutexDestroy( ec->requestMutex );
        NvOsMutexDestroy( ec->responseMutex );
        NvOsMutexDestroy( ec->eventMutex );
        NvOsSemaphoreDestroy( ec->sema );
        NvOsSemaphoreDestroy( ec->LowPowerEntrySema );
        NvOsSemaphoreDestroy( ec->LowPowerExitSema );
        if ( ec->mutex )
        {
            NvOsMutexUnlock( ec->mutex );
            // Destroying of this mutex here is not safe, if another thread is
            // waiting on this mutex, it can cause issues.  We shold have
            // serialized Init/DeInit calls for creating and destroying this mutex.
            NvOsMutexDestroy( ec->mutex );
            NvOsMemset( ec, 0, sizeof(NvEcPrivState) );
            ec->mutex = NULL;
        }
    }
    return NvError_NotInitialized;
}
Esempio n. 23
0
static int nvec_battery_probe(struct nvec_device *pdev)
{
	int i, rc;
	NvError ErrorStatus = NvSuccess;
	NvBool result = NV_FALSE;

	batt_dev = kzalloc(sizeof(struct tegra_battery_dev), GFP_KERNEL);
	if (!batt_dev) {
		pr_err("nvec_battery_probe:NOMEM\n");
		return -ENOMEM;
	}

	ErrorStatus = NvOsMutexCreate(&batt_dev->hBattEventMutex);
	if (NvSuccess != ErrorStatus) {
		pr_err("NvOsMutexCreate Failed!\n");
		goto Cleanup;
	}

	ErrorStatus = NvOsSemaphoreCreate(&batt_dev->hOdmSemaphore, 0);
	if (NvSuccess != ErrorStatus) {
		pr_err("NvOsSemaphoreCreate Failed!\n");
		goto Cleanup;
	}
/*Henry++ adjust thread to be createn at last
	batt_dev->exitThread = NV_FALSE;
	batt_dev->inSuspend  = NV_FALSE;
	ErrorStatus = NvOsThreadCreate(NvBatteryEventHandlerThread,
					batt_dev,
					&(batt_dev->hBattEventThread));
	if (NvSuccess != ErrorStatus) {
		pr_err("NvOsThreadCreate FAILED\n");
		goto Cleanup;
	}
*/

//Henry add retry when fail ****
for(i=0;i<BATTERY_RETRY_TIMES;i++){
	result = NvOdmBatteryDeviceOpen(&(batt_dev->hOdmBattDev),
		(NvOdmOsSemaphoreHandle *)&batt_dev->hOdmSemaphore);
	if (!result || !batt_dev->hOdmBattDev) {
		pr_err("NvOdmBatteryDeviceOpen FAILED,retry i=%i\n",i);
		NvOsWaitUS(10000);
		continue;
	}
	else{
		break;
	}
}
if(i==BATTERY_RETRY_TIMES){
	pr_err("NvOdmBatteryDeviceOpen FAILED\n");	
	goto Cleanup;
}
//******************************
    
	for (i = 0; i < ARRAY_SIZE(tegra_power_supplies); i++) {
		if (power_supply_register(&pdev->dev, &tegra_power_supplies[i]))
			pr_err("Failed to register power supply\n");
	}

	batt_dev->batt_status_poll_period = NVBATTERY_POLLING_INTERVAL;

	rc = device_create_file(&pdev->dev, &tegra_battery_attr);
	if (rc) {
		for (i = 0; i < ARRAY_SIZE(tegra_power_supplies); i++) {
			power_supply_unregister(&tegra_power_supplies[i]);
		}

		pr_err("nvec_battery_probe:device_create_file FAILED");
		goto Cleanup;
	}

//Henry++ adjust thread to be createn at last
	batt_dev->exitThread = NV_FALSE;
	batt_dev->inSuspend  = NV_FALSE;
	ErrorStatus = NvOsThreadCreate(NvBatteryEventHandlerThread,
					batt_dev,
					&(batt_dev->hBattEventThread));
	if (NvSuccess != ErrorStatus) {
		pr_err("NvOsThreadCreate FAILED\n");
		goto Cleanup;
	}

	return 0;

Cleanup:
	batt_dev->exitThread = NV_TRUE;
	if (batt_dev->hOdmSemaphore) {
		NvOsSemaphoreSignal(batt_dev->hOdmSemaphore);
		NvOsSemaphoreDestroy(batt_dev->hOdmSemaphore);
		batt_dev->hOdmSemaphore = NULL;
	}

	if (batt_dev->hBattEventThread) {
		NvOsThreadJoin(batt_dev->hBattEventThread);
	}
    
	if (batt_dev->hBattEventMutex) {    
		NvOsMutexDestroy(batt_dev->hBattEventMutex);
		batt_dev->hBattEventMutex = NULL;
	}

	if (batt_dev->hOdmBattDev) {
		NvOdmBatteryDeviceClose(batt_dev->hOdmBattDev);
		batt_dev->hOdmBattDev = NULL;
	}

	kfree(batt_dev);
	batt_dev = NULL;
	//return -1;
   return 0;  //henry++ workaround system can't enter suspend
}
/*
 * Always use one EcPrivThread-global clock/time for timeout calculations
 */
static void
NvEcPrivThread( void * args )
{
    NvEcPrivState   *ec = (NvEcPrivState *)args;
    NvU32           t, timeout = NV_WAIT_INFINITE;
    NvU32           tStatus = 0;
    NvError         wait = NvSuccess;
    NvError         e;

    while( !ec->exitThread )
    {
    #if ENABLE_TIMEOUT
        if ( timeout )
            wait = NvOsSemaphoreWaitTimeout( ec->sema, timeout );
    #else
        NvOsSemaphoreWait( ec->sema );
        wait = NvSuccess;
    #endif
        
    #if ENABLE_FAKE_TIMEOUT_TEST
        t = ec->lastTime + 0x200;
    #else
        t = NvOsGetTimeMS();
    #endif
        ec->timeDiff = t - ec->lastTime;
        ec->lastTime = t;      // update last timer value
        
        if ( !timeout || (wait == NvError_Timeout) )
        {
            // timeout case
            NvEcPrivProcessTimeout( ec );
        }
        
        // look for any pending packets
        tStatus = NvEcTransportQueryStatus( ec->transport );
        
        e = NvSuccess;

        /* 
         * SEND_COMPLETE event must be processed before RESPONSE_RECEIVE_COMPLETE 
         * event as SEND_COMPLETE event schedules timeout for RESPONSE_RECEIVE event.
         */
        if ( tStatus & (NVEC_TRANSPORT_STATUS_SEND_COMPLETE |
                         NVEC_TRANSPORT_STATUS_SEND_ERROR) )
        {
            NvEcPrivProcessPostSendRequest( ec,
                (tStatus & NVEC_TRANSPORT_STATUS_SEND_COMPLETE) ?
                    NvSuccess : NvError_I2cWriteFailed );
        }

        if ( tStatus & (NVEC_TRANSPORT_STATUS_RESPONSE_RECEIVE_ERROR |
                    NVEC_TRANSPORT_STATUS_RESPONSE_RECEIVE_COMPLETE) )
        {
            e = (tStatus & NVEC_TRANSPORT_STATUS_RESPONSE_RECEIVE_COMPLETE) ?
                    NvSuccess : NvError_I2cReadFailed;
            e = NvEcPrivProcessReceiveResponse( ec, e );
            // return ignored.  Could be spurious response.
        }
        if ( tStatus & (NVEC_TRANSPORT_STATUS_EVENT_RECEIVE_ERROR |
                    NVEC_TRANSPORT_STATUS_EVENT_RECEIVE_COMPLETE) )
        {
            e = (tStatus & NVEC_TRANSPORT_STATUS_EVENT_RECEIVE_COMPLETE) ?
                    NvSuccess : NvError_I2cReadFailed;
            e = NvEcPrivProcessReceiveEvent( ec, e );
                // return ignored.  Could be spurious event.
        }

	if ( tStatus & NVEC_TRANSPORT_STATUS_EVENT_PACKET_MAX_NACK ) {
		// signal the ping thread to send a ping command since max
		// number of nacks have been sent to the EC
		if (ec->hPingSema) {
			NvOsSemaphoreSignal(ec->hPingSema);
		}
	}

        // send request whenever possible 
        if ( (ec->timeout[NVEC_IDX_REQUEST] == NV_WAIT_INFINITE) && 
             (ec->EnterLowPowerState == NV_FALSE) )
            NvEcPrivProcessSendRequest( ec );
    #if ENABLE_TIMEOUT
        timeout = NvEcPrivUpdateActualTimeout( ec ); 
    #endif
        
        if (ec->EnterLowPowerState)
        {
            // This code assumes that EC is already in kept in sleep mode by
            // either shim or top level code. And there will not be any activity
            // going on SM bus.
            if (ec->timeout[NVEC_IDX_REQUEST] != NV_WAIT_INFINITE)
            {
                NvOsDebugPrintf("\r\nNvEc has active requests during suspend. "
                    "It shouldn't have. check it.");
            }
            
            // No active request is pending. Enter into low power state.
            // Signal power suspend API to enter into suspend mode.
            NvOsSemaphoreSignal(ec->LowPowerEntrySema);
            // Wait till power resume API signals resume operation.
            NvOsSemaphoreWait(ec->LowPowerExitSema);
            // Update the timeouts for the active responses, which are scheduled 
            // to receive before system entering into suspend.
#if ENABLE_TIMEOUT
            NvEcPrivResetActiveRequestResponseTimeouts( ec );
            timeout = NvEcPrivUpdateActualTimeout( ec );
#endif // ENABLE_TIMEOUT
        }
    }
}
NvError
NvEcSendRequest(
    NvEcHandle hEc,
    NvEcRequest *pRequest,
    NvEcResponse *pResponse,
    NvU32 RequestSize,
    NvU32 ResponseSize)
{
    NvEcPrivState       *ec;
    NvError             e = NvSuccess;
    NvEcRequestNode     *requestNode = NULL;
    NvEcResponseNode    *responseNode = NULL;
    NvOsSemaphoreHandle requestSema = NULL;
    NvOsSemaphoreHandle responseSema = NULL;
    
    NV_ASSERT( pRequest );
    NV_ASSERT( hEc );
    if ( (RequestSize > sizeof(NvEcRequest)) || 
         (ResponseSize > sizeof(NvEcResponse)) )
        return NvError_InvalidSize;
    
    ec = hEc->ec;
    requestNode = NvOsAlloc(sizeof(NvEcRequestNode));
    if ( NULL == requestNode )
    {
        e = NvError_InsufficientMemory;
        goto fail;
    }
    NV_CHECK_ERROR_CLEANUP( NvOsSemaphoreCreate( &requestSema, 0 ) );
    
    if ( pResponse )
    {
        responseNode = NvOsAlloc(sizeof(NvEcResponseNode));
        if ( NULL == responseNode )
        {
            e = NvError_InsufficientMemory;
            goto fail;
        }
        NV_CHECK_ERROR_CLEANUP( NvOsSemaphoreCreate( &responseSema, 0 ) );
    }

    ec->IsEcActive = NV_TRUE;

    // request end-queue.  Timeout set to infinite until request sent.
    NvOsMemset( requestNode, 0, sizeof(NvEcRequestNode) );
    pRequest->RequestorTag = hEc->tag;      // assigned tag here
    DISP_MESSAGE(("NvEcSendRequest:pRequest->RequestorTag=0x%x\n", pRequest->RequestorTag));
    NvOsMemcpy(&requestNode->request, pRequest, RequestSize);
    requestNode->tag = hEc->tag;
    DISP_MESSAGE(("NvEcSendRequest:requestNode->tag=0x%x\n", requestNode->tag));
    requestNode->sema = requestSema;
    requestNode->timeout = NV_WAIT_INFINITE;
    requestNode->completed = NV_FALSE;
    requestNode->size = RequestSize;
    
    NvOsMutexLock( ec->requestMutex );
    NVEC_ENQ( ec->request, requestNode );
    DISP_MESSAGE(("\r\nSendReq ec->requestBegin=0x%x", ec->requestBegin));
    NvOsMutexUnlock( ec->requestMutex );
    
    // response en-queue.  Timeout set to infinite until request completes.
    if ( pResponse )
    {
        NvOsMemset( responseNode, 0, sizeof(NvEcResponseNode) );
        requestNode->responseNode = responseNode;   // association between
        responseNode->requestNode = requestNode;    //   request & response
        responseNode->sema = responseSema;
        responseNode->timeout = NV_WAIT_INFINITE;
        responseNode->tag = hEc->tag;
        DISP_MESSAGE(("NvEcSendRequest:responseNode->tag=0x%x\n", responseNode->tag));
        responseNode->size = ResponseSize;
        NvOsMutexLock( ec->responseMutex );
        NVEC_ENQ( ec->response, responseNode );
        DISP_MESSAGE(("\r\nSendReq ec->responseBegin=0x%x", ec->responseBegin));
        NvOsMutexUnlock( ec->responseMutex );
    }

    NvOsMutexLock( ec->mutex );
    if ( !ec->thread )
        NvEcPrivThreadCreate( ec );
    NvOsMutexUnlock( ec->mutex );

    // Trigger EcPrivThread
    NvOsSemaphoreSignal( ec->sema );
    DISP_MESSAGE(("\r\nSendReq requestNode=0x%x, requestNode->responseNode=0x%x",
        requestNode, requestNode->responseNode));
    // Wait on Request returns
    NvOsSemaphoreWait( requestSema );
    DISP_MESSAGE(("\r\nSendReq Out of req sema"));

    e = requestNode->status;
    if ( NvSuccess != e )
    {
        NvEcResponseNode    *t = NULL, *p = NULL;

        // de-queue responseNode too !!!!
        NvOsMutexLock( ec->responseMutex );
        NVEC_REMOVE_FROM_Q( ec->response, responseNode, t, p );
        DISP_MESSAGE(("\r\nSendReq responseBegin=0x%x", ec->responseBegin));
        NvOsMutexUnlock( ec->responseMutex );
        goto fail;
    }

    if ( pResponse )
    {
        // Wait on Response returns
        NvOsSemaphoreWait( responseSema );
        DISP_MESSAGE(("\r\nSendReq Out of resp sema"));
        NV_CHECK_ERROR_CLEANUP( responseNode->status );
        NvOsMemcpy(pResponse, &responseNode->response, ResponseSize);
    }
    // if successful, nodes should be de-queue already but not freed yet

fail:
    NvOsSemaphoreDestroy( requestSema );
    NvOsSemaphoreDestroy( responseSema );
    DISP_MESSAGE(("\r\nSendReq Freeing requestNode=0x%x, responseNode=0x%x", 
        requestNode, responseNode));
    NvOsFree( requestNode );
    NvOsFree( responseNode );
    return e;
}