コード例 #1
0
ファイル: nvrm_clocks_limits.c プロジェクト: peetr/ics_kernel
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;
    }
コード例 #2
0
ファイル: nvrm_power.c プロジェクト: 0x0f/adam-kernel
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;
}