2 * Rockchip SoC Mali-450 DVFS driver
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software FoundatIon.
10 /* #define ENABLE_DEBUG_LOG */
11 #include "custom_log.h"
13 #include "mali_platform.h"
14 #include "mali_dvfs.h"
16 /*---------------------------------------------------------------------------*/
19 * 若 count_of_continuous_requests_to_jump_up_in_dvfs_level_table 达到 value,
20 * 将触发一次 current_dvfs_level jump_up.
22 #define NUM_OF_CONTINUOUS_REQUESTS_TO_TRIGGER_REAL_JUMPING_UP (1)
24 * 若 count_of_continuous_requests_to_jump_down_in_dvfs_level_table 达到 value,
25 * 将触发一次 current_dvfs_level jump_down.
27 #define NUM_OF_CONTINUOUS_REQUESTS_TO_TRIGGER_REAL_JUMPING_DOWN (30)
31 #define levelf_max 100
33 #define mali_dividend 7
34 #define mali_fix_float(a) \
35 ((((a) * mali_dividend) % 10) \
36 ? ((((a) * mali_dividend) / 10) + 1) \
37 : (((a) * mali_dividend) / 10))
39 #define work_to_dvfs(w) container_of(w, struct mali_dvfs, work)
40 #define dvfs_to_drv_data(dvfs) \
41 container_of(dvfs, struct mali_platform_drv_data, dvfs)
43 /*---------------------------------------------------------------------------*/
45 static bool mali_dvfs_should_jump_up(const struct mali_dvfs *p_dvfs)
47 return (p_dvfs->m_count_of_requests_to_jump_up
48 >= NUM_OF_CONTINUOUS_REQUESTS_TO_TRIGGER_REAL_JUMPING_UP);
51 static bool mali_dvfs_should_jump_down(const struct mali_dvfs *p_dvfs)
53 return (p_dvfs->m_count_of_requests_to_jump_down
54 >= NUM_OF_CONTINUOUS_REQUESTS_TO_TRIGGER_REAL_JUMPING_DOWN);
59 * work_to_handle_mali_utilization_event 的实现主体,
60 * 将根据 current_mali_utilization_data, 改变 current_dvfs_level.
62 static void mali_dvfs_event_proc(struct work_struct *w)
64 struct mali_dvfs *dvfs = work_to_dvfs(w);/* mali_dvfs_context. */
65 struct mali_platform_drv_data *drv_data = dvfs_to_drv_data(dvfs);
66 unsigned int utilisation = dvfs->utilisation;
68 /* index_of_current_dvfs_level. */
69 unsigned int level = dvfs->current_level;
70 /* current_dvfs_level. */
71 const struct mali_fv_info *threshold = &drv_data->fv_info[level];
72 /* utilization_in_percentage. */
73 utilisation = utilisation * 100 / 256;
75 V("mali_utilization_in_percentage : %d; index_of_current_level : %d;"
81 V("num_of_requests_to_jump_up : %d; num_of_requests_to_jump_down : %d",
82 dvfs->m_count_of_requests_to_jump_up,
83 dvfs->m_count_of_requests_to_jump_down);
85 if (utilisation > threshold->max) {
86 dvfs->m_count_of_requests_to_jump_down = 0;
88 /* 若 current_dvfs_level 还 "有" 上跳的空间, 则... */
89 if (level < (drv_data->fv_info_length - 1)) {
90 V("to count one request_to_jump_up.");
91 dvfs->m_count_of_requests_to_jump_up++;
93 /* 若足够触发一次 real_jump_up, 则... */
94 if (mali_dvfs_should_jump_up(dvfs)) {
95 V("to preset real_jump_up.");
98 dvfs->m_count_of_requests_to_jump_up = 0;
100 goto MAKE_DVFS_LEVEL_UPDATE_EFFECTIVE;
102 /* 否则, 即还 "不" 足以 real_jump_up, 则... */
106 /* 否则, 即 "没有" 上跳的空间了, 则... */
109 } else if (utilisation < (threshold->min)) {
110 dvfs->m_count_of_requests_to_jump_up = 0;
112 /* 若 current_dvfs_level 还有 下跳的空间, 则... */
114 V("to count one request_to_jump_down.");
115 dvfs->m_count_of_requests_to_jump_down++;
117 /* 若足够触发一次 real_jump_down, 则... */
118 if (mali_dvfs_should_jump_down(dvfs)) {
119 V("to preset real_jump_down.");
122 dvfs->m_count_of_requests_to_jump_down = 0;
124 goto MAKE_DVFS_LEVEL_UPDATE_EFFECTIVE;
129 /* 否则, 即 "没有" 下跳的空间了, 则... */
134 * 即 mali_utilization 在 'min' 和 'max' 之间,
135 * 即 current_dvfs_level 合适, 则 ... */
137 dvfs->m_count_of_requests_to_jump_up = 0;
138 dvfs->m_count_of_requests_to_jump_down = 0;
143 dev_dbg(drv_data->dev, "Setting dvfs level %u: freq = %lu Hz\n",
144 level, drv_data->fv_info[level].freq);
147 MAKE_DVFS_LEVEL_UPDATE_EFFECTIVE:
148 ret = mali_set_level(drv_data->dev, level);
150 dev_err(drv_data->dev, "set freq error, %d", ret);
156 * 返回 mali_dvfs_facility 当前是否被使能.
158 bool mali_dvfs_is_enabled(struct device *dev)
160 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
161 struct mali_dvfs *dvfs = &drv_data->dvfs;
163 return dvfs->enabled;
167 * 使能 mali_dvfs_facility.
169 void mali_dvfs_enable(struct device *dev)
171 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
172 struct mali_dvfs *dvfs = &drv_data->dvfs;
174 dvfs->enabled = true;
178 * 禁用 mali_dvfs_facility.
180 void mali_dvfs_disable(struct device *dev)
182 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
183 struct mali_dvfs *dvfs = &drv_data->dvfs;
185 dvfs->enabled = false;
186 cancel_work_sync(&dvfs->work);
188 /* 使用 lowest_dvfs_level. */
189 if (0 != mali_set_level(dev, 0))
190 W("fail to set current_dvfs_level to index 0.");
194 * 返回当前 mali_dvfs_context 中最后记录的 mali_utilization.
196 unsigned int mali_dvfs_utilisation(struct device *dev)
198 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
199 struct mali_dvfs *dvfs = &drv_data->dvfs;
201 return dvfs->utilisation;
205 * 根据当前的 mali_utilization_event, 对应调整 mali_dvfs_facility.
206 * 运行在 context_of_common_part_calling_back_mali_utilization_event 中.
208 int mali_dvfs_event(struct device *dev, u32 utilisation)
210 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
211 struct mali_dvfs *dvfs = &drv_data->dvfs;
213 dvfs->utilisation = utilisation;
214 V("mali_utilization_in_percentage : %d", utilisation * 100 / 256);
217 /* 将 work_to_handle_mali_utilization_event,
218 * 放置到 kernel_global_workqueue, 待执行. */
219 schedule_work(&dvfs->work);
225 static void mali_dvfs_threshold(u32 div,
226 struct mali_platform_drv_data *drv_data)
228 int length = drv_data->fv_info_length;
233 for (level = 0; level < length; level++) {
235 drv_data->fv_info[level].min = level0_min;
237 drv_data->fv_info[level].max = levelf_max;
239 drv_data->fv_info[level].max = level0_max;
241 pre_level = level - 1;
242 if (level == length - 1)
243 drv_data->fv_info[level].max = levelf_max;
245 drv_data->fv_info[level].max
246 = drv_data->fv_info[pre_level].max + div;
248 drv_data->fv_info[level].min
249 = drv_data->fv_info[pre_level].max
250 * drv_data->fv_info[pre_level].freq
251 / drv_data->fv_info[level].freq;
254 = drv_data->fv_info[level].max
255 - drv_data->fv_info[level].min;
256 drv_data->fv_info[level].min += mali_fix_float(tmp);
259 dev_info(drv_data->dev, "freq: %lu, min_threshold: %d, max_threshold: %d\n",
260 drv_data->fv_info[level].freq,
261 drv_data->fv_info[level].min,
262 drv_data->fv_info[level].max);
266 /*---------------------------------------------------------------------------*/
268 int mali_dvfs_init(struct device *dev)
270 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
271 /*mali_dvfs_context. */
272 struct mali_dvfs *dvfs = &drv_data->dvfs;
273 /* gpu_clk_freq_table. */
274 struct cpufreq_frequency_table *freq_table;
279 freq_table = dvfs_get_freq_volt_table(drv_data->clk);
281 dev_err(dev, "Can't find dvfs table in dts\n");
285 /* 确定 len_of_avaialble_dvfs_level_list. */
286 while (freq_table[i].frequency != CPUFREQ_TABLE_END) {
287 drv_data->fv_info_length++;
291 drv_data->fv_info = devm_kcalloc(dev, drv_data->fv_info_length,
292 sizeof(*drv_data->fv_info),
294 if (!drv_data->fv_info)
297 for (i = 0; i < drv_data->fv_info_length; i++)
298 drv_data->fv_info[i].freq = freq_table[i].frequency * 1000;
300 if (drv_data->fv_info_length > 1)
302 = round_up(((levelf_max - level0_max)
303 / (drv_data->fv_info_length - 1)),
306 mali_dvfs_threshold(div_dvfs, drv_data);
308 ret = dvfs_clk_set_rate(drv_data->clk, drv_data->fv_info[0].freq);
312 drv_data->dvfs.current_level = 0;
314 dev_info(dev, "initial freq = %lu\n",
315 dvfs_clk_get_rate(drv_data->clk));
317 INIT_WORK(&dvfs->work, mali_dvfs_event_proc);
318 dvfs->enabled = true;
320 dvfs->m_count_of_requests_to_jump_up = 0;
321 dvfs->m_count_of_requests_to_jump_down = 0;
326 void mali_dvfs_term(struct device *dev)
328 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
329 struct mali_dvfs *dvfs = &drv_data->dvfs;
331 dvfs->m_count_of_requests_to_jump_up = 0;
332 dvfs->m_count_of_requests_to_jump_down = 0;
334 dvfs->enabled = false;
335 cancel_work_sync(&dvfs->work);