NvError NvRmPrivPmuInit(NvRmDeviceHandle hRmDevice) { NvError e; ExecPlatform env; NvOdmPmuProperty PmuProperty; NV_ASSERT(hRmDevice); env = NvRmPrivGetExecPlatform(hRmDevice); NvOsMemset(&s_Pmu, 0, sizeof(NvRmPmu)); s_PmuSupportedEnv = NV_FALSE; if (env == ExecPlatform_Soc) { // Set supported environment flag s_PmuSupportedEnv = NV_TRUE; // Create the PMU mutex, semaphore, interrupt handler thread, // register PMU interrupt, and get ODM PMU handle NV_CHECK_ERROR_CLEANUP(NvOsMutexCreate(&s_Pmu.hMutex)); NV_CHECK_ERROR_CLEANUP(NvOsSemaphoreCreate(&s_Pmu.hSemaphore, 0)); if (NvOdmQueryGetPmuProperty(&PmuProperty) && PmuProperty.IrqConnected) { if (hRmDevice->ChipId.Id >= 0x20) NvRmPrivAp20SetPmuIrqPolarity( hRmDevice, PmuProperty.IrqPolarity); else NV_ASSERT(PmuProperty.IrqPolarity == NvOdmInterruptPolarity_Low); { NvOsInterruptHandler hPmuIsr = PmuIsr; NvU32 PmuExtIrq = NvRmGetIrqForLogicalInterrupt( hRmDevice, NVRM_MODULE_ID(NvRmPrivModuleID_PmuExt, 0), 0); NV_CHECK_ERROR_CLEANUP(NvRmInterruptRegister(hRmDevice, 1, &PmuExtIrq, &hPmuIsr, &s_Pmu, &s_Pmu.hInterrupt, NV_FALSE)); } } if(!NvOdmPmuDeviceOpen(&s_Pmu.hOdmPmu)) { e = NvError_NotInitialized; goto fail; } NV_CHECK_ERROR_CLEANUP(NvOsThreadCreate(PmuThread, &s_Pmu, &s_Pmu.hThread)); NvRmPrivIoPowerControlInit(hRmDevice); NvRmPrivCoreVoltageInit(hRmDevice); } return NvSuccess; fail: NvRmPrivPmuDeinit(hRmDevice); return e; }
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; }
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; }
NvError NvRmPrivPowerInit(NvRmDeviceHandle hRmDeviceHandle) { NvU32 i; NvError e; NV_ASSERT(hRmDeviceHandle); // Initialize registry s_PowerRegistry.pPowerClients = NULL; s_PowerRegistry.AvailableEntries = 0; s_PowerRegistry.UsedIndexRange = 0; // Clear busy head pointers as well as starvation and power plane // reference counts. Aalthough power plane references are cleared // here, the combined power state is not updated - it will kept as // set by the boot code, until the 1st client requests power. NvOsMemset(s_BusyReqHeads, 0, sizeof(s_BusyReqHeads)); NvOsMemset(s_StarveOnRefCounts, 0, sizeof(s_StarveOnRefCounts)); NvOsMemset(s_PowerOnRefCounts, 0, sizeof(s_PowerOnRefCounts)); // Initialize busy requests pool NvOsMemset(s_BusyReqPool, 0, sizeof(s_BusyReqPool)); for (i = 0; i < NVRM_BUSYREQ_POOL_SIZE; i++) s_pFreeBusyReqPool[i] = &s_BusyReqPool[i]; s_FreeBusyReqPoolSize = NVRM_BUSYREQ_POOL_SIZE; // Create the RM registry mutex and initialize RM/OAL interface s_hPowerClientMutex = NULL; NV_CHECK_ERROR_CLEANUP(NvOsMutexCreate(&s_hPowerClientMutex)); NV_CHECK_ERROR_CLEANUP(NvRmPrivOalIntfInit(hRmDeviceHandle)); // Initialize power group control, and power gate SoC partitions NvRmPrivPowerGroupControlInit(hRmDeviceHandle); return NvSuccess; fail: NvRmPrivOalIntfDeinit(hRmDeviceHandle); NvOsMutexDestroy(s_hPowerClientMutex); s_hPowerClientMutex = NULL; return e; }
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; }
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; }
/* platform driver interface */ static int __devinit tegra_otg_probe(struct platform_device *pdev) { int err = 0; struct resource *res; int instance = pdev->id; NvError e; struct tegra_otg_platform_data *pdata; sg_tegra_otg = kzalloc(sizeof(struct tegra_otg_data), GFP_KERNEL); if (!sg_tegra_otg) return -ENOMEM; spin_lock_init(&sg_tegra_otg->lock); platform_set_drvdata(pdev, sg_tegra_otg); NV_CHECK_ERROR_CLEANUP( NvDdkUsbPhyOpen(s_hRmGlobal, instance, &sg_tegra_otg->usb_phy) ); NV_CHECK_ERROR_CLEANUP( NvDdkUsbPhyPowerUp(sg_tegra_otg->usb_phy, NV_FALSE, 0) ); sg_tegra_otg->instance = pdev->id; sg_tegra_otg->dev = &pdev->dev; sg_tegra_otg->otg.label = driver_name; sg_tegra_otg->otg.state = OTG_STATE_UNDEFINED; sg_tegra_otg->otg.set_peripheral = tegra_otg_set_peripheral; sg_tegra_otg->otg.set_host = tegra_otg_set_host; sg_tegra_otg->otg.set_power = tegra_otg_set_power; sg_tegra_otg->otg.set_suspend = tegra_otg_set_suspend; pdata = sg_tegra_otg->dev->platform_data; if(pdata->usb_property->IdPinDetectionType == NvOdmUsbIdPinType_CableId) sg_tegra_otg->id_connected = 1; if(pdata->usb_property->UseInternalPhyWakeup) sg_tegra_otg->vbus_connected = 1; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { err = -ENXIO; goto err_irq; } sg_tegra_otg->regs = ioremap(res->start, resource_size(res)); if (!sg_tegra_otg->regs) { err = -ENOMEM; goto err_irq; } sg_tegra_otg->irq = platform_get_irq(pdev, 0); if (!sg_tegra_otg->irq) { err = -ENODEV; goto err_irq; } err = request_irq(sg_tegra_otg->irq, tegra_otg_irq, IRQF_SHARED, driver_name, pdev); if (err) { printk("cannot request irq %d err %d\n", sg_tegra_otg->irq, err); goto err_mem_map; } /* only active when a gadget is registered */ err = otg_set_transceiver(&sg_tegra_otg->otg); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); goto err_otg; } NvDdkUsbPhyPowerDown(sg_tegra_otg->usb_phy, NV_FALSE, 0); return 0; err_otg: free_irq(sg_tegra_otg->irq, &pdev->dev); err_mem_map: iounmap(sg_tegra_otg->regs); err_irq: NvDdkUsbPhyClose(sg_tegra_otg->usb_phy); fail: platform_set_drvdata(pdev, NULL); kfree(sg_tegra_otg); return err; }
NvError NvDdkUsbPhyPowerDown( NvDdkUsbPhyHandle hUsbPhy, NvBool IsHostMode, NvBool IsDpd) { NvError e = NvSuccess; NvDdkUsbPhyIoctl_VBusStatusOutputArgs VBusStatus; NvU32 TimeOut = USB_PHY_HW_TIMEOUT_US; NV_ASSERT(hUsbPhy); NvOsMutexLock(hUsbPhy->ThreadSafetyMutex); if (!hUsbPhy->IsPhyPoweredUp) { NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; } /* Allow saving register context for the USB host if it is a ULPI interface or if the lowest power state is LP1 */ if (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) { hUsbPhy->SaveContext(hUsbPhy); } /* Turn on/off the vbus for host mode */ hUsbPhy->IsHostMode = IsHostMode; if (IsHostMode) { UsbPrivEnableVbus(hUsbPhy, NV_FALSE); /* Wait till Vbus is turned off */ do { NvOsWaitUS(1000); TimeOut -= 1000; e = hUsbPhy->Ioctl(hUsbPhy, NvDdkUsbPhyIoctlType_VBusStatus, NULL, &VBusStatus); } while (VBusStatus.VBusDetected && TimeOut); } // Power down the USB Phy NV_CHECK_ERROR_CLEANUP(hUsbPhy->PowerDown(hUsbPhy)); // On AP20 H-CLK should not be turned off // This is required to detect the sensor interrupts. // However, phy can be programmed to put in the low power mode if (!hUsbPhy->Caps.PhyRegInController) { // Disable the clock NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NV_FALSE)); } // Disable power NV_CHECK_ERROR_CLEANUP( NvRmPowerVoltageControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NvRmVoltsOff, NvRmVoltsOff, NULL, 0, NULL)); /* Turn off the USB busy hints */ UsbPhyDfsBusyHint(hUsbPhy, NV_FALSE, NV_WAIT_INFINITE); if (hUsbPhy->TurnOffPowerRail) { NvOdmEnableUsbPhyPowerRail(NV_FALSE); NvOdmEnableOtgCircuitry(NV_FALSE); } hUsbPhy->IsPhyPoweredUp = NV_FALSE; fail: NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; }
NvError NvDdkUsbPhyPowerUp( NvDdkUsbPhyHandle hUsbPhy, NvBool IsHostMode, NvBool IsDpd) { NvError e = NvSuccess; NV_ASSERT(hUsbPhy); NvOsMutexLock(hUsbPhy->ThreadSafetyMutex); if (hUsbPhy->IsPhyPoweredUp) { NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; } if (hUsbPhy->TurnOffPowerRail) { NvOdmEnableUsbPhyPowerRail(NV_TRUE); } // Enable power for USB module NV_CHECK_ERROR_CLEANUP( NvRmPowerVoltageControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NvRmVoltsUnspecified, NvRmVoltsUnspecified, NULL, 0, NULL)); // On Ap20 We will not turn off the H-Clk so not required to turn on if (!hUsbPhy->Caps.PhyRegInController) { NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NV_TRUE)); } // Power up the Phy NV_CHECK_ERROR_CLEANUP(hUsbPhy->PowerUp(hUsbPhy)); /* Allow restoring register context for the USB host if it is a ULPI interface or if the lowest power state is LP1 */ if (hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) { hUsbPhy->RestoreContext(hUsbPhy); } hUsbPhy->IsHostMode = IsHostMode; if (IsHostMode) { UsbPrivEnableVbus(hUsbPhy, NV_TRUE); } else { /* Turn on the USB busy hints */ UsbPhyDfsBusyHint(hUsbPhy, NV_TRUE, NV_WAIT_INFINITE); } hUsbPhy->IsPhyPoweredUp = NV_TRUE; fail: NvOsMutexUnlock(hUsbPhy->ThreadSafetyMutex); return e; }
NvError NvDdkUsbPhyOpen( NvRmDeviceHandle hRm, NvU32 Instance, NvDdkUsbPhyHandle *hUsbPhy) { NvError e; NvU32 MaxInstances = 0; NvDdkUsbPhy *pUsbPhy = NULL; NvOsMutexHandle UsbPhyMutex = NULL; NvRmModuleInfo info[MAX_USB_INSTANCES]; NvU32 j; NV_ASSERT(hRm); NV_ASSERT(hUsbPhy); NV_ASSERT(Instance < MAX_USB_INSTANCES); NV_CHECK_ERROR(NvRmModuleGetModuleInfo( hRm, NvRmModuleID_Usb2Otg, &MaxInstances, NULL )); if (MaxInstances > MAX_USB_INSTANCES) { // Ceil "instances" to MAX_USB_INSTANCES MaxInstances = MAX_USB_INSTANCES; } NV_CHECK_ERROR(NvRmModuleGetModuleInfo( hRm, NvRmModuleID_Usb2Otg, &MaxInstances, info )); for (j = 0; j < MaxInstances; j++) { // Check whether the requested instance is present if(info[j].Instance == Instance) break; } // No match found return if (j == MaxInstances) { return NvError_ModuleNotPresent; } if (!s_UsbPhyMutex) { e = NvOsMutexCreate(&UsbPhyMutex); if (e!=NvSuccess) return e; if (NvOsAtomicCompareExchange32( (NvS32*)&s_UsbPhyMutex, 0, (NvS32)UsbPhyMutex)!=0) { NvOsMutexDestroy(UsbPhyMutex); } } NvOsMutexLock(s_UsbPhyMutex); if (!s_pUsbPhy) { s_pUsbPhy = NvOsAlloc(MaxInstances * sizeof(NvDdkUsbPhy)); if (s_pUsbPhy) NvOsMemset(s_pUsbPhy, 0, MaxInstances * sizeof(NvDdkUsbPhy)); } NvOsMutexUnlock(s_UsbPhyMutex); if (!s_pUsbPhy) return NvError_InsufficientMemory; NvOsMutexLock(s_UsbPhyMutex); if (!s_pUtmiPadConfig) { s_pUtmiPadConfig = NvOsAlloc(sizeof(NvDdkUsbPhyUtmiPadConfig)); if (s_pUtmiPadConfig) { NvRmPhysAddr PhyAddr; NvOsMemset(s_pUtmiPadConfig, 0, sizeof(NvDdkUsbPhyUtmiPadConfig)); NvRmModuleGetBaseAddress( hRm, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, 0), &PhyAddr, &s_pUtmiPadConfig->BankSize); NV_CHECK_ERROR_CLEANUP( NvRmPhysicalMemMap( PhyAddr, s_pUtmiPadConfig->BankSize, NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, (void **)&s_pUtmiPadConfig->pVirAdr)); } } NvOsMutexUnlock(s_UsbPhyMutex); if (!s_pUtmiPadConfig) return NvError_InsufficientMemory; pUsbPhy = &s_pUsbPhy[Instance]; NvOsMutexLock(s_UsbPhyMutex); if (!pUsbPhy->RefCount) { NvRmPhysAddr PhysAddr; NvOsMutexHandle ThreadSafetyMutex = NULL; NvOsMemset(pUsbPhy, 0, sizeof(NvDdkUsbPhy)); pUsbPhy->Instance = Instance; pUsbPhy->hRmDevice = hRm; pUsbPhy->RefCount = 1; pUsbPhy->IsPhyPoweredUp = NV_FALSE; pUsbPhy->pUtmiPadConfig = s_pUtmiPadConfig; pUsbPhy->pProperty = NvOdmQueryGetUsbProperty( NvOdmIoModule_Usb, pUsbPhy->Instance); pUsbPhy->TurnOffPowerRail = UsbPhyTurnOffPowerRail(MaxInstances); NV_CHECK_ERROR_CLEANUP(NvOsMutexCreate(&ThreadSafetyMutex)); if (NvOsAtomicCompareExchange32( (NvS32*)&pUsbPhy->ThreadSafetyMutex, 0, (NvS32)ThreadSafetyMutex)!=0) { NvOsMutexDestroy(ThreadSafetyMutex); } NvRmModuleGetBaseAddress( pUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, pUsbPhy->Instance), &PhysAddr, &pUsbPhy->UsbBankSize); NV_CHECK_ERROR_CLEANUP( NvRmPhysicalMemMap( PhysAddr, pUsbPhy->UsbBankSize, NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, (void **)&pUsbPhy->UsbVirAdr)); NvRmModuleGetBaseAddress( pUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Misc, 0), &PhysAddr, &pUsbPhy->MiscBankSize); NV_CHECK_ERROR_CLEANUP( NvRmPhysicalMemMap( PhysAddr, pUsbPhy->MiscBankSize, NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, (void **)&pUsbPhy->MiscVirAdr)); if ( ( pUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiNullPhy) || ( pUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiExternalPhy)) { if (NvRmSetModuleTristate( pUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, pUsbPhy->Instance), NV_FALSE) != NvSuccess ) return NvError_NotSupported; } // Register with Power Manager NV_CHECK_ERROR_CLEANUP( NvOsSemaphoreCreate(&pUsbPhy->hPwrEventSem, 0)); pUsbPhy->RmPowerClientId = NVRM_POWER_CLIENT_TAG('U','S','B','p'); NV_CHECK_ERROR_CLEANUP( NvRmPowerRegister(pUsbPhy->hRmDevice, pUsbPhy->hPwrEventSem, &pUsbPhy->RmPowerClientId)); // Open the H/W interface UsbPhyOpenHwInterface(pUsbPhy); // Initialize the USB Phy NV_CHECK_ERROR_CLEANUP(UsbPhyInitialize(pUsbPhy)); } else { pUsbPhy->RefCount++; } *hUsbPhy = pUsbPhy; NvOsMutexUnlock(s_UsbPhyMutex); return NvSuccess; fail: NvDdkUsbPhyClose(pUsbPhy); NvOsMutexUnlock(s_UsbPhyMutex); return e; }
static NvError UsbPhyInitialize( NvDdkUsbPhyHandle hUsbPhy) { NvError e = NvSuccess; NvRmFreqKHz CurrentFreq = 0; NvRmFreqKHz PrefFreqList[3] = {12000, 60000, NvRmFreqUnspecified}; // NvOsDebugPrintf("UsbPhyInitialize::VOLTAGE ON, instance %d\n", // hUsbPhy->Instance); // request power NV_CHECK_ERROR_CLEANUP( NvRmPowerVoltageControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NvRmVoltsUnspecified, NvRmVoltsUnspecified, NULL, 0, NULL)); // Enable clock to the USB controller and Phy NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NV_TRUE)); if (!hUsbPhy->Caps.PhyRegInController) { if (hUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiNullPhy) { /* Request for 60MHz clk */ NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, PrefFreqList[1], PrefFreqList[1], &PrefFreqList[1], 1, &CurrentFreq, 0)); } else { /* Request for 12 MHz clk */ NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, PrefFreqList[0], PrefFreqList[0], &PrefFreqList[0], 1, &CurrentFreq, 0)); } } // else { /* No need for actual clock configuration - all USB PLL frequencies are available concurrently in this case. */ } // Reset controller NvRmModuleReset(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance)); // On AP20 H-CLK should not be turned off // This is required to detect the sensor interrupts. // However, phy can be programmed to put in the low power mode if (!hUsbPhy->Caps.PhyRegInController) { // Disable the clock NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NV_FALSE)); } // Disable power NV_CHECK_ERROR_CLEANUP( NvRmPowerVoltageControl(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, NvRmVoltsOff, NvRmVoltsOff, NULL, 0, NULL)); // if we are not turning off the power rail during power up and down // then turn on only once during the initalization. if (!hUsbPhy->TurnOffPowerRail) { NvOdmEnableUsbPhyPowerRail(NV_TRUE); } fail: return e; }
static NvError UsbPhyDfsBusyHint( NvDdkUsbPhyHandle hUsbPhy, NvBool DfsOn, NvU32 BoostDurationMs) { NvRmDfsBusyHint pUsbHintOn[] = { { NvRmDfsClockId_Emc, NV_WAIT_INFINITE, USB_HW_MIN_SYSTEM_FREQ_KH, NV_TRUE }, { NvRmDfsClockId_Ahb, NV_WAIT_INFINITE, USB_HW_MIN_SYSTEM_FREQ_KH, NV_TRUE }, { NvRmDfsClockId_Cpu, NV_WAIT_INFINITE, USB_HW_MIN_CPU_FREQ_KH, NV_TRUE } }; NvRmDfsBusyHint pUsbHintOff[] = { { NvRmDfsClockId_Emc, 0, 0, NV_TRUE }, { NvRmDfsClockId_Ahb, 0, 0, NV_TRUE }, { NvRmDfsClockId_Cpu, 0, 0, NV_TRUE } }; NvError e = NvSuccess; NvU32 NumHints; if (hUsbPhy->IsHostMode) { // Do not enable busy hints for cpu clock in host mode NumHints = NV_ARRAY_SIZE(pUsbHintOn) - 1; } else { NumHints = NV_ARRAY_SIZE(pUsbHintOn); } pUsbHintOn[0].BoostDurationMs = BoostDurationMs; pUsbHintOn[1].BoostDurationMs = BoostDurationMs; pUsbHintOn[2].BoostDurationMs = BoostDurationMs; if (DfsOn) { if (hUsbPhy->Caps.PhyRegInController) { // Indicate USB controller is active NvRmFreqKHz PrefFreq = NvRmPowerModuleGetMaxFrequency( hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance)); NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, PrefFreq, PrefFreq, &PrefFreq, 1, NULL, 0)); } return NvRmPowerBusyHintMulti(hUsbPhy->hRmDevice, hUsbPhy->RmPowerClientId, pUsbHintOn, NumHints, NvRmDfsBusyHintSyncMode_Async); } else { if (hUsbPhy->Caps.PhyRegInController) { // Indicate USB controller is idle NvRmFreqKHz PrefFreq = USBC_IDLE_KHZ; NV_CHECK_ERROR_CLEANUP( NvRmPowerModuleClockConfig(hUsbPhy->hRmDevice, NVRM_MODULE_ID(NvRmModuleID_Usb2Otg, hUsbPhy->Instance), hUsbPhy->RmPowerClientId, PrefFreq, PrefFreq, &PrefFreq, 1, NULL, 0)); } return NvRmPowerBusyHintMulti(hUsbPhy->hRmDevice, hUsbPhy->RmPowerClientId, pUsbHintOff, NumHints, NvRmDfsBusyHintSyncMode_Async); } fail: return e; }