/*
* 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
#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;
*
*/
-
+/**
+ * @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
#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);
E("fail to register pm_notifier.");
return -1;
}
+
pr_info("%s,register_reboot_notifier\n",__func__);
register_reboot_notifier(&mali_reboot_notifier);
return 0;
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);
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))
/* 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
* DVFS
*/
+// #define ENABLE_DEBUG_LOG
+#include "custom_log.h"
+
#include <mali_kbase.h>
#include <mali_kbase_uku.h>
#include <mali_kbase_mem.h>
/* 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;
#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)
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);
}
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";
goto err1;
pr_info("%s\n",__func__);
return 0;
+
err1:
input_unregister_handle(handle);
err2:
pr_info("%s\n",__func__);
}
+/**
+ * 待处理(关联) 的 input_device_ids_table.
+ */
static const struct input_device_id gpufreq_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
.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);
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;
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);
- */
+
+ /* 若 上跳请求的次数 达到 执行具体上跳 要求, 则... */
+ 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);
+ /* 预置 current_dvfs_level 上跳. */ // 具体生效将在最后.
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);
+
+ 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;
- } else if ((dvfs_status->step > 0) &&
- (dvfs_status->utilisation < mali_dvfs_infotbl[dvfs_status->step].min_threshold)) {
+ }
+ /* 否则, 若 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) {
- /*
- printk("down,utilisation=%d,current clock=%d,fps = %d,temperature = %d",
+
+ 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);
- */
+ 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)
}
#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] )
{
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)
{
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;
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);
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;
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;
}
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");
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;
}
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)
{
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
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;
}
return -1;
}
+
void kbase_platform_dvfs_set_level(struct kbase_device *kbdev, int level)
{
static int prev_level = -1;
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;
#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;
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");
{
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;
/**
* @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_
#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;
#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);
/**
* @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>
#include <linux/rockchip/dvfs.h>
+// #define ENABLE_DEBUG_LOG
#include "custom_log.h"
/* ############################################################################################# */
#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)
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)
{
}
return ret;
}
+
+/**
+ * 初始化和 gpu_pm 和 gpu_clk.
+ */
static int kbase_platform_power_clock_init(struct kbase_device *kbdev)
{
/*struct device *dev = kbdev->dev;*/
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:
return -EPERM;
}
+
int kbase_platform_clock_off(struct kbase_device *kbdev)
{
struct rk_context *platform;
return 0;
}
+
int kbase_platform_is_power_on(void)
{
return mali_pd_status;
return 0;
}
+
int kbase_platform_cmu_pmu_control(struct kbase_device *kbdev, int control)
{
unsigned long flags;
/* 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");
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);
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';
}
#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;
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");
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)
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 {
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
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;
- kbdev = dev_get_drvdata(dev);
- freq = 0;
+ unsigned int freq = 0; // 可能由 caller 传入的, 待设置的 gpu_freq_upper_limit.
+ int ret = 0;
- if (!kbdev)
- return -ENODEV;
+ kbdev = dev_get_drvdata(dev);
-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))
}
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
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);
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
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
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)
{
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");
goto out;
}
return 0;
+
out:
return -ENOENT;
}
if (NULL == platform)
return MALI_ERROR_OUT_OF_MEMORY;
+ /* .KP : 将 'rk_context' 关联到 mali_device. */
kbdev->platform_context = (void *)platform;
platform->cmu_pmu_status = 0;
/**
* @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 */
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
} 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_ */