[kernel] rk3288_mali_t760_driver_r6p0-02rel0_13_x@0

(cherry picked from commit 1075a28e369596f902a832a5e4fe2ce0abcccbd0)
This commit is contained in:
Firefly
2015-08-12 17:54:35 +08:00
committed by djw
parent 9e87c01241
commit 54c3b851a3
8 changed files with 756 additions and 167 deletions

View File

@ -162,7 +162,8 @@ enum {
/*
* Default period for DVFS sampling
*/
#define DEFAULT_PM_DVFS_PERIOD 100 /* 100ms */
// #define DEFAULT_PM_DVFS_PERIOD 100 /* 100ms */
#define DEFAULT_PM_DVFS_PERIOD 20 /* 20 ms */
/*
* Power Management poweroff tick granuality. This is in nanoseconds to

View File

@ -104,7 +104,7 @@ EXPORT_SYMBOL(shared_kernel_test_data);
#define KBASE_DRV_NAME "mali"
/** rk_ext : version of rk_ext on mali_ko, aka. rk_ko_ver. */
#define ROCKCHIP_VERSION (12)
#define ROCKCHIP_VERSION (13)
static const char kbase_drv_name[] = KBASE_DRV_NAME;

View File

@ -13,7 +13,12 @@
*
*/
/**
* @file mali_kbase_config_platform.h
* 声明 platform_config_of_rk (platform_rk 的 platform_config).
*
* 参见 文档 'mali_midgard_ddk_r6p0_integration_manual_DIT0023P_en' 中的 3.4.1.
*/
/**
* Maximum frequency

View File

@ -28,6 +28,16 @@
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <linux/reboot.h>
/**
* @file mali_kbase_config_rk.c
* 对 platform_config_of_rk 的具体实现.
*
* mali_device_driver 包含两部分 :
* .DP : platform_dependent_part_in_mdd : 依赖 platform 部分, 源码在 <mdd_src_dir>/platform/<platform_name> 目录下.
* 在 mali_device_driver 内部, 记为 platform_dependent_part.
* .DP : common_parts_in_mdd : arm 实现的通用的部分, 源码在 <mdd_src_dir> 目录下.
* 在 mali_device_driver 内部, 记为 common_parts.
*/
int get_cpu_clock_speed(u32 *cpu_clock);
@ -155,6 +165,7 @@ int kbase_platform_rk_init(struct kbase_device *kbdev)
E("fail to register pm_notifier.");
return -1;
}
pr_info("%s,register_reboot_notifier\n",__func__);
register_reboot_notifier(&mali_reboot_notifier);
return 0;
@ -191,19 +202,30 @@ static int pm_callback_power_on(struct kbase_device *kbdev)
struct rk_context *platform;
platform = (struct rk_context *)kbdev->platform_context;
/* 若 mali_device 是 suspended 的, 则... */
if (pm_runtime_status_suspended(dev))
{
/* 预置返回 1, 表征 gpu_state 可能已经 lost 了. */
ret_val = 1;
}
else
{
ret_val = 0;
}
if(dev->power.disable_depth > 0) {
if(platform->cmu_pmu_status == 0)
{
/* 使能 gpu_power_domain 和 clk_of_gpu_dvfs_node. */
kbase_platform_cmu_pmu_control(kbdev, 1);
}
return ret_val;
}
result = pm_runtime_resume(dev);
if (result < 0 && result == -EAGAIN)
// if (result < 0 && result == -EAGAIN)
if ( -EAGAIN == result )
kbase_platform_cmu_pmu_control(kbdev, 1);
else if (result < 0)
printk(KERN_ERR "pm_runtime_get_sync failed (%d)\n", result);
@ -220,6 +242,7 @@ static void pm_callback_power_off(struct kbase_device *kbdev)
int kbase_device_runtime_init(struct kbase_device *kbdev)
{
pm_suspend_ignore_children(kbdev->dev, true);
/* 对 mali_device 使能 runtime_pm. */
pm_runtime_enable(kbdev->dev);
#ifdef CONFIG_MALI_MIDGARD_DEBUG_SYS
if (kbase_platform_create_sysfs_file(kbdev->dev))

View File

@ -1,6 +1,6 @@
/* drivers/gpu/t6xx/kbase/src/platform/manta/mali_kbase_dvfs.c
*
*
*
*
* Rockchip SoC Mali-T764 DVFS driver
*
* This program is free software; you can redistribute it and/or modify
@ -13,6 +13,9 @@
* DVFS
*/
// #define ENABLE_DEBUG_LOG
#include "custom_log.h"
#include <mali_kbase.h>
#include <mali_kbase_uku.h>
#include <mali_kbase_mem.h>
@ -51,41 +54,91 @@
/* This table and variable are using the check time share of GPU Clock */
/***********************************************************/
extern int rockchip_tsadc_get_temp(int chn);
/** gpu 温度上限. */
#define gpu_temp_limit 110
/** 经过 gpu_temp_statis_time 次测量记录之后, 对温度数据取平均. */
#define gpu_temp_statis_time 1
#define level0_min 0
#define level0_max 70
#define levelf_max 100
static u32 div_dvfs = 0 ;
/**
* .DP : mali_dvfs_level_table.
* 其中的 level_items 的 gpu_clk_freq 从低到高.
*
* 运行时初始化阶段, 将从 'mali_freq_table' 进行运行时初始化,
* 若获取 'mali_freq_table' 失败, 则使用这里的 缺省配置.
* 参见 kbase_platform_dvfs_init.
*/
static mali_dvfs_info mali_dvfs_infotbl[] = {
{925000, 100000, 0, 70, 0},
{925000, 160000, 50, 65, 0},
{1025000, 266000, 60, 78, 0},
{1075000, 350000, 65, 75, 0},
{1125000, 400000, 70, 75, 0},
{1200000, 500000, 90, 100, 0},
{925000, 100000, 0, 70, 0},
{925000, 160000, 50, 65, 0},
{1025000, 266000, 60, 78, 0},
{1075000, 350000, 65, 75, 0},
{1125000, 400000, 70, 75, 0},
{1200000, 500000, 90, 100, 0},
};
/**
* pointer_to_mali_dvfs_level_table.
*/
mali_dvfs_info *p_mali_dvfs_infotbl = NULL;
/**
* num_of_mali_dvfs_levels : mali_dvfs_level_table 中有效的 level_item 的数量.
*/
unsigned int MALI_DVFS_STEP = ARRAY_SIZE(mali_dvfs_infotbl);
/**
* mali_dvfs_level_table 中可以容纳的 level_items 的最大数量.
*/
const unsigned int MAX_NUM_OF_MALI_DVFS_LEVELS = ARRAY_SIZE(mali_dvfs_infotbl);
/**
* gpu_clk_freq_table_from_system_dvfs_module, 从 system_dvfs_module 得到的 gpu_clk 的 频点表.
* 原始的 频点配置信息在 .dts 文件中.
*/
static struct cpufreq_frequency_table *mali_freq_table = NULL;
#ifdef CONFIG_MALI_MIDGARD_DVFS
/** mali_dvfs_status_t. */
typedef struct _mali_dvfs_status_type {
struct kbase_device *kbdev;
/**
* .DP : current_dvfs_level : 当前使用的 mali_dvfs_level 在 mali_dvfs_level_table 中的 index.
* 参见 mali_dvfs_infotbl.
*/
int step;
/** 最新的 由 metrics_system 报告的 current_calculated_utilisation. */
int utilisation;
/** 最近一次完成的 temperature_record_section 记录得到的温度数据. */
u32 temperature;
/** 当前 temperature_record_section 中, 已经记录温度的次数. */
u32 temperature_time;
#ifdef CONFIG_MALI_MIDGARD_FREQ_LOCK
/**
* gpu_freq_upper_limit, 即 dvfs_level_upper_limit.
* 量纲是 index of mali_dvfs_level_table.
* 若是 -1, 则表示当前未设置 dvfs_level_upper_limit.
*/
int upper_lock;
/**
* gpu_freq_lower_limit, 即 dvfs_level_lower_limit.
* 量纲是 index of mali_dvfs_level_table.
* 若是 -1, 则表示当前未设置 dvfs_level_lower_limit.
*/
int under_lock;
#endif
} mali_dvfs_status;
static struct workqueue_struct *mali_dvfs_wq = 0;
/**
* 用来在并发环境下, 保护 mali_dvfs_status_current 等数据.
*/
spinlock_t mali_dvfs_spinlock;
struct mutex mali_set_clock_lock;
struct mutex mali_enable_clock_lock;
@ -93,12 +146,13 @@ struct mutex mali_enable_clock_lock;
#ifdef CONFIG_MALI_MIDGARD_DEBUG_SYS
static void update_time_in_state(int level);
#endif
/*dvfs status*/
/* .DP : current_mali_dvfs_status. */
static mali_dvfs_status mali_dvfs_status_current;
#define LIMIT_FPS 60
#define LIMIT_FPS_POWER_SAVE 50
/*---------------------------------------------------------------------------*/
#ifdef CONFIG_MALI_MIDGARD_DVFS
static void gpufreq_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
@ -114,6 +168,7 @@ static void gpufreq_input_event(struct input_handle *handle, unsigned int type,
platform = (struct rk_context *)dvfs_status->kbdev->platform_context;
spin_lock_irqsave(&platform->gpu_in_touch_lock, flags);
/* 有 input_event 到来, 设置对应标识. */
platform->gpu_in_touch = true;
spin_unlock_irqrestore(&platform->gpu_in_touch_lock, flags);
}
@ -121,14 +176,14 @@ static void gpufreq_input_event(struct input_handle *handle, unsigned int type,
static int gpufreq_input_connect(struct input_handler *handler,
struct input_dev *dev, const struct input_device_id *id)
{
struct input_handle *handle;
struct input_handle *handle; // 用于关联 'dev' 和 'handler'.
int error;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->dev = dev; // 'handle' 关联的 input_dev.
handle->handler = handler;
handle->name = "gpufreq";
@ -141,6 +196,7 @@ static int gpufreq_input_connect(struct input_handler *handler,
goto err1;
pr_info("%s\n",__func__);
return 0;
err1:
input_unregister_handle(handle);
err2:
@ -156,6 +212,9 @@ static void gpufreq_input_disconnect(struct input_handle *handle)
pr_info("%s\n",__func__);
}
/**
* 待处理(关联) 的 input_device_ids_table.
*/
static const struct input_device_id gpufreq_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
@ -183,16 +242,23 @@ static struct input_handler gpufreq_input_handler = {
.id_table = gpufreq_ids,
};
#endif
/*---------------------------------------------------------------------------*/
/**
* mali_dvfs_work 的实现主体, 即对 dvfs_event 的处理流程的主体函数.
*/
static void mali_dvfs_event_proc(struct work_struct *w)
{
unsigned long flags;
mali_dvfs_status *dvfs_status;
static int level_down_time = 0;
static int level_up_time = 0;
static int level_down_time = 0; // counter_of_requests_to_jump_down_in_dvfs_level_table :
// 对 mali_dvfs_level 下跳 请求 发生的次数的静态计数.
static int level_up_time = 0; // counter_of_requests_to_jump_up_in_dvfs_level_table :
// 对 mali_dvfs_level 上跳 请求发生的次数的静态计数.
static u32 temp_tmp;
struct rk_context *platform;
u32 fps=0;
u32 fps = 0; // real_fps.
u32 fps_limit;
u32 policy;
mutex_lock(&mali_enable_clock_lock);
@ -207,9 +273,9 @@ static void mali_dvfs_event_proc(struct work_struct *w)
fps = rk_get_real_fps(0);
dvfs_status->temperature_time++;
temp_tmp += rockchip_tsadc_get_temp(1);
temp_tmp += rockchip_tsadc_get_temp(1); // .Q : 获取当前温度? "1" : 意义? 指定特定的测试通道?
if(dvfs_status->temperature_time >= gpu_temp_statis_time) {
dvfs_status->temperature_time = 0;
dvfs_status->temperature = temp_tmp / gpu_temp_statis_time;
@ -226,68 +292,105 @@ static void mali_dvfs_event_proc(struct work_struct *w)
dvfs_status->step = MALI_DVFS_STEP - 1;
} else {
fps_limit = (ROCKCHIP_PM_POLICY_NORMAL == policy)?LIMIT_FPS : LIMIT_FPS_POWER_SAVE;
/*
printk("policy : %d , fps_limit = %d\n",policy,fps_limit);
*/
V("policy : %d , fps_limit = %d", policy, fps_limit);
/*give priority to temperature unless in performance mode */
if (dvfs_status->temperature > gpu_temp_limit) {
if (dvfs_status->temperature > gpu_temp_limit) // 若记录的 gpu 温度 超过了 上限, 则 ...
{
if(dvfs_status->step > 0)
dvfs_status->step--;
if(gpu_temp_statis_time > 1)
dvfs_status->temperature = 0;
/*
pr_info("decrease step for temperature over %d,next clock = %d\n",
gpu_temp_limit, mali_dvfs_infotbl[dvfs_status->step].clock);
*/
} else if ((dvfs_status->utilisation > mali_dvfs_infotbl[dvfs_status->step].max_threshold) &&
(dvfs_status->step < MALI_DVFS_STEP-1) && fps < fps_limit) {
pr_info("decrease step for temperature over %d,next clock = %d\n",
gpu_temp_limit, mali_dvfs_infotbl[dvfs_status->step].clock);
*/
V("jump down in dvfs_level_table to level '%d', for temperature over %d, next clock = %d",
dvfs_status->step,
gpu_temp_limit,
mali_dvfs_infotbl[dvfs_status->step].clock);
}
// 若 current_calculated_utilisation 要求 上调 mali_dvfs_level,
// 且 current_dvfs_level 还可能被上调,
// 且 real_fps "小于" fps_limit,
// 则 ....
else if ( (dvfs_status->utilisation > mali_dvfs_infotbl[dvfs_status->step].max_threshold)
&& (dvfs_status->step < MALI_DVFS_STEP - 1)
&& fps < fps_limit )
{
// 至此, 可认为一次请求 mali_dvfs_level 上跳 发生.
level_up_time++;
if (level_up_time == MALI_DVFS_UP_TIME_INTERVAL) {
/*
printk("up,utilisation=%d,current clock=%d,fps = %d,temperature = %d",
dvfs_status->utilisation, mali_dvfs_infotbl[dvfs_status->step].clock,
fps,dvfs_status->temperature);
*/
dvfs_status->step++;
level_up_time = 0;
/*
printk(" next clock=%d\n",mali_dvfs_infotbl[dvfs_status->step].clock);
*/
BUG_ON(dvfs_status->step >= MALI_DVFS_STEP);
}
level_down_time = 0;
} else if ((dvfs_status->step > 0) &&
(dvfs_status->utilisation < mali_dvfs_infotbl[dvfs_status->step].min_threshold)) {
level_down_time++;
if (level_down_time==MALI_DVFS_DOWN_TIME_INTERVAL) {
/*
printk("down,utilisation=%d,current clock=%d,fps = %d,temperature = %d",
/* 若 上跳请求的次数 达到 执行具体上跳 要求, 则... */
if (level_up_time == MALI_DVFS_UP_TIME_INTERVAL)
{
V("to jump up in dvfs_level_table, utilisation=%d, current clock=%d, fps = %d, temperature = %d",
dvfs_status->utilisation,
mali_dvfs_infotbl[dvfs_status->step].clock,fps,dvfs_status->temperature);
*/
mali_dvfs_infotbl[dvfs_status->step].clock,
fps,
dvfs_status->temperature);
/* 预置 current_dvfs_level 上跳. */ // 具体生效将在最后.
dvfs_status->step++;
/* 清 上跳请求计数. */
level_up_time = 0;
V(" next clock=%d.", mali_dvfs_infotbl[dvfs_status->step].clock);
BUG_ON(dvfs_status->step >= MALI_DVFS_STEP); // 数组中元素的 index 总是比 size 小.
}
/* 清 下跳请求计数. */
level_down_time = 0;
}
/* 否则, 若 current_calculated_utilisation 要求 current_dvfs_level 下跳, 且 还可以下跳, 则... */
else if ((dvfs_status->step > 0)
&& (dvfs_status->utilisation < mali_dvfs_infotbl[dvfs_status->step].min_threshold))
{
level_down_time++;
if (level_down_time==MALI_DVFS_DOWN_TIME_INTERVAL)
{
V("to jump down in dvfs_level_table ,utilisation=%d, current clock=%d, fps = %d, temperature = %d",
dvfs_status->utilisation,
mali_dvfs_infotbl[dvfs_status->step].clock,
fps,
dvfs_status->temperature);
BUG_ON(dvfs_status->step <= 0);
dvfs_status->step--;
level_down_time = 0;
/*
printk(" next clock=%d\n",mali_dvfs_infotbl[dvfs_status->step].clock);
*/
V(" next clock=%d",mali_dvfs_infotbl[dvfs_status->step].clock);
}
level_up_time = 0;
} else {
}
/* 否则, ... */
else
{
level_down_time = 0;
level_up_time = 0;
/*
printk("keep,utilisation=%d,current clock=%d,fps = %d,temperature = %d\n",
V("keep current_dvfs_level, utilisation=%d,current clock=%d,fps = %d,temperature = %d\n",
dvfs_status->utilisation,
mali_dvfs_infotbl[dvfs_status->step].clock,fps,dvfs_status->temperature);
*/
mali_dvfs_infotbl[dvfs_status->step].clock,
fps,
dvfs_status->temperature);
}
}
#ifdef CONFIG_MALI_MIDGARD_FREQ_LOCK
if ((dvfs_status->upper_lock >= 0) && (dvfs_status->step > dvfs_status->upper_lock))
// #error // 目前配置下, 本段代码有效.
// 若 指定了 dvfs_level_upper_limit,
// 且 预置的 current_dvfs_level "大于" dvfs_level_upper_limit,
// 则...
if ((dvfs_status->upper_lock >= 0)
&& (dvfs_status->step > dvfs_status->upper_lock))
{
/* 将 预置的 current_dvfs_level 调整到 dvfs_level_upper_limit. */
dvfs_status->step = dvfs_status->upper_lock;
}
if (dvfs_status->under_lock > 0) {
if (dvfs_status->step < dvfs_status->under_lock)
@ -295,15 +398,26 @@ static void mali_dvfs_event_proc(struct work_struct *w)
}
#endif
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
/* 将命令 dvfs_module 让 current_dvfs_level 具体生效. */
kbase_platform_dvfs_set_level(dvfs_status->kbdev, dvfs_status->step);
mutex_unlock(&mali_enable_clock_lock);
}
/**
* mali_dvfs_work : 处理来自 kbase_platform_dvfs_event 的 dvfs_event 的 work.
*/
static DECLARE_WORK(mali_dvfs_work, mali_dvfs_event_proc);
/* ############################################################################################# */
// callback_interface_to_common_parts_in_mdd
/**
* 由 common_parts_in_mdd 调用的, 将 dvfs_event (utilisation_report_event) 通知回调到 platform_dependent_part_in_mdd.
*/
int kbase_platform_dvfs_event(struct kbase_device *kbdev,
u32 utilisation,
u32 utilisation, // current_calculated_utilisation
u32 util_gl_share_no_use,
u32 util_cl_share_no_use[2] )
{
@ -330,13 +444,16 @@ int kbase_platform_dvfs_event(struct kbase_device *kbdev,
platform->utilisation = (100 * platform->time_busy) /
(platform->time_idle + platform->time_busy);
/* 记录 current_calculated_utilisation. */
mali_dvfs_status_current.utilisation = utilisation;
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
/* 要求在 cpu_0 上, 使用 workqueue mali_dvfs_wq, 执行 mali_dvfs_work. */
queue_work_on(0, mali_dvfs_wq, &mali_dvfs_work);
/*add error handle here */
return MALI_TRUE;
}
/* ############################################################################################# */
int kbase_platform_dvfs_get_utilisation(void)
{
@ -380,6 +497,7 @@ int kbase_platform_dvfs_enable(bool enable, int freq)
mutex_lock(&mali_enable_clock_lock);
if (enable != kbdev->pm.backend.metrics.timer_active) {
/* 若要 使能 dvfs, 则... */
if (enable) {
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
kbdev->pm.backend.metrics.timer_active = MALI_TRUE;
@ -387,7 +505,9 @@ int kbase_platform_dvfs_enable(bool enable, int freq)
hrtimer_start(&kbdev->pm.backend.metrics.timer,
HR_TIMER_DELAY_MSEC(KBASE_PM_DVFS_FREQUENCY),
HRTIMER_MODE_REL);
} else {
}
/* 否则, 即要 disable dvfs, 则 ... */
else {
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
kbdev->pm.backend.metrics.timer_active = MALI_FALSE;
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
@ -404,14 +524,18 @@ int kbase_platform_dvfs_enable(bool enable, int freq)
dvfs_status->step = kbase_platform_dvfs_get_level(freq);
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
kbase_platform_dvfs_set_level(dvfs_status->kbdev, dvfs_status->step);
}
}
mutex_unlock(&mali_enable_clock_lock);
return MALI_TRUE;
}
#define dividend 7
#define fix_float(a) ((((a)*dividend)%10)?((((a)*dividend)/10)+1):(((a)*dividend)/10))
/**
* 为 'mali_dvfs_info' 中 index 是 'level' 的 level_item, 计算 min_threshold 和 max_threshold.
*/
static bool calculate_dvfs_max_min_threshold(u32 level)
{
u32 pre_level;
@ -440,9 +564,11 @@ static bool calculate_dvfs_max_min_threshold(u32 level)
mali_dvfs_infotbl[level].min_threshold += fix_float(tmp);
}
pr_info("mali_dvfs_infotbl[%d].clock=%d,min_threshold=%d,max_threshold=%d\n",
level,mali_dvfs_infotbl[level].clock, mali_dvfs_infotbl[level].min_threshold,
mali_dvfs_infotbl[level].max_threshold);
return MALI_TRUE;
}
@ -460,26 +586,37 @@ int kbase_platform_dvfs_init(struct kbase_device *kbdev)
if (NULL == platform)
panic("oops");
D("to get gpu_clk_freq_table from system_dvfs_module.");
mali_freq_table = dvfs_get_freq_volt_table(platform->mali_clk_node);
if (mali_freq_table == NULL) {
printk("mali freq table not assigned yet,use default\n");
goto not_assigned ;
} else {
D("we got valid gpu_clk_freq_table, to init mali_dvfs_level_table with it.");
/*recalculte step*/
MALI_DVFS_STEP = 0;
for (i = 0; mali_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
for ( i = 0;
mali_freq_table[i].frequency != CPUFREQ_TABLE_END
&& i < MAX_NUM_OF_MALI_DVFS_LEVELS;
i++ )
{
mali_dvfs_infotbl[i].clock = mali_freq_table[i].frequency;
MALI_DVFS_STEP++;
}
if(MALI_DVFS_STEP > 1)
div_dvfs = round_up(((levelf_max - level0_max)/(MALI_DVFS_STEP-1)),1);
printk("MALI_DVFS_STEP=%d,div_dvfs=%d\n",MALI_DVFS_STEP,div_dvfs);
div_dvfs = round_up( ( (levelf_max - level0_max) / (MALI_DVFS_STEP - 1) ), 1);
printk("MALI_DVFS_STEP = %d, div_dvfs = %d \n",MALI_DVFS_STEP, div_dvfs);
for(i=0;i<MALI_DVFS_STEP;i++)
calculate_dvfs_max_min_threshold(i);
p_mali_dvfs_infotbl = mali_dvfs_infotbl;
}
not_assigned :
if (!mali_dvfs_wq)
mali_dvfs_wq = create_singlethread_workqueue("mali_dvfs");
@ -488,21 +625,24 @@ not_assigned :
mutex_init(&mali_set_clock_lock);
mutex_init(&mali_enable_clock_lock);
spin_lock_init(&platform->gpu_in_touch_lock);
rc = input_register_handler(&gpufreq_input_handler);
/*add a error handling here */
spin_lock_irqsave(&mali_dvfs_spinlock, flags);
mali_dvfs_status_current.kbdev = kbdev;
mali_dvfs_status_current.utilisation = 0;
mali_dvfs_status_current.step = 0;
#ifdef CONFIG_MALI_MIDGARD_FREQ_LOCK
mali_dvfs_status_current.upper_lock = -1;
mali_dvfs_status_current.upper_lock = -1; // 初始时, 未设置.
mali_dvfs_status_current.under_lock = -1;
#endif
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
spin_lock_init(&platform->gpu_in_touch_lock);
rc = input_register_handler(&gpufreq_input_handler);
if ( 0 != rc )
{
E("fail to register gpufreq_input_handler.");
}
return MALI_TRUE;
}
@ -520,29 +660,33 @@ void kbase_platform_dvfs_term(void)
int mali_get_dvfs_upper_locked_freq(void)
{
unsigned long flags;
int locked_level = -1;
int gpu_clk_freq = -1; // gpu_clk_freq_of_upper_limit
#ifdef CONFIG_MALI_MIDGARD_FREQ_LOCK
spin_lock_irqsave(&mali_dvfs_spinlock, flags);
if (mali_dvfs_status_current.upper_lock >= 0)
locked_level = mali_dvfs_infotbl[mali_dvfs_status_current.upper_lock].clock;
{
gpu_clk_freq = mali_dvfs_infotbl[mali_dvfs_status_current.upper_lock].clock;
}
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
#endif
return locked_level;
return gpu_clk_freq;
}
int mali_get_dvfs_under_locked_freq(void)
{
unsigned long flags;
int locked_level = -1;
int gpu_clk_freq = -1; // gpu_clk_freq_of_upper_limit
#ifdef CONFIG_MALI_MIDGARD_FREQ_LOCK
spin_lock_irqsave(&mali_dvfs_spinlock, flags);
if (mali_dvfs_status_current.under_lock >= 0)
locked_level = mali_dvfs_infotbl[mali_dvfs_status_current.under_lock].clock;
{
gpu_clk_freq = mali_dvfs_infotbl[mali_dvfs_status_current.under_lock].clock;
}
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
#endif
return locked_level;
return gpu_clk_freq;
}
int mali_get_dvfs_current_level(void)
@ -562,15 +706,22 @@ int mali_dvfs_freq_lock(int level)
{
unsigned long flags;
#ifdef CONFIG_MALI_MIDGARD_FREQ_LOCK
/*-----------------------------------*/
spin_lock_irqsave(&mali_dvfs_spinlock, flags);
if (mali_dvfs_status_current.under_lock >= 0 &&
mali_dvfs_status_current.under_lock > level) {
if (mali_dvfs_status_current.under_lock >= 0
&& mali_dvfs_status_current.under_lock > level)
{
printk(KERN_ERR " Upper lock Error : Attempting to set upper lock to below under lock\n");
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
return -1;
}
V("to set current dvfs_upper_lock to level '%d'.", level);
mali_dvfs_status_current.upper_lock = level;
spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
/*-----------------------------------*/
printk(KERN_DEBUG " Upper Lock Set : %d\n", level);
#endif
@ -633,12 +784,12 @@ void kbase_platform_dvfs_set_clock(struct kbase_device *kbdev, int freq)
printk("mali_clk_node not init\n");
return;
}
/* .KP : 将调用平台特定接口, 设置 gpu_clk. */
mali_dvfs_clk_set(platform->mali_clk_node,freq);
return;
}
int kbase_platform_dvfs_get_level(int freq)
{
int i;
@ -648,6 +799,7 @@ int kbase_platform_dvfs_get_level(int freq)
}
return -1;
}
void kbase_platform_dvfs_set_level(struct kbase_device *kbdev, int level)
{
static int prev_level = -1;
@ -672,8 +824,10 @@ void kbase_platform_dvfs_set_level(struct kbase_device *kbdev, int level)
mutex_lock(&mali_set_clock_lock);
#endif
/* 令 mali_dvfs_status_current 的 current_dvfs_level 的具体时钟配置生效. */
kbase_platform_dvfs_set_clock(kbdev, mali_dvfs_infotbl[level].clock);
#if defined(CONFIG_MALI_MIDGARD_DEBUG_SYS) && defined(CONFIG_MALI_MIDGARD_DVFS)
// 将实际退出 prev_level, update mali_dvfs_level_table 中 prev_level 的 total_time_in_this_level.
update_time_in_state(prev_level);
#endif
prev_level = level;
@ -684,29 +838,38 @@ void kbase_platform_dvfs_set_level(struct kbase_device *kbdev, int level)
#ifdef CONFIG_MALI_MIDGARD_DEBUG_SYS
#ifdef CONFIG_MALI_MIDGARD_DVFS
static u64 prev_time = 0;
/**
* update mali_dvfs_level_table 中当前 dvfs_level 'level' 的 total_time_in_this_level.
*/
static void update_time_in_state(int level)
{
u64 current_time;
static u64 prev_time=0;
if (level < 0)
return;
#if 0
/* 若当前 mali_dvfs "未开启", 则... */
if (!kbase_platform_dvfs_get_enable_status())
{
return;
}
#endif
if (prev_time ==0)
prev_time=get_jiffies_64();
current_time = get_jiffies_64();
mali_dvfs_infotbl[level].time += current_time-prev_time;
mali_dvfs_infotbl[level].time += current_time - prev_time;
prev_time = current_time;
}
#endif
ssize_t show_time_in_state(struct device *dev, struct device_attribute *attr,
char *buf)
ssize_t show_time_in_state(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct kbase_device *kbdev;
ssize_t ret = 0;
@ -718,12 +881,35 @@ ssize_t show_time_in_state(struct device *dev, struct device_attribute *attr,
update_time_in_state(mali_dvfs_status_current.step);
#endif
if (!kbdev)
{
return -ENODEV;
}
for (i = 0; i < MALI_DVFS_STEP; i++)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
"%d %llu\n",
mali_dvfs_infotbl[i].clock, mali_dvfs_infotbl[i].time);
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"------------------------------------------------------------------------------");
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"\n%-16s\t%-24s\t%-24s",
"index_of_level",
"gpu_clk_freq (KHz)",
"time_in_this_level (s)");
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"\n------------------------------------------------------------------------------");
for ( i = 0; i < MALI_DVFS_STEP; i++ )
{
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"\n%-16d\t%-24u\t%-24u",
i,
mali_dvfs_infotbl[i].clock / 1000,
jiffies_to_msecs(mali_dvfs_infotbl[i].time) / 1000);
}
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"\n------------------------------------------------------------------------------");
if (ret < PAGE_SIZE - 1)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
@ -741,8 +927,13 @@ ssize_t set_time_in_state(struct device *dev, struct device_attribute *attr,
{
int i;
/* reset 所有 level 的 total_time_in_this_level. */
for (i = 0; i < MALI_DVFS_STEP; i++)
{
mali_dvfs_infotbl[i].time = 0;
}
prev_time = 0;
printk(KERN_DEBUG "time_in_state value is reset complete.\n");
return count;

View File

@ -10,6 +10,10 @@
/**
* @file mali_kbase_dvfs.h
* DVFS
* 声明 平台相关的 mali_dvfs_facility 对外提供的接口, 比如初始化, 设置 gpu_clk_freq, ...
* 但这里 并没有 实现良好封装.
*
* .DP : mali_dvfs_facility : platform_dependent_part 中对 mali(gpu) DVFS 功能的具体实现.
*/
#ifndef _KBASE_DVFS_H_
@ -19,18 +23,50 @@
#define KBASE_PM_DVFS_FREQUENCY 100
#define MALI_DVFS_KEEP_STAY_CNT 10
/**
* 一个门限, 当 counter_of_requests_to_jump_up_in_dvfs_level_table 到达该 value 的时候,
* 才执行具体的将 current_dvfs_level 上跳的操作.
*/
#define MALI_DVFS_UP_TIME_INTERVAL 1
/**
* 一个门限, 当 counter_of_requests_to_jump_down_in_dvfs_level_table 到达该 value 的时候,
* 才执行具体的将 current_dvfs_level 下跳的操作.
*/
#define MALI_DVFS_DOWN_TIME_INTERVAL 2
/**
* @see kbase_platform_dvfs_enable.
*/
#define MALI_DVFS_CURRENT_FREQ 0
#if 0
#define MALI_DVFS_BL_CONFIG_FREQ 500
#define MALI_DVFS_START_FREQ 400
#endif
/**
* mali_dvfs_level_t, 某 mali_dvfs_level (功耗层级) 的具体配置信息.
*/
typedef struct _mali_dvfs_info {
/** 使用的电压. .Q : 目前实际不起作用? */
unsigned int voltage;
/**
* gpu_clock_freq. 当前 level 使用的 GPU 时钟频率. 以 KHz 为单位.
*/
unsigned int clock;
/**
* 若 current_calculated_utilisation 低于本成员, 将可能下跳到 mali_dvfs_level_table 中, 临近的低功耗 mali_dvfs_level.
*/
int min_threshold;
/**
* 若 current_calculated_utilisation 高于本成员, 将可能上跳到 mali_dvfs_level_table 中, 临近的高功耗 mali_dvfs_level.
*/
int max_threshold;
/**
* total_time_in_this_level : gpu 停留在当前 level 上的 累计时间. 以 jiffy 为单位.
*/
unsigned long long time;
} mali_dvfs_info;
@ -41,28 +77,96 @@ extern unsigned int MALI_DVFS_STEP;
#define CONFIG_MALI_MIDGARD_FREQ_LOCK
#endif
/**
* 将 gpu_clk 设置为 'freq', 'freq' 以 KHz 为单位.
*/
void kbase_platform_dvfs_set_clock(struct kbase_device *kbdev, int freq);
/**
* 命令 dvfs_module 为 gpu 配置 'level' 指定的 dvfs_level, 并具体生效.
* @param level
* 待使用的 mali_dvfs_level 在 mali_dvfs_level_table 中的 index.
*/
void kbase_platform_dvfs_set_level(struct kbase_device *kbdev, int level);
/**
* 检索 mali_dvfs_level_table, 返回其中 gpu_clock_freq 精确是 'freq' 的 level_item 的 index.
* 若没有找到, 返回 -1.
* 'freq' 以 KHz 为单位.
*/
int kbase_platform_dvfs_get_level(int freq);
#ifdef CONFIG_MALI_MIDGARD_DVFS
/**
* 初始化 mali_dvfs_facility.
*/
int kbase_platform_dvfs_init(struct kbase_device *dev);
/**
* 中止化 mali_dvfs_facility.
*/
void kbase_platform_dvfs_term(void);
/*int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation);*/
/*int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation,u32 util_gl_share, u32 util_cl_share[2]);*/
/**
* 返回当前 mali_dvfs 是否是开启, 即 common_parts 是否会回调通知 dvfs_event.
*/
int kbase_platform_dvfs_get_enable_status(void);
/**
* 使能或者禁用 dvfs, 并将 gpu_clk 设置为 'freq'(最接近的 允许的 clk).
* 若 'freq' 是 MALI_DVFS_CURRENT_FREQ, 则 "不" 改变当前的 gpu_clk_freq.
*/
int kbase_platform_dvfs_enable(bool enable, int freq);
/**
* 返回 mali(gpu) 当前(最近的) utilisation.
*/
int kbase_platform_dvfs_get_utilisation(void);
#endif
/**
* 返回 current_dvfs_level 在 mali_dvfs_level_table 中的 index.
*/
int mali_get_dvfs_current_level(void);
/**
* 返回当前 dvfs_level_upper_limit 的 gpu_clk_freq, 以 KHz 为单位.
* 若没有设置, 返回 -1.
*/
int mali_get_dvfs_upper_locked_freq(void);
/**
* 返回当前 dvfs_level_lower_limit 的 gpu_clk_freq, 以 KHz 为单位.
* 若没有设置, 返回 -1.
*/
int mali_get_dvfs_under_locked_freq(void);
/**
* 将 'level' 设置为当前的 dvfs_level_upper_limit..
* 这里用 "freq_lock" 不贴切.
* @return
* 若成功, 返回 0.
* 否则, 返回其他 value.
*/
int mali_dvfs_freq_lock(int level);
/**
* 清除当前的 dvfs_level_upper_limit 设置.
*/
void mali_dvfs_freq_unlock(void);
/**
* 将 'level' 设置为当前的 dvfs_level_lower_limit.
* @return
* 若成功, 返回 0.
* 否则, 返回其他 value.
*/
int mali_dvfs_freq_under_lock(int level);
/**
* 清除当前的 dvfs_level_lower_limit 设置.
*/
void mali_dvfs_freq_under_unlock(void);
// @see 'time_in_state' in mali_kbase_platform.c.
ssize_t show_time_in_state(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t set_time_in_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);

View File

@ -9,8 +9,10 @@
/**
* @file mali_kbase_platform.c
* Platform-dependent init.
*
* 对 mali_kbase_platform.h 声明的 pm, clk 等接口的具体实现.
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
#include <mali_kbase_uku.h>
@ -43,6 +45,7 @@
#include <linux/rockchip/dvfs.h>
// #define ENABLE_DEBUG_LOG
#include "custom_log.h"
/* ############################################################################################# */
@ -50,13 +53,24 @@
#define MALI_T7XX_DEFAULT_CLOCK 100000
/**
* clk_of_gpu_dvfs_node 的状态.
* 1, clock 被使能.
* 0, 禁止.
*/
static int mali_clk_status = 0;
/**
* gpu_power_domain 的状态.
* 1, 上电.
* 0, 掉电.
*/
static int mali_pd_status = 0;
u32 kbase_group_error = 0;
// u32 kbase_group_error = 0;
static struct kobject *rk_gpu;
int mali_dvfs_clk_set(struct dvfs_node *node,unsigned long rate)
int mali_dvfs_clk_set(struct dvfs_node *node, unsigned long rate)
{
int ret = 0;
if(!node)
@ -64,6 +78,7 @@ int mali_dvfs_clk_set(struct dvfs_node *node,unsigned long rate)
printk("clk_get_dvfs_node error \r\n");
ret = -1;
}
/* .KP : 调用 dvfs_module 设置 gpu_clk. */
ret = dvfs_clk_set_rate(node,rate * MALI_KHZ);
if(ret)
{
@ -71,6 +86,10 @@ int mali_dvfs_clk_set(struct dvfs_node *node,unsigned long rate)
}
return ret;
}
/**
* 初始化和 gpu_pm 和 gpu_clk.
*/
static int kbase_platform_power_clock_init(struct kbase_device *kbdev)
{
/*struct device *dev = kbdev->dev;*/
@ -108,9 +127,9 @@ static int kbase_platform_power_clock_init(struct kbase_device *kbdev)
dvfs_clk_prepare_enable(platform->mali_clk_node);
printk("clk enabled\n");
}
mali_dvfs_clk_set(platform->mali_clk_node,MALI_T7XX_DEFAULT_CLOCK);
mali_dvfs_clk_set(platform->mali_clk_node, MALI_T7XX_DEFAULT_CLOCK);
mali_clk_status = 1;
return 0;
out:
@ -120,6 +139,7 @@ out:
return -EPERM;
}
int kbase_platform_clock_off(struct kbase_device *kbdev)
{
struct rk_context *platform;
@ -161,6 +181,7 @@ int kbase_platform_clock_on(struct kbase_device *kbdev)
return 0;
}
int kbase_platform_is_power_on(void)
{
return mali_pd_status;
@ -212,6 +233,7 @@ int kbase_platform_power_off(struct kbase_device *kbdev)
return 0;
}
int kbase_platform_cmu_pmu_control(struct kbase_device *kbdev, int control)
{
unsigned long flags;
@ -228,33 +250,45 @@ int kbase_platform_cmu_pmu_control(struct kbase_device *kbdev, int control)
/* off */
if (control == 0)
{
/* 若已经关闭, 则... */
if (platform->cmu_pmu_status == 0)
{
spin_unlock_irqrestore(&platform->cmu_pmu_lock, flags);
return 0;
}
/* 关闭 gpu_power_domain. */
if (kbase_platform_power_off(kbdev))
{
panic("failed to turn off mali power domain\n");
}
/* 关闭 gpu_dvfs_node 的 clock. */
if (kbase_platform_clock_off(kbdev))
{
panic("failed to turn off mali clock\n");
}
platform->cmu_pmu_status = 0;
printk("turn off mali power \n");
}
else
else /* on */
{
/* on */
if (platform->cmu_pmu_status == 1)
{
spin_unlock_irqrestore(&platform->cmu_pmu_lock, flags);
return 0;
}
/* 开启 gpu_power_domain. */
if (kbase_platform_power_on(kbdev))
{
panic("failed to turn on mali power domain\n");
}
/* 使能 gpu_dvfs_node 的 clock. */
if (kbase_platform_clock_on(kbdev))
{
panic("failed to turn on mali clock\n");
}
platform->cmu_pmu_status = 1;
printk(KERN_ERR "turn on mali power\n");
@ -265,31 +299,110 @@ int kbase_platform_cmu_pmu_control(struct kbase_device *kbdev, int control)
return 0;
}
/*---------------------------------------------------------------------------*/
static ssize_t error_count_show(struct device *dev,struct device_attribute *attr, char *buf)
{
struct kbase_device *kbdev = dev_get_drvdata(dev);
ssize_t ret;
D_PTR(dev);
if ( NULL == kbdev )
{
E("fail to get kbase_device instance.");
return 0;
}
D_PTR(dev);
if ( NULL == kbdev )
{
E("fail to get kbase_device instance.");
return 0;
}
D_DEC(kbdev->kbase_group_error);
ret = scnprintf(buf, PAGE_SIZE, "%d\n", kbdev->kbase_group_error);
D_DEC(kbdev->kbase_group_error);
ret = scnprintf(buf, PAGE_SIZE, "%u\n", kbdev->kbase_group_error);
return ret;
}
static DEVICE_ATTR(error_count, S_IRUGO, error_count_show, NULL);
/*---------------------------------------------------------------------------*/
/* < 对在 sysfs_dir_of_mali_device 下的 rk_ext_file_nodes 的具体实现, >*/
// .DP : impl_of_rk_ext_file_nodes.
/**
* .doc : 对 sysfs_dir_of_mali_device 下 rk_ext_file_nodes 提供的接口的定义
*
* sysfs_dir_of_mali_device 通常是 sys/devices/ffa30000.gpu
*
* 其下有如下的 rk_ext_file_nodes :
* clock,
* 对该文件的 cat 操作, 将输出当前 gpu_clk_freq 和可能的 freq 的列表, 形如 :
* current_gpu_clk_freq : 99000 KHz
* possible_freqs : 99000, 179000, 297000, 417000, 480000 (KHz)
* 出现在 "possible_freqs" 中的有效频点, 依赖在 .dts 文件中的配置.
* 可以使用 echo 命令向本文件写入待设置的 gpu_clk_freq_in_khz, 比如 :
* echo 417000 > clock
* 注意, 这里写入的 gpu_clk_freq_in_khz "必须" 是出现在 possible_freqs 中的.
* 另外, mali_module 默认使能 dvfs,
* 所以若希望将 gpu_clk 固定在上面的特定 freq, 要关闭 dvfs 先 :
* echo off > dvfs
* fbdev,
* 只支持 cat.
* .R : 目前不确定该提供接口的用意.
* // dtlb,
* dvfs,
* cat 该节点, 将返回当前 mali_dvfs 的状态, 包括 mali_dvfs 是否开启, gpu 使用率, 当前 gpu_clk 频率.
* 若当前 mali_dvfs 被开启, 可能返回如下信息 :
* mali_dvfs is ON
* gpu_utilisation : 100
* current_gpu_clk_freq : 480 MHz
* 若当前 mali_dvfs 被关闭, 可能返回 :
* mali_dvfs is OFF
* current_gpu_clk_freq : 99 MHz
* 若一段时间没有 job 下发到 gpu, common_parts 也会自动关闭 mali_dvfs.
*
* 将字串 off 写入该节点, 将关闭 mali_dvfs,
* 且会将 gpu_clk_freq 固定到可能的最高的频率 或者 gpu_clk_freq_of_upper_limit(若有指定).
* 之后, 若将字串 on 写入该节点, 将重新开启 mali_dvfs.
*
* dvfs_upper_lock,
* cat 该节点, 返回当前 dvfs_level_upper_limit 的信息, 诸如
* upper_lock_freq : 417000 KHz
* possible upper_lock_freqs : 99000, 179000, 297000, 417000, 480000 (KHz)
* if you want to unset upper_lock_freq, to echo 'off' to this file.
*
* 对该节点写入上面 possible upper_lock_freqs 中的某个 频率, 可以将该频率设置为 gpu_clk_freq_of_upper_limit, 比如.
* echo 417000 > dvfs_upper_lock
* 若要清除之前设置的 dvfs_level_upper_limit, 写入 off 即可.
*
* dvfs_under_lock,
* cat 该节点, 返回当前 dvfs_level_lower_limit 的信息, 诸如
* under_lock_freq : 179000 KHz
* possible under_lock_freqs : 99000, 179000, 297000, 417000, 480000 (KHz)
* if you want to unset under_lock_freq, to echo 'off' to this file.
* 对该节点写入上面 possible under_lock_freq 中的某个 频率, 可以将该频率设置为 gpu_clk_freq_of_lower_limit, 比如.
* echo 179000 > dvfs_under_lock
* 若要清除之前设置的 dvfs_level_lower_limit, 写入 off 即可.
*
* time_in_state
* cat 该节点, 返回 mali_dvfs 停留在不同 level 中的时间统计, 譬如
* ------------------------------------------------------------------------------
* index_of_level gpu_clk_freq (KHz) time_in_this_level (s)
* ------------------------------------------------------------------------------
* 0 99 206
* 1 179 9
* 2 297 0
* 3 417 0
* 4 480 47
* ------------------------------------------------------------------------------
* 若通过 dvfs 节点, 开启/关闭 mali_dvfs, 则本节点输出的信息可能不准确.
*
* 若要复位上述时间统计, 可以向该节点写入任意字串, 比如 :
* echo dummy > time_in_state
*/
#ifdef CONFIG_MALI_MIDGARD_DEBUG_SYS
static ssize_t show_clock(struct device *dev, struct device_attribute *attr, char *buf)
{
struct kbase_device *kbdev;
struct rk_context *platform;
ssize_t ret = 0;
unsigned int clkrate;
unsigned int clkrate = 0; // 从 dvfs_module 获取的 gpu_clk_freq, Hz 为单位.
int i ;
kbdev = dev_get_drvdata(dev);
@ -306,16 +419,27 @@ static ssize_t show_clock(struct device *dev, struct device_attribute *attr, cha
return -ENODEV;
}
clkrate = dvfs_clk_get_rate(platform->mali_clk_node);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Current clk mali = %dMhz", clkrate / 1000000);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "current_gpu_clk_freq : %d KHz", clkrate / 1000);
/* To be revised */
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nPossible settings:");
for(i=0;i<MALI_DVFS_STEP;i++)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d ",p_mali_dvfs_infotbl[i].clock/1000);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Mhz");
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\npossible_freqs : ");
for ( i = 0; i < MALI_DVFS_STEP; i++ )
{
if ( i < (MALI_DVFS_STEP - 1) )
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d, ", p_mali_dvfs_infotbl[i].clock);
}
else
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d ", p_mali_dvfs_infotbl[i].clock);
}
}
ret += snprintf(buf + ret, PAGE_SIZE - ret, "(KHz)");
if (ret < PAGE_SIZE - 1)
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
}
else {
buf[PAGE_SIZE - 2] = '\n';
buf[PAGE_SIZE - 1] = '\0';
@ -360,6 +484,7 @@ static ssize_t set_clock(struct device *dev, struct device_attribute *attr, cons
}
#endif
freq = simple_strtoul(buf, NULL, 10);
D("freq : %u.", freq);
kbase_platform_dvfs_set_level(kbdev, kbase_platform_dvfs_get_level(freq));
return count;
@ -377,7 +502,15 @@ static ssize_t show_fbdev(struct device *dev, struct device_attribute *attr, cha
return -ENODEV;
for (i = 0; i < num_registered_fb; i++)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "fb[%d] xres=%d, yres=%d, addr=0x%lx\n", i, registered_fb[i]->var.xres, registered_fb[i]->var.yres, registered_fb[i]->fix.smem_start);
{
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"fb[%d] xres=%d, yres=%d, addr=0x%lx\n",
i,
registered_fb[i]->var.xres,
registered_fb[i]->var.yres,
registered_fb[i]->fix.smem_start);
}
if (ret < PAGE_SIZE - 1)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
@ -611,15 +744,29 @@ static ssize_t show_dvfs(struct device *dev, struct device_attribute *attr, char
if (!platform)
return -ENODEV;
/* 获取当前 gpu_dvfs_node 的 clk_freq, Hz 为单位. */
clkrate = dvfs_clk_get_rate(platform->mali_clk_node);
#ifdef CONFIG_MALI_MIDGARD_DVFS
/* 若 mali_dvfs 是 开启的, 则... */
if (kbase_platform_dvfs_get_enable_status())
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mali DVFS is on\nutilisation:%d\ncurrent clock:%dMhz", kbase_platform_dvfs_get_utilisation(),clkrate/1000000);
{
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"mali_dvfs is ON \ngpu_utilisation : %d \ncurrent_gpu_clk_freq : %u MHz",
kbase_platform_dvfs_get_utilisation(),
clkrate / 1000000);
}
/* 否则, ... */
else
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mali DVFS is off,clock:%dMhz",clkrate/1000000);
{
ret += snprintf(buf + ret,
PAGE_SIZE - ret,
"mali_dvfs is OFF \ncurrent_gpu_clk_freq : %u MHz",
clkrate / 1000000);
}
#else
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mali DVFS is disabled");
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mali_dvfs is DISABLED");
#endif
if (ret < PAGE_SIZE - 1)
@ -647,10 +794,12 @@ static ssize_t set_dvfs(struct device *dev, struct device_attribute *attr, const
platform = (struct rk_context *)kbdev->platform_context;
if (sysfs_streq("off", buf)) {
/*kbase_platform_dvfs_enable(false, MALI_DVFS_BL_CONFIG_FREQ);*/
D("to disable mali_dvfs, and set current_dvfs_level to the highest one.");
kbase_platform_dvfs_enable(false, p_mali_dvfs_infotbl[MALI_DVFS_STEP-1].clock);
platform->dvfs_enabled = false;
} else if (sysfs_streq("on", buf)) {
/*kbase_platform_dvfs_enable(true, MALI_DVFS_START_FREQ);*/
D("to disable mali_dvfs, and set current_dvfs_level to the lowest one.");
kbase_platform_dvfs_enable(true, p_mali_dvfs_infotbl[0].clock);
platform->dvfs_enabled = true;
} else {
@ -668,27 +817,47 @@ static ssize_t show_upper_lock_dvfs(struct device *dev, struct device_attribute
ssize_t ret = 0;
int i;
#ifdef CONFIG_MALI_MIDGARD_DVFS
int locked_level = -1;
int gpu_clk_freq = 0;
#endif
kbdev = dev_get_drvdata(dev);
if (!kbdev)
{
E("err.");
return -ENODEV;
}
#ifdef CONFIG_MALI_MIDGARD_DVFS
locked_level = mali_get_dvfs_upper_locked_freq();
if (locked_level > 0)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Current Upper Lock Level = %dMhz", locked_level);
gpu_clk_freq = mali_get_dvfs_upper_locked_freq();
if (gpu_clk_freq > 0)
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "upper_lock_freq : %d KHz", gpu_clk_freq);
}
else
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Unset the Upper Lock Level");
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "upper_lock_freq is NOT set");
}
/*ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nPossible settings : 400, 350,266, 160, 100, If you want to unlock : 600 or off");*/
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nPossible settings :");
for(i=0;i<MALI_DVFS_STEP;i++)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d ",p_mali_dvfs_infotbl[i].clock/1000);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Mhz");
ret += snprintf(buf + ret, PAGE_SIZE - ret, ", If you want to unlock : off");
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\npossible upper_lock_freqs : ");
for ( i = 0; i < MALI_DVFS_STEP; i++ )
{
if ( i < (MALI_DVFS_STEP - 1) )
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d, ", p_mali_dvfs_infotbl[i].clock);
}
else
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d ", p_mali_dvfs_infotbl[i].clock);
}
}
ret += snprintf(buf + ret, PAGE_SIZE - ret, "(KHz)");
if ( gpu_clk_freq > 0 )
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nif you want to unset upper_lock_freq, to echo 'off' to this file.");
}
#else
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mali DVFS is disabled. You can not set");
#endif
@ -706,16 +875,18 @@ static ssize_t show_upper_lock_dvfs(struct device *dev, struct device_attribute
static ssize_t set_upper_lock_dvfs(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct kbase_device *kbdev;
struct kbase_device *kbdev = NULL;
int i;
unsigned int freq;
unsigned int freq = 0; // 可能由 caller 传入的, 待设置的 gpu_freq_upper_limit.
int ret = 0;
kbdev = dev_get_drvdata(dev);
freq = 0;
if (!kbdev)
return -ENODEV;
freq = simple_strtoul(buf, NULL, 10);
if ( NULL == kbdev)
{
E("'kbdev' is NULL.");
return -ENODEV;
}
#ifdef CONFIG_MALI_MIDGARD_DVFS
if (sysfs_streq("off", buf))
@ -724,20 +895,34 @@ freq = simple_strtoul(buf, NULL, 10);
}
else
{
freq = simple_strtoul(buf, NULL, 10);
D_DEC(freq);
D("to search the level that matches target_freq; num_of_mali_dvfs_levels : %d.", MALI_DVFS_STEP);
for(i=0;i<MALI_DVFS_STEP;i++)
{
D("p_mali_dvfs_infotbl[%d].clock : %d", i, p_mali_dvfs_infotbl[i].clock);
if (p_mali_dvfs_infotbl[i].clock == freq)
{
mali_dvfs_freq_lock(i);
D("target_freq is acceptable in level '%d', to set '%d' as index of dvfs_level_upper_limit.", i, i);
ret = mali_dvfs_freq_lock(i);
if ( 0 != ret )
{
E("fail to set dvfs_level_upper_limit, ret : %d.", ret);
return -EINVAL;
}
break;
}
if(i==MALI_DVFS_STEP)
{
dev_err(dev, "set_clock: invalid value\n");
return -ENOENT;
}
}
/* 若 "没有" 找到和 target_freq match 的 level, 则... */
if ( MALI_DVFS_STEP == i )
{
// dev_err(dev, "set_clock: invalid value\n");
E("invalid target_freq : %d", freq);
return -ENOENT;
}
}
#else /* CONFIG_MALI_MIDGARD_DVFS */
printk(KERN_DEBUG "mali DVFS is disabled. You can not set\n");
#endif
@ -751,7 +936,7 @@ static ssize_t show_under_lock_dvfs(struct device *dev, struct device_attribute
ssize_t ret = 0;
int i;
#ifdef CONFIG_MALI_MIDGARD_DVFS
int locked_level = -1;
int gpu_clk_freq = 0;
#endif
kbdev = dev_get_drvdata(dev);
@ -760,18 +945,34 @@ static ssize_t show_under_lock_dvfs(struct device *dev, struct device_attribute
return -ENODEV;
#ifdef CONFIG_MALI_MIDGARD_DVFS
locked_level = mali_get_dvfs_under_locked_freq();
if (locked_level > 0)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Current Under Lock Level = %dMhz", locked_level);
gpu_clk_freq = mali_get_dvfs_under_locked_freq();
if (gpu_clk_freq > 0)
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "under_lock_freq : %d KHz",gpu_clk_freq);
}
else
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Unset the Under Lock Level");
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "under_lock_freq is NOT set.");
}
/*ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nPossible settings : 600, 400, 350,266, 160, If you want to unlock : 100 or off");*/
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nPossible settings :");
for(i=0;i<MALI_DVFS_STEP;i++)
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d ",p_mali_dvfs_infotbl[i].clock/1000);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "Mhz");
ret += snprintf(buf + ret, PAGE_SIZE - ret, ", If you want to unlock : off");
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\npossible under_lock_freqs : ");
for ( i = 0; i < MALI_DVFS_STEP; i++ )
{
if ( i < (MALI_DVFS_STEP - 1) )
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d, ", p_mali_dvfs_infotbl[i].clock);
}
else
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d ", p_mali_dvfs_infotbl[i].clock);
}
}
ret += snprintf(buf + ret, PAGE_SIZE - ret, "(KHz)");
if ( gpu_clk_freq > 0 )
{
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\nif you want to unset under_lock_freq, to echo 'off' to this file.");
}
#else
ret += snprintf(buf + ret, PAGE_SIZE - ret, "mali DVFS is disabled. You can not set");
#endif
@ -790,35 +991,49 @@ static ssize_t show_under_lock_dvfs(struct device *dev, struct device_attribute
static ssize_t set_under_lock_dvfs(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int i;
unsigned int freq;
struct kbase_device *kbdev;
kbdev = dev_get_drvdata(dev);
freq = 0;
unsigned int freq = 0;
struct kbase_device *kbdev = NULL;
int ret = 0;
if (!kbdev)
kbdev = dev_get_drvdata(dev);
if ( NULL == kbdev)
{
E("err.")
return -ENODEV;
}
#ifdef CONFIG_MALI_MIDGARD_DVFS
if (sysfs_streq("off", buf))
{
mali_dvfs_freq_unlock();
mali_dvfs_freq_under_unlock();
}
else
{
freq = simple_strtoul(buf, NULL, 10);
D_DEC(freq);
for(i=0;i<MALI_DVFS_STEP;i++)
{
if (p_mali_dvfs_infotbl[i].clock == freq)
{
mali_dvfs_freq_lock(i);
D("to set '%d' as the index of dvfs_level_lower_limit", i);
ret = mali_dvfs_freq_under_lock(i);
if ( 0 != ret )
{
E("fail to set dvfs_level_lower_limit, ret : %d.", ret);
return -EINVAL;
}
break;
}
if(i==MALI_DVFS_STEP)
{
dev_err(dev, "set_clock: invalid value\n");
return -ENOENT;
}
}
/* 若 "没有" 找到和 target_freq match 的 level, 则... */
if( i == MALI_DVFS_STEP )
{
dev_err(dev, "set_clock: invalid value\n");
return -ENOENT;
}
}
#else /* CONFIG_MALI_MIDGARD_DVFS */
printk(KERN_DEBUG "mali DVFS is disabled. You can not set\n");
#endif
@ -836,6 +1051,7 @@ DEVICE_ATTR(dvfs, S_IRUGO | S_IWUSR, show_dvfs, set_dvfs);
DEVICE_ATTR(dvfs_upper_lock, S_IRUGO | S_IWUSR, show_upper_lock_dvfs, set_upper_lock_dvfs);
DEVICE_ATTR(dvfs_under_lock, S_IRUGO | S_IWUSR, show_under_lock_dvfs, set_under_lock_dvfs);
DEVICE_ATTR(time_in_state, S_IRUGO | S_IWUSR, show_time_in_state, set_time_in_state);
/*---------------------------------------------------------------------------*/
int kbase_platform_create_sysfs_file(struct device *dev)
{
@ -849,12 +1065,12 @@ int kbase_platform_create_sysfs_file(struct device *dev)
goto out;
}
/* rk_ext : device will crash after "cat /sys/devices/ffa30000.gpu/dtlb".
/* rk_ext : device will crash after "cat /sys/devices/ffa30000.gpu/dtlb".
if (device_create_file(dev, &dev_attr_dtlb)) {
dev_err(dev, "Couldn't create sysfs file [dtlb]\n");
goto out;
}
*/
*/
if (device_create_file(dev, &dev_attr_dvfs)) {
dev_err(dev, "Couldn't create sysfs file [dvfs]\n");
@ -876,6 +1092,7 @@ int kbase_platform_create_sysfs_file(struct device *dev)
goto out;
}
return 0;
out:
return -ENOENT;
}
@ -902,6 +1119,7 @@ mali_error kbase_platform_init(struct kbase_device *kbdev)
if (NULL == platform)
return MALI_ERROR_OUT_OF_MEMORY;
/* .KP : 将 'rk_context' 关联到 mali_device. */
kbdev->platform_context = (void *)platform;
platform->cmu_pmu_status = 0;

View File

@ -8,18 +8,25 @@
/**
* @file mali_kbase_platform.h
* Platform-dependent init
* // Platform-dependent init
*
* 声明 platform_dependent_part 的 work_context 类型, pm, clk 等操作的接口.
*/
#ifndef _KBASE_PLATFORM_H_
#define _KBASE_PLATFORM_H_
/**
* work_context_of_platform_dependent_part_of_rk.
*/
struct rk_context {
/** Indicator if system clock to mail-t604 is active */
int cmu_pmu_status;
/** cmd & pmu lock */
spinlock_t cmu_pmu_lock;
/** gpu_power_domain. */
struct clk *mali_pd;
/** gpu_dvfs_node. */
struct dvfs_node * mali_clk_node;
#ifdef CONFIG_MALI_MIDGARD_DVFS
/*To calculate utilization for x sec */
@ -27,7 +34,9 @@ struct rk_context {
int utilisation;
u32 time_busy;
u32 time_idle;
/** mali_dvfs 是否被使能. */
bool dvfs_enabled;
/** 标识当前有 touch_input_event 到来. */
bool gpu_in_touch;
spinlock_t gpu_in_touch_lock;
#endif
@ -158,20 +167,58 @@ typedef enum
} mali_error;
int mali_dvfs_clk_set(struct dvfs_node * node,unsigned long rate);
/**
* 将 gpu_clk 设置为 'rate', 'rate' 以 KHz 为单位.
* @param node:
* 指向 gpu_dvfs_node
* @param rate
* 预期设置的 gpu_clk 的 value, KHz 为单位.
*/
int mali_dvfs_clk_set(struct dvfs_node * node,unsigned long rate); // 'rate' 以 KHz 为单位.
/* All things that are needed for the Linux port. */
/**
* 关闭/开启 gpu 的 power 和 clock.
* @param kbdev
* 指向 mali_device.
* @param control
* 若是 1, 表征要开启.
* 若是 0, 表征要关闭.
*/
int kbase_platform_cmu_pmu_control(struct kbase_device *kbdev, int control);
/**
* 在 sysfs_dir_of_mali_device 下创建 rk_ext_file_nodes.
*/
int kbase_platform_create_sysfs_file(struct device *dev);
/**
* 删除 sysfs_dir_of_mali_device 下的 rk_ext_file_nodes.
*/
void kbase_platform_remove_sysfs_file(struct device *dev);
/**
* 返回 gpu_power_domain 是否开启.
*/
int kbase_platform_is_power_on(void);
mali_error kbase_platform_init(struct kbase_device *kbdev);
void kbase_platform_term(struct kbase_device *kbdev);
/**
* 使能 clk_of_gpu_dvfs_node.
*/
int kbase_platform_clock_on(struct kbase_device *kbdev);
/**
* 禁止(关闭) clk_of_gpu_dvfs_node.
*/
int kbase_platform_clock_off(struct kbase_device *kbdev);
/**
* 开启 gpu_power_domain.
*/
int kbase_platform_power_off(struct kbase_device *kbdev);
/**
* 关闭 gpu_power_domain.
*/
int kbase_platform_power_on(struct kbase_device *kbdev);
#endif /* _KBASE_PLATFORM_H_ */