const NvRmModuleClockLimits* NvRmPrivClockLimitsInit(NvRmDeviceHandle hRmDevice) { NvU32 i; NvRmFreqKHz CpuMaxKHz, AvpMaxKHz, VdeMaxKHz, TDMaxKHz, DispMaxKHz; NvRmSKUedLimits* pSKUedLimits; const NvRmScaledClkLimits* pHwLimits; const NvRmSocShmoo* pShmoo; NV_ASSERT(hRmDevice); NvRmPrivChipFlavorInit(hRmDevice); pShmoo = s_ChipFlavor.pSocShmoo; pHwLimits = &pShmoo->ScaledLimitsList[0]; #ifndef CONFIG_FAKE_SHMOO pSKUedLimits = pShmoo->pSKUedLimits; #else /* NvRmFreqKHz CpuMaxKHz; NvRmFreqKHz AvpMaxKHz; NvRmFreqKHz VdeMaxKHz; NvRmFreqKHz McMaxKHz; NvRmFreqKHz Emc2xMaxKHz; NvRmFreqKHz TDMaxKHz; NvRmFreqKHz DisplayAPixelMaxKHz; NvRmFreqKHz DisplayBPixelMaxKHz; NvRmMilliVolts NominalCoreMv; // for common core rail NvRmMilliVolts NominalCpuMv; // for dedicated CPU rail */ pSKUedLimits = pShmoo->pSKUedLimits; // override default with configuration values // CPU clock duh! pSKUedLimits->CpuMaxKHz = MAX_CPU_OC_FREQ; #ifdef CONFIG_BOOST_PERIPHERALS // AVP clock pSKUedLimits->AvpMaxKHz = CONFIG_MAX_AVP_OC_FREQ; // 3D clock pSKUedLimits->TDMaxKHz = CONFIG_MAX_3D_OC_FREQ; #endif // CONFIG_BOOST_PERIPHERALS #endif // CONFIG_FAKE_SHMOO NvOsDebugPrintf("NVRM corner (%d, %d)\n", s_ChipFlavor.corner, s_ChipFlavor.CpuCorner); NvOsMemset((void*)s_pClockScales, 0, sizeof(s_pClockScales)); NvOsMemset(s_ClockRangeLimits, 0, sizeof(s_ClockRangeLimits)); NvOsMemset(s_VoltageStepRefCounts, 0, sizeof(s_VoltageStepRefCounts)); s_VoltageStepRefCounts[0] = NvRmPrivModuleID_Num; // all at minimum step // Combine AVP/System clock absolute limit with scaling V/F ladder upper // boundary, and set default clock range for all present modules the same // as for AVP/System clock #ifdef CONFIG_AVP_OVERCLOCK AvpMaxKHz = 266400; #else AvpMaxKHz = pSKUedLimits->AvpMaxKHz; for (i = 0; i < pShmoo->ScaledLimitsListSize; i++) { if (pHwLimits[i].HwDeviceId == NV_DEVID_AVP) { AvpMaxKHz = NV_MIN( AvpMaxKHz, pHwLimits[i].MaxKHzList[pShmoo->ShmooVmaxIndex]); break; } } #endif //CONFIG_AVP_OVERCLOCK for (i = 0; i < NvRmPrivModuleID_Num; i++) { NvRmModuleInstance *inst; if (NvRmPrivGetModuleInstance(hRmDevice, i, &inst) == NvSuccess) { s_ClockRangeLimits[i].MaxKHz = AvpMaxKHz; s_ClockRangeLimits[i].MinKHz = NVRM_BUS_MIN_KHZ; } } // Fill in limits for modules with slectable clock sources and/or dividers // as specified by the h/w table according to the h/w device ID // (CPU and AVP are not in relocation table - need translate id explicitly) // TODO: need separate subclock limits? (current implementation applies // main clock limits to all subclocks) for (i = 0; i < pShmoo->ScaledLimitsListSize; i++) { NvRmModuleID id; if (pHwLimits[i].HwDeviceId == NV_DEVID_CPU) id = NvRmModuleID_Cpu; else if (pHwLimits[i].HwDeviceId == NV_DEVID_AVP) id = NvRmModuleID_Avp; else if (pHwLimits[i].HwDeviceId == NVRM_DEVID_CLK_SRC) id = NvRmClkLimitsExtID_ClkSrc; else id = NvRmPrivDevToModuleID(pHwLimits[i].HwDeviceId); if ((id != NVRM_DEVICE_UNKNOWN) && (pHwLimits[i].SubClockId == 0)) { s_ClockRangeLimits[id].MinKHz = pHwLimits[i].MinKHz; s_ClockRangeLimits[id].MaxKHz = pHwLimits[i].MaxKHzList[pShmoo->ShmooVmaxIndex]; s_pClockScales[id] = pHwLimits[i].MaxKHzList; } } // Fill in CPU scaling data if SoC has dedicated CPU rail, and CPU clock // characterization data is separated from other modules on common core rail if (s_ChipFlavor.pCpuShmoo) { const NvRmScaledClkLimits* pCpuLimits = s_ChipFlavor.pCpuShmoo->pScaledCpuLimits; NV_ASSERT(pCpuLimits && (pCpuLimits->HwDeviceId == NV_DEVID_CPU)); s_ClockRangeLimits[NvRmModuleID_Cpu].MinKHz = pCpuLimits->MinKHz; s_ClockRangeLimits[NvRmModuleID_Cpu].MaxKHz = pCpuLimits->MaxKHzList[s_ChipFlavor.pCpuShmoo->ShmooVmaxIndex]; s_pClockScales[NvRmModuleID_Cpu] = pCpuLimits->MaxKHzList; } // Set AVP upper clock boundary with combined Absolute/Scaled limit; // Sync System clock with AVP (System is not in relocation table) s_ClockRangeLimits[NvRmModuleID_Avp].MaxKHz = AvpMaxKHz; s_ClockRangeLimits[NvRmPrivModuleID_System].MaxKHz = s_ClockRangeLimits[NvRmModuleID_Avp].MaxKHz; s_ClockRangeLimits[NvRmPrivModuleID_System].MinKHz = s_ClockRangeLimits[NvRmModuleID_Avp].MinKHz; s_pClockScales[NvRmPrivModuleID_System] = s_pClockScales[NvRmModuleID_Avp]; // Set VDE upper clock boundary with combined Absolute/Scaled limit (on // AP15/Ap16 VDE clock derived from the system bus, and VDE maximum limit // must be the same as AVP/System). VdeMaxKHz = pSKUedLimits->VdeMaxKHz; VdeMaxKHz = NV_MIN( VdeMaxKHz, s_ClockRangeLimits[NvRmModuleID_Vde].MaxKHz); if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) { NV_ASSERT(VdeMaxKHz == AvpMaxKHz); } s_ClockRangeLimits[NvRmModuleID_Vde].MaxKHz = VdeMaxKHz; // Set upper clock boundaries for devices on CPU bus (CPU, Mselect, // CMC) with combined Absolute/Scaled limits CpuMaxKHz = pSKUedLimits->CpuMaxKHz; CpuMaxKHz = NV_MIN( CpuMaxKHz, s_ClockRangeLimits[NvRmModuleID_Cpu].MaxKHz); s_ClockRangeLimits[NvRmModuleID_Cpu].MaxKHz = CpuMaxKHz; if ((hRmDevice->ChipId.Id == 0x15) || (hRmDevice->ChipId.Id == 0x16)) { s_ClockRangeLimits[NvRmModuleID_CacheMemCtrl].MaxKHz = CpuMaxKHz; s_ClockRangeLimits[NvRmPrivModuleID_Mselect].MaxKHz = CpuMaxKHz; NV_ASSERT(s_ClockRangeLimits[NvRmClkLimitsExtID_ClkSrc].MaxKHz >= CpuMaxKHz); } else if (hRmDevice->ChipId.Id == 0x20) { // No CMC; TODO: Mselect/CPU <= 1/4? s_ClockRangeLimits[NvRmPrivModuleID_Mselect].MaxKHz = CpuMaxKHz >> 2; }
NvError NvRmPowerVoltageControl( NvRmDeviceHandle hRmDeviceHandle, NvRmModuleID ModuleId, NvU32 ClientId, NvRmMilliVolts MinVolts, NvRmMilliVolts MaxVolts, const NvRmMilliVolts* PrefVoltageList, NvU32 PrefVoltageListCount, NvRmMilliVolts* pCurrentVolts) { NvError error; NvU32 PowerGroup = 0; NvBool PowerChanged = NV_FALSE; NvRmModuleInstance *pInstance = NULL; ModuleVoltageReq* pVoltageReq = NULL; NvRmPowerClient* pPowerClient = NULL; NvRmPowerRegistry* pRegistry = &s_PowerRegistry; NvU32 ClientIndex = NVRM_POWER_ID2INDEX(ClientId); /* validate the Rm Handle */ NV_ASSERT(hRmDeviceHandle); // Validate module ID and get associated Power Group if (ModuleId == NvRmPrivModuleID_System) { PowerGroup = NVRM_POWERGROUP_NPG_AUTO; } else { error = NvRmPrivGetModuleInstance(hRmDeviceHandle, ModuleId, &pInstance); if (error != NvSuccess) { NV_ASSERT(!" Voltage control: Invalid module ID. "); return NvError_ModuleNotPresent; } PowerGroup = pInstance->DevPowerGroup; NV_ASSERT(PowerGroup < NV_POWERGROUP_MAX); } NvOsMutexLock(s_hPowerClientMutex); // Check if this ID was registered; return error otherwise if (ClientIndex < pRegistry->UsedIndexRange) { pPowerClient = pRegistry->pPowerClients[ClientIndex]; } if ((pPowerClient == NULL) || (pPowerClient->id != ClientId)) { NvOsMutexUnlock(s_hPowerClientMutex); return NvError_BadValue; } // Search for the previously recorded voltage request for this module pVoltageReq = pPowerClient->pVoltageReqHead; while ((pVoltageReq != NULL) && (pVoltageReq->ModuleId != ModuleId)) { pVoltageReq = pVoltageReq->pNext; } // If it is a new voltage request record, allocate and fill it in, // otherwise just update power status. In both cases determine if // power requirements for the module have changed. if (pVoltageReq == NULL) { pVoltageReq = NvOsAlloc(sizeof(*pVoltageReq)); if (pVoltageReq == NULL) { NvOsMutexUnlock(s_hPowerClientMutex); return NvError_InsufficientMemory; } // Link at head pVoltageReq->pNext = pPowerClient->pVoltageReqHead; pPowerClient->pVoltageReqHead = pVoltageReq; pVoltageReq->ModuleId = ModuleId; pVoltageReq->PowerGroup = PowerGroup; pVoltageReq->PowerCycled = NV_FALSE; // Only new power On request counts as change PowerChanged = (MaxVolts != NvRmVoltsOff); } else { // Only changes from On to Off or vice versa counts PowerChanged = (pVoltageReq->MaxVolts != MaxVolts) && ((pVoltageReq->MaxVolts == NvRmVoltsOff) || (MaxVolts == NvRmVoltsOff)); } // Record new power request voltages pVoltageReq->MinVolts = MinVolts; pVoltageReq->MaxVolts = MaxVolts; // If module power requirements have changed, update power group reference // count, and execute the respective h/w power control procedure if (PowerChanged) { if (MaxVolts != NvRmVoltsOff) { s_PowerOnRefCounts[PowerGroup]++; if (s_PowerOnRefCounts[PowerGroup] == 1) { NvRmMilliVolts v = NvRmPrivPowerGroupGetVoltage(hRmDeviceHandle, PowerGroup); if (v == NvRmVoltsOff) { RecordPowerCycle(hRmDeviceHandle, PowerGroup); NvRmPrivPowerGroupControl(hRmDeviceHandle, PowerGroup, NV_TRUE); } } } else { NV_ASSERT(s_PowerOnRefCounts[PowerGroup] != 0); if (s_PowerOnRefCounts[PowerGroup] == 0) { NVRM_POWER_PRINTF(("Power balance failed: module %d\n", ModuleId)); } s_PowerOnRefCounts[PowerGroup]--; if (s_PowerOnRefCounts[PowerGroup] == 0) { NvRmPrivPowerGroupControl(hRmDeviceHandle, PowerGroup, NV_FALSE); } } } ReportRmPowerState(hRmDeviceHandle); // Return current voltage, unless this is the first request after module // was power cycled by RM; in the latter case return NvRmVoltsCycled value if (pCurrentVolts != NULL) { *pCurrentVolts = NvRmPrivPowerGroupGetVoltage(hRmDeviceHandle, PowerGroup); if (pVoltageReq->PowerCycled && (*pCurrentVolts != NvRmVoltsOff)) { *pCurrentVolts = NvRmVoltsCycled; } } // In any case clear power cycled indicator pVoltageReq->PowerCycled = NV_FALSE; NvOsMutexUnlock(s_hPowerClientMutex); return NvSuccess; }