📄 dvs.c
字号:
//------------------------------------------------------------------------------
//
// Module: dvs.c
//
// Dynamic Voltage and Frequency Scaling Implementation
//
#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <oal.h>
#include <s3c6410.h>
#include <bsp_cfg.h>
#include <pmplatform.h>
//#define ENABLE_VOLTAGE_CONTROL
//#define DVS_LEVEL_PROFILE
#define DVS_UPDATE_PERIOD_FINE (100)
#define DVS_UPDATE_PERIOD_COARSE (1000)
#define DVS_DEFAULT_DELAY (100)
typedef struct
{
DWORD dwTickCount;
DWORD dwIdleCount;
DWORD dwIdleRate;
} IDLE_LOG;
//-----------------------------------------------------------------------------------
// Following System Clock configuration Level Transition Table has definition of 6 state
// and system can be changed adjacent state.
// for example,
// { SYS_L2, means that current definition is from System Level 2.
// 50, SYS_L3, means that if system idle rate reaches over 50%(idle), system level will be changed to level 3
// 10, SYS_L1, means that if system idle rate is downto 10%(hardworking), system level will be changed to level 1
// 3, 1, 8 } means current system level's(Lv 2) system clock configuration
//-----------------------------------------------------------------------------------
// Current System Level,
// Shift Down Idle Rate,
// Shift Down Level,
// Shift Up Idle Rate,
// Shift Up Level,
// ARMCLK Divider,
// HCLKx2 Divider,
// PCLK Divider
// MFCCLK Divider (MFCCLK = HCLKx2/Div)
// VddARM
// VddInternal
//-----------------------------------------------------------------------------------
DWORD g_aTransitionTable[SYS_LEVEL_MAX][11] =
{
#if (S3C6410_ACLK == CLK_400MHz)
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1000, 1000}, // 400, 100, 25, 100
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 900, 975}, // 200, 100, 25, 100
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 133, 100, 25, 100
{SYS_L3, 50, SYS_L4, 20, SYS_L1, 4, 1, 8, 2, 900, 975}, // 100, 100, 25, 100
{SYS_L4, 60, SYS_L5, 20, SYS_L1, 4, 2, 4, 1, 900, 900}, // 100, 50, 25, 100
{SYS_L5, 100, SYS_L5, 20, SYS_L1, 8, 2, 4, 1, 900, 900}, // 50, 50, 25, 100
#elif (S3C6410_ACLK == CLK_532MHz)
#if (CPU_NAME == S3C6400)
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1100, 1000}, // 532, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 900, 975}, // 266, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 177, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 1, 8, 2, 900, 975}, // 133, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 2, 4, 1, 900, 900}, // 133, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 2, 4, 1, 900, 900}, // 66, 66, 33, 133
#endif
#if (CPU_NAME == S3C6410)
#if (SYNCMODE)
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 2, 8, 2, 1200, 1200}, // 532, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 2, 8, 2, 1100, 1200}, // 266, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 4, 2, 8, 2, 1100, 1200}, // 177, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 2, 8, 2, 1100, 1200}, // 133, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 4, 4, 1, 1100, 1000}, // 133, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 4, 4, 1, 1000, 1000}, // 66, 66, 33, 133
#elif
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1100, 1000}, // 532, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 900, 975}, // 266, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 177, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 1, 8, 2, 900, 975}, // 133, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 2, 4, 1, 900, 900}, // 133, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 2, 4, 1, 900, 900}, // 66, 66, 33, 133
#endif
#endif
#elif (S3C6410_ACLK == CLK_634MHz)
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1300, 975}, // 634, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 975, 975}, // 317, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 211, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 1, 8, 2, 900, 975}, // 158, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 2, 4, 1, 900, 900}, // 158, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 2, 4, 1, 900, 900}, // 79, 66, 33, 133
#elif (S3C6410_ACLK == CLK_800MHz)
#if (CPU_NAME == S3C6410)
// to be modified
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1300, 975}, // 634, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 975, 975}, // 317, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 211, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 1, 8, 2, 900, 975}, // 158, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 2, 4, 1, 900, 900}, // 158, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 2, 4, 1, 900, 900}, // 79, 66, 33, 133
#elif (CPU_NAME == S3C6400)
#error ARMCLK_UNDEFINED_ERROR
#endif
#elif (S3C6410_ACLK == CLK_667MHz)
#if (CPU_NAME == S3C6410)
// to be modified
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1300, 975}, // 634, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 975, 975}, // 317, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 211, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 1, 8, 2, 900, 975}, // 158, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 2, 4, 1, 900, 900}, // 158, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 2, 4, 1, 900, 900}, // 79, 66, 33, 133
#elif (CPU_NAME == S3C6400)
#error ARMCLK_UNDEFINED_ERROR
#endif
#elif (S3C6410_ACLK == CLK_1066MHz)
#if (CPU_NAME == S3C6410)
// to be modified
{SYS_L0, 50, SYS_L1, 0, SYS_L0, 1, 1, 8, 2, 1300, 975}, // 634, 133, 33, 133
{SYS_L1, 40, SYS_L2, 10, SYS_L0, 2, 1, 8, 2, 975, 975}, // 317, 133, 33, 133
{SYS_L2, 40, SYS_L3, 10, SYS_L1, 3, 1, 8, 2, 900, 975}, // 211, 133, 33, 133
{SYS_L3, 50, SYS_L4, 20, SYS_L2, 4, 1, 8, 2, 900, 975}, // 158, 133, 33, 133
{SYS_L4, 60, SYS_L5, 20, SYS_L2, 4, 2, 4, 1, 900, 900}, // 158, 66, 33, 133
{SYS_L5, 100, SYS_L5, 20, SYS_L2, 8, 2, 4, 1, 900, 900}, // 79, 66, 33, 133
#elif (CPU_NAME == S3C6400)
#error ARMCLK_UNDEFINED_ERROR
#endif
#else
#error ARMCLK_UNDEFINED_ERROR
#endif
};
static DWORD g_dwCurrentIdleTime;
static BOOL g_bDVSDisabledByUSB = FALSE;
static IDLE_LOG tLastLogFine;
static IDLE_LOG tLastLogCoarse;
static SYSTEM_ACTIVE_LEVEL g_CurrentLevel = SYS_L0;
static volatile S3C6410_SYSCON_REG *g_pSysConReg = NULL;
static volatile S3C6410_GPIO_REG *g_pGPIOReg = NULL;
static volatile unsigned int *g_pOTGLinkReg = NULL;
#ifdef DVS_LEVEL_PROFILE
static DWORD g_aDVFSProfileTable[SYS_LEVEL_MAX][2];
static BOOL g_bProfileDSV = FALSE;
static DWORD g_dwLastTickCount_NonDVS = 0; // for Profile Non-DVS Idle rate
static DWORD g_dwLastIdleCount_NonDVS = 0; // for Profile Non-DVS Idle rate
#endif
// Transition Table Data field macro
#define ShiftDownRate (g_aTransitionTable[g_CurrentLevel][1])
#define ShiftDownLevel ((SYSTEM_ACTIVE_LEVEL)(g_aTransitionTable[g_CurrentLevel][2]))
#define ShiftUpRate (g_aTransitionTable[g_CurrentLevel][3])
#define ShiftUpLevel ((SYSTEM_ACTIVE_LEVEL)(g_aTransitionTable[g_CurrentLevel][4]))
#define SysARMCLKDiv(x) (g_aTransitionTable[x][5])
#define SysHCLKx2Div(x) (g_aTransitionTable[x][6])
#define SysPCLKDiv(x) (g_aTransitionTable[x][7])
#define SysMFCLKDiv(x) (g_aTransitionTable[x][8])
#define VoltageARM(x) (g_aTransitionTable[x][9])
#define VoltageInternal(x) (g_aTransitionTable[x][10])
#define SETVOLTAGE_ARM (1)
#define SETVOLTAGE_INTERNAL (2)
#define SETVOLTAGE_BOTH (SETVOLTAGE_ARM|SETVOLTAGE_INTERNAL)
void LTC3714_Init();
void LTC3714_VoltageSet(UINT32 uPwr, UINT32 uVoltage, UINT32 uDelay);
void ChangeDVSLevel(SYSTEM_ACTIVE_LEVEL eLevel);
void InitializeDVS(void)
{
g_pSysConReg = (S3C6410_SYSCON_REG *)OALPAtoVA(S3C6410_BASE_REG_PA_SYSCON, FALSE);
g_pOTGLinkReg = (unsigned int *)OALPAtoVA(S3C6410_BASE_REG_PA_USBOTG_LINK, FALSE);
g_CurrentLevel = SYS_L0; // Initial System Level
#ifdef ENABLE_VOLTAGE_CONTROL
LTC3714_Init();
// This is caused by SMDK board bug. You can erase this line if you use the other board.
LTC3714_VoltageSet(SETVOLTAGE_BOTH, VoltageARM(g_CurrentLevel), DVS_DEFAULT_DELAY);
#endif
}
void SetCurrentIdleTime(DWORD dwIdleTime)
{
g_dwCurrentIdleTime = dwIdleTime;
}
void UpdateDVS(void)
{
IDLE_LOG tCurLog;
DWORD dwCurrentMSec;
DWORD dwCurrentIdleMSec;
#ifdef DVS_LEVEL_PROFILE
DWORD dwIdleTick, dwActiveTick;
#endif
// Check USB Device in Use
if (*g_pOTGLinkReg & (0x3<<18))
{
if (g_bDVSDisabledByUSB == FALSE)
{
g_bDVSDisabledByUSB = TRUE;
ChangeDVSLevel(SYS_L0);
OALMSG(TRUE, (L"[DVS] DVS disabled by USB\r\n"));
}
return; // Do not apply DVS, when USB is in Use
}
if (g_bDVSDisabledByUSB == TRUE)
{
// Enable DVS after USB operation finished
g_bDVSDisabledByUSB = FALSE;
OALMSG(TRUE, (L"[DVS] DVS enabled\r\n"));
}
dwCurrentMSec = CurMSec;
dwCurrentIdleMSec = g_dwCurrentIdleTime;
if ( (dwCurrentMSec - tLastLogFine.dwTickCount) >= DVS_UPDATE_PERIOD_FINE )
{
tCurLog.dwTickCount = dwCurrentMSec;
tCurLog.dwIdleCount = g_dwCurrentIdleTime;
tCurLog.dwIdleRate = (100*(dwCurrentIdleMSec-tLastLogFine.dwIdleCount))/(dwCurrentMSec-tLastLogFine.dwTickCount);
#ifdef DVS_LEVEL_PROFILE
dwIdleTick = dwCurrentIdleMSec-tLastLogFine.dwIdleCount;
dwActiveTick = (dwCurrentMSec-tLastLogFine.dwTickCount)-dwIdleTick;
#endif
tLastLogFine.dwTickCount = tCurLog.dwTickCount;
tLastLogFine.dwIdleCount = tCurLog.dwIdleCount;
tLastLogFine.dwIdleRate = tCurLog.dwIdleRate;
if (tCurLog.dwIdleRate < ShiftUpRate) // Pump Up the Clock
{
#ifdef DVS_LEVEL_PROFILE
if (g_bProfileDSV)
{
g_aDVFSProfileTable[g_CurrentLevel][0] += dwIdleTick; // Idle
g_aDVFSProfileTable[g_CurrentLevel][1] += dwActiveTick; // Active
}
#endif
//OALMSG(TRUE, (L"[%d:%d]\r\n", g_CurrentLevel, tCurLog.dwIdleRate));
ChangeDVSLevel(ShiftUpLevel);
}
else
{
if ( (dwCurrentMSec - tLastLogCoarse.dwTickCount) >= DVS_UPDATE_PERIOD_COARSE )
{
tCurLog.dwIdleRate = (100*(dwCurrentIdleMSec-tLastLogCoarse.dwIdleCount))/(dwCurrentMSec-tLastLogCoarse.dwTickCount);
#ifdef DVS_LEVEL_PROFILE
dwIdleTick = dwCurrentIdleMSec-tLastLogCoarse.dwIdleCount;
dwActiveTick = (dwCurrentMSec-tLastLogCoarse.dwTickCount)-dwIdleTick;
if (g_bProfileDSV)
{
g_aDVFSProfileTable[g_CurrentLevel][0] += dwIdleTick; // Idle
g_aDVFSProfileTable[g_CurrentLevel][1] += dwActiveTick; // Active
}
#endif
tLastLogCoarse.dwTickCount = tCurLog.dwTickCount;
tLastLogCoarse.dwIdleCount = tCurLog.dwIdleCount;
tLastLogCoarse.dwIdleRate = tCurLog.dwIdleRate;
//OALMSG(TRUE, (L"[%d:%d]\r\n", g_CurrentLevel, tCurLog.dwIdleRate));
if (tCurLog.dwIdleRate > ShiftDownRate) // Pump Down the Clock
{
ChangeDVSLevel(ShiftDownLevel);
}
}
}
}
}
void ChangeDVSLevel(SYSTEM_ACTIVE_LEVEL NewLevel)
{
if (g_CurrentLevel == NewLevel)
{
// There is no need to change
return;
}
else
{
#ifdef ENABLE_VOLTAGE_CONTROL
if(g_CurrentLevel > NewLevel) // Clock Speed of New Level is Slower, Voltage of New Level is Lower
{
LTC3714_VoltageSet(SETVOLTAGE_ARM, VoltageARM(NewLevel), DVS_DEFAULT_DELAY/SysARMCLKDiv(NewLevel));
LTC3714_VoltageSet(SETVOLTAGE_INTERNAL, VoltageInternal(NewLevel), DVS_DEFAULT_DELAY/SysARMCLKDiv(NewLevel));
}
#endif
#if (CPU_NAME == S3C6410)
#if (SYNCMODE)
// In order to change clock divider values, clock source should be changed as FIN_APLL
g_pSysConReg->CLK_SRC &= ~(0x7);
g_pSysConReg->CLK_DIV0 = (g_pSysConReg->CLK_DIV0 & ~((0xf<<12)|(0xf<<8)|(0xf<<4)|(0xf)))
| ((0x1<<12)|(0x3<<8)|(0x0<<4)|(0x0)); // ARM:HCLKx2:HCLK:PCLK = 1:2:2:2
#endif
#endif
// Change System Clock Divider
g_pSysConReg->CLK_DIV0 = (g_pSysConReg->CLK_DIV0 & ~((0xf<<28)|(0xf<<12)|(0x7<<9)|(0x7)))
| ((SysMFCLKDiv(NewLevel)-1)<<28)
| ((SysPCLKDiv(NewLevel)-1)<<12)
| ((SysHCLKx2Div(NewLevel)-1)<<9)
| (SysARMCLKDiv(NewLevel)-1);
#if (CPU_NAME == S3C6410)
#if (SYNCMODE)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -