508 lines
12 KiB
C
508 lines
12 KiB
C
#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"); */
|
||
}
|
||
|