初版
This commit is contained in:
@@ -0,0 +1,507 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".clock_manager.data.bss")
|
||||
#pragma data_seg(".clock_manager.data")
|
||||
#pragma const_seg(".clock_manager.text.const")
|
||||
#pragma code_seg(".clock_manager.text")
|
||||
#endif
|
||||
|
||||
#include "clock_manager.h"
|
||||
#include "system/init.h"
|
||||
#include "system/timer.h"
|
||||
#include "system/task.h"
|
||||
#include "os/os_api.h"
|
||||
#include "clock.h"
|
||||
#include "list.h"
|
||||
#include "app_config.h"
|
||||
#include "power/power_manage.h"
|
||||
|
||||
#define CLOCK_DETECT_PERIOD (4 * 1000) //4s
|
||||
#define CLOCK_DETECT_COUNTER (7) //7 times
|
||||
|
||||
#define CLOCK_MINIMUM_FREQ (24 * 1000000) //24MHz
|
||||
#define CLOCK_MAXIMUM_FREQ clk_get_max_frequency()
|
||||
|
||||
|
||||
|
||||
typedef struct clock_manager_st {
|
||||
struct list_head entry;
|
||||
u32 freq;
|
||||
u32 name;
|
||||
} clock_manager_item;
|
||||
|
||||
|
||||
static u8 ref_cnt;
|
||||
static u8 clk_adjust_step = 0;
|
||||
static u8 clk_adjust_fast = 0;
|
||||
static u16 clk_ref_timer;
|
||||
static clock_manager_item clk_locker;
|
||||
static struct list_head clk_mgr_head = LIST_HEAD_INIT(clk_mgr_head);
|
||||
|
||||
static const int clock_table[] = {
|
||||
24 * MHz, 48 * MHz, 60 * MHz, 64 * MHz, 76 * MHz,
|
||||
80 * MHz, 96 * MHz, 120 * MHz, 128 * MHz, 140 * MHz, 160 * MHz,
|
||||
200 * MHz, 240 * MHz, 280 * MHz, 320 * MHz,
|
||||
};
|
||||
|
||||
static u32 __get_clock(u32 dest_clk)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(clock_table); i++) {
|
||||
if (dest_clk <= clock_table[i]) {
|
||||
return clock_table[i];
|
||||
}
|
||||
}
|
||||
return clock_table[ARRAY_SIZE(clock_table) - 1];
|
||||
}
|
||||
|
||||
#if (defined CONFIG_EARPHONE_CASE_ENABLE)
|
||||
static u8 cpu_usage_limit[] = {75, 80, 85};
|
||||
#elif (defined CONFIG_WATCH_CASE_ENABLE)
|
||||
static u8 cpu_usage_limit[] = {75, 80, 85};
|
||||
#else
|
||||
static u8 cpu_usage_limit[] = {50, 55, 60};
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
#define CLOCK_MANAGER_INIT_CRITICAL()
|
||||
#define CLOCK_MANAGER_ENTER_CRITICAL() local_irq_disable()
|
||||
#define CLOCK_MANAGER_EXIT_CRITICAL() local_irq_enable()
|
||||
#else
|
||||
static OS_MUTEX clk_mutex;
|
||||
#define CLOCK_MANAGER_INIT_CRITICAL() os_mutex_create(&clk_mutex)
|
||||
#define CLOCK_MANAGER_ENTER_CRITICAL() os_mutex_pend(&clk_mutex, 0)
|
||||
#define CLOCK_MANAGER_EXIT_CRITICAL() os_mutex_post(&clk_mutex)
|
||||
#endif
|
||||
|
||||
extern void core_voltage_dump(); //debug info api
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_alloc,此函数会触发时钟频率设置
|
||||
*
|
||||
* @param name
|
||||
* @param clk
|
||||
*
|
||||
* @return 0:succ
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_alloc(const char *name, u32 clk)
|
||||
{
|
||||
clock_manager_item *p;
|
||||
u32 hash = JBHash((u8 *)name, strlen(name));
|
||||
|
||||
CLOCK_MANAGER_ENTER_CRITICAL();
|
||||
|
||||
list_for_each_entry(p, &clk_mgr_head, entry) {
|
||||
if (p->name == hash) {
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
clock_manager_item *it = malloc(sizeof(clock_manager_item));
|
||||
if (it == NULL) {
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
return -1;
|
||||
}
|
||||
|
||||
it->name = hash;
|
||||
it->freq = clk;
|
||||
|
||||
list_add_tail(&it->entry, &clk_mgr_head);
|
||||
|
||||
clock_refurbish();
|
||||
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_free,此函数会触发时钟频率设置
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return 0:succ
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_free(char *name)
|
||||
{
|
||||
clock_manager_item *p, *n;
|
||||
u32 hash = JBHash((u8 *)name, strlen(name));
|
||||
|
||||
CLOCK_MANAGER_ENTER_CRITICAL();
|
||||
|
||||
list_for_each_entry_safe(p, n, &clk_mgr_head, entry) {
|
||||
if (p->name == hash) {
|
||||
__list_del_entry(&p->entry);
|
||||
free(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clock_refurbish();
|
||||
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_manager_dump
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
static u32 clock_list_sum(void)
|
||||
{
|
||||
clock_manager_item *p;
|
||||
u32 total = CLOCK_MINIMUM_FREQ;
|
||||
|
||||
list_for_each_entry(p, &clk_mgr_head, entry) {
|
||||
/*printf("%s : %dHz", p->name, p->freq);*/
|
||||
total += p->freq;
|
||||
}
|
||||
/*printf("%s : %dHz", "total", total);*/
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_manager_test
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
void clock_manager_test(void)
|
||||
{
|
||||
clock_alloc("clk_mgr_1", 2 * 1000000U);
|
||||
clock_manager_dump();
|
||||
|
||||
clock_alloc("clk_mgr_2", 2 * 1000000U);
|
||||
clock_manager_dump();
|
||||
|
||||
clock_free("clk_mgr_1");
|
||||
clock_manager_dump();
|
||||
|
||||
clock_free("clk_mgr_2");
|
||||
clock_manager_dump();
|
||||
|
||||
printf("test over");
|
||||
while (1);
|
||||
}
|
||||
|
||||
|
||||
void clock_lock_dump(void)
|
||||
{
|
||||
printf("clock_lock owner : %x", clk_locker.name);
|
||||
printf("clock_lock freq : %d", clk_locker.freq);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_lock_deal
|
||||
*
|
||||
* @param name: owner name
|
||||
* @param clk: lock_freq
|
||||
* @param check: check is locked
|
||||
*
|
||||
* @return 0:succ, other:err code
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_lock_deal(const char *name, u32 clk, u8 check)
|
||||
{
|
||||
u32 hash = JBHash((u8 *)name, strlen(name));
|
||||
|
||||
CLOCK_MANAGER_ENTER_CRITICAL();
|
||||
|
||||
if (check && clk_locker.freq) {
|
||||
//has been locked, lock fail
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
return -1;
|
||||
}
|
||||
|
||||
clk_locker.freq = clk;
|
||||
clk_locker.name = hash;
|
||||
|
||||
clk_set_api("sys", clk);
|
||||
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clock_lock(const char *name, u32 clk)
|
||||
{
|
||||
return clock_lock_deal(name, clk, 1);
|
||||
}
|
||||
|
||||
int clock_force_lock(const char *name, u32 clk)
|
||||
{
|
||||
return clock_lock_deal(name, clk, 0);
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_unlock
|
||||
*
|
||||
* @param name: owner name
|
||||
*
|
||||
* @return 0:succ, other:err code
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_unlock(char *name)
|
||||
{
|
||||
u32 hash = JBHash((u8 *)name, strlen(name));
|
||||
|
||||
printf("%s", __func__);
|
||||
|
||||
CLOCK_MANAGER_ENTER_CRITICAL();
|
||||
|
||||
clock_lock_dump();
|
||||
|
||||
if (clk_locker.freq == 0) {
|
||||
//has been locked
|
||||
/* ASSERT(0, "please lock it befor unlock"); */
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (clk_locker.name != hash) {
|
||||
/* ASSERT(0, "locker owner is %s", clk_locker.name); */
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
return -2;
|
||||
}
|
||||
|
||||
clk_locker.freq = 0;
|
||||
|
||||
//refurbish clock after unlock succ
|
||||
clock_refurbish();
|
||||
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_manager_reflash
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clk_ref_cal
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
static void clk_ref_cal(void)
|
||||
{
|
||||
u32 clk_freq;
|
||||
u32 clk_decr;
|
||||
u32 pct;
|
||||
|
||||
#if 0 //效率统计
|
||||
|
||||
extern void CacheReport(void);
|
||||
CacheReport();
|
||||
|
||||
task_info_output(0);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
int usage[2] = { 0, 0 };
|
||||
int a = os_cpu_usage(NULL, usage);
|
||||
task_info_reset();
|
||||
|
||||
if (a < 0) {
|
||||
return;
|
||||
}
|
||||
int usage_max = MAX(usage[0], usage[1]);
|
||||
|
||||
int curr_clk = clk_get("sys");
|
||||
int dest_clk = curr_clk;
|
||||
__again:
|
||||
switch (clk_adjust_step) {
|
||||
case 0:
|
||||
dest_clk = __get_clock(curr_clk / 50 * usage_max);
|
||||
clk_adjust_step = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (usage_max < cpu_usage_limit[0]) {
|
||||
dest_clk = __get_clock(curr_clk / cpu_usage_limit[1] * usage_max);
|
||||
} else if (usage_max > cpu_usage_limit[2]) {
|
||||
dest_clk = __get_clock(curr_clk / (cpu_usage_limit[1] - 10) * usage_max);
|
||||
} else {
|
||||
clk_adjust_step = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (usage_max >= cpu_usage_limit[0] && usage_max <= cpu_usage_limit[2]) {
|
||||
break;
|
||||
}
|
||||
clk_adjust_step = 1;
|
||||
goto __again;
|
||||
}
|
||||
|
||||
int min_clk = clock_list_sum();
|
||||
if (dest_clk < min_clk) {
|
||||
dest_clk = min_clk;
|
||||
}
|
||||
|
||||
r_printf("cpu: %d %d clk:%d %d %d, %d\n", usage[0], usage[1],
|
||||
curr_clk, min_clk, dest_clk, clk_adjust_step);
|
||||
|
||||
//clock lock
|
||||
if (clk_locker.freq) {
|
||||
dest_clk = clk_locker.freq;
|
||||
} else if (dest_clk < min_clk) {
|
||||
dest_clk = min_clk;
|
||||
}
|
||||
|
||||
clk_set_api("sys", dest_clk);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clk_ref_fun
|
||||
*
|
||||
* @param p
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
static void clk_ref_fun(void *p)
|
||||
{
|
||||
CLOCK_MANAGER_ENTER_CRITICAL();
|
||||
if (ref_cnt < CLOCK_DETECT_COUNTER) {
|
||||
/* printf("CLOCK_DETECT_COUNTER:%d\n", ref_cnt); */
|
||||
ref_cnt++;
|
||||
clk_ref_cal();
|
||||
} else {
|
||||
#if TCFG_CFG_TOOL_ENABLE
|
||||
task_info_reset();
|
||||
/* printf("clock_reflash_reset"); */
|
||||
#else
|
||||
sys_timer_del(clk_ref_timer);
|
||||
clk_ref_timer = 0;
|
||||
/* printf("clock_reflash_stop"); */
|
||||
#endif
|
||||
}
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_refurbish,MIPS变化时调用此函数刷新。
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
void clock_refurbish(void)
|
||||
{
|
||||
if (cpu_in_irq() || cpu_irq_disabled()) {
|
||||
//以上情况,需要改为在APP任务上设置
|
||||
int msg[3];
|
||||
msg[0] = (int)clock_refurbish;
|
||||
msg[1] = 1;
|
||||
msg[2] = 0;
|
||||
os_taskq_post_type("app_core", Q_CALLBACK, 3, msg);
|
||||
/* sys_timeout_add(NULL, (void *)clock_refurbish, 1); */
|
||||
return;
|
||||
}
|
||||
|
||||
CLOCK_MANAGER_ENTER_CRITICAL();
|
||||
|
||||
//clk driver 需要提供每个芯片可以设置的最高挡位
|
||||
clk_set_api("sys", CLOCK_MAXIMUM_FREQ);
|
||||
|
||||
ref_cnt = 0;
|
||||
clk_adjust_step = 0;
|
||||
clk_adjust_fast = 0;
|
||||
|
||||
if (clk_ref_timer) {
|
||||
sys_timer_modify(clk_ref_timer, CLOCK_DETECT_PERIOD);
|
||||
} else {
|
||||
clk_ref_timer = sys_timer_add(NULL, clk_ref_fun, CLOCK_DETECT_PERIOD);
|
||||
}
|
||||
|
||||
ASSERT(clk_ref_timer);
|
||||
|
||||
task_info_reset();
|
||||
|
||||
CLOCK_MANAGER_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
_NOINLINE_
|
||||
int clk_set_unused(const char *name, int clk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 extern_clk_ref_suspend(u32 timeout)
|
||||
{
|
||||
if (clk_ref_timer && (clk_adjust_fast == 0)) {
|
||||
/* printf("\n\n enter lowerpower:%d \n\n", timeout); */
|
||||
clk_adjust_fast = 1;
|
||||
// 能进低功耗,快速收敛
|
||||
sys_timer_modify(clk_ref_timer, 500);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
REGISTER_LP_REQUEST(power_clk_ref_target) = {
|
||||
.name = "clk_ref",
|
||||
.request_enter = extern_clk_ref_suspend,
|
||||
};
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief init
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
static int clock_manager_init(void)
|
||||
{
|
||||
CLOCK_MANAGER_INIT_CRITICAL();
|
||||
return 0;
|
||||
}
|
||||
early_initcall(clock_manager_init);
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_manager_test
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
static void clk_test2_tmr_fun(void *p)
|
||||
{
|
||||
clk_set_api("sys", CLOCK_MAXIMUM_FREQ);
|
||||
|
||||
clock_refurbish();
|
||||
}
|
||||
|
||||
void clock_manager_test2(void)
|
||||
{
|
||||
sys_timer_add(NULL, clk_test2_tmr_fun, 60 * 1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void clk_test3_tmr_fun(void *p)
|
||||
{
|
||||
/* int ret; */
|
||||
clock_unlock("test");
|
||||
/* ASSERT(ret == 0); */
|
||||
}
|
||||
void clock_manager_test3(void)
|
||||
{
|
||||
int ret;
|
||||
ret = clock_lock("test", 128 * 1000000U);
|
||||
ASSERT(ret == 0);
|
||||
sys_timeout_add(NULL, clk_test3_tmr_fun, 30 * 1000);
|
||||
|
||||
/* void mpu_set(int idx, u32 begin, u32 end, u32 inv, const char *format, ...); */
|
||||
/* u32 addr = (u32)(&clk_locker.freq); */
|
||||
/* mpu_set(2, addr, addr+3, 0, "Cr"); */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef _CLOCK_MANAGER_
|
||||
#define _CLOCK_MANAGER_
|
||||
|
||||
#include "typedef.h"
|
||||
// #include "asm/clock.h"
|
||||
|
||||
|
||||
// int clk_set(const char *name, int clk);
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_alloc,此函数会触发时钟频率设置
|
||||
*
|
||||
* @param name
|
||||
* @param clk
|
||||
*
|
||||
* @return 0:succ
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_alloc(const char *name, u32 clk);
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_free,此函数会触发时钟频率设置
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return 0:succ
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_free(char *name);
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_manager_dump
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
void clock_manager_dump(void);
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_refurbish,当CPU运行代码有改变时候,建议调用此函数刷新时钟频率
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
void clock_refurbish(void);
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_lock_deal
|
||||
*
|
||||
* @param name: owner name
|
||||
* @param clk: lock_freq
|
||||
* @param check: check is locked
|
||||
*
|
||||
* @return 0:succ, other:err code
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_lock_deal(const char *name, u32 clk, u8 check);
|
||||
int clock_lock(const char *name, u32 clk); // 检查是否已经锁定
|
||||
int clock_force_lock(const char *name, u32 clk); // 不检查,强制设定时钟频率
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief clock_unlock
|
||||
*
|
||||
* @param name: owner name
|
||||
*
|
||||
* @return 0:succ, other:err code
|
||||
*/
|
||||
/* ----------------------------------------------------------------------------*/
|
||||
int clock_unlock(char *name);
|
||||
|
||||
#endif //_CLOCK_MANAGER_
|
||||
Reference in New Issue
Block a user