2 * This confidential and proprietary software may be used only as
3 * authorised by a licensing agreement from ARM Limited
4 * (C) COPYRIGHT 2009-2012 ARM Limited
6 * The entire notice above must be reproduced on all authorised
7 * copies and copies may only be made to the extent permitted
8 * by a licensing agreement from ARM Limited.
12 * @file mali_platform.c
13 * Platform specific Mali driver functions
14 * for a default platform
17 /* #define ENABLE_DEBUG_LOG */
18 #include "custom_log.h"
20 #include <linux/workqueue.h>
21 #include <linux/kernel.h>
22 #include <linux/slab.h>
23 #include <linux/gfp.h>
25 #include <linux/clk.h>
26 #include <linux/device.h>
27 #include <linux/regulator/driver.h>
28 #include <linux/miscdevice.h>
29 #include <linux/uaccess.h>
30 #include <linux/cpufreq.h>
33 #include "mali_kernel_common.h"
35 #include "arm_core_scaling.h"
36 #include "mali_platform.h"
40 * 是否使能 core_scaling 机制.
41 * .DP : core_scaling : 根据当前 mali_utilization_data,
42 * 配置 mali_gpu 中具体使用的 pp_core 的个数.
44 static int mali_core_scaling_enable;
49 * anchor_of_device_of_mali_gpu.
51 static struct device *mali_dev;
54 * 设置 current_dvfs_level.
57 * 待设置为 current 的 dvfs_level 实例,
58 * 在 dvfs_level_list 中的 index,
59 * 即 index_of_new_current_level.
65 int mali_set_level(struct device *dev, int level)
67 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
68 /* gpu_clk_freq_of_new_current_level. */
71 /* index_of_old_current_level. */
72 unsigned int current_level;
74 _mali_osk_mutex_wait(drv_data->clock_set_lock);
76 current_level = drv_data->dvfs.current_level;
77 freq = drv_data->fv_info[level].freq;
79 if (level == current_level) {
80 D("we are already in the target level, to exit.");
81 _mali_osk_mutex_signal(drv_data->clock_set_lock);
85 /* .KP : 调用 dvfs_module 的接口, 将 cpu_clk 设置为 'freq'. */
86 ret = dvfs_clk_set_rate(drv_data->clk, freq);
88 _mali_osk_mutex_signal(drv_data->clock_set_lock);
92 D("have set gpu_clk to %lu of new_level %d, " "the old_level is %d.",
96 /* update index_of_current_dvfs_level. */
97 drv_data->dvfs.current_level = level;
99 _mali_osk_mutex_signal(drv_data->clock_set_lock);
105 * 初始化 gpu_dvfs_node 和 gpu_power_domain.
107 static int mali_clock_init(struct device *dev)
111 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
113 drv_data->pd = devm_clk_get(dev, "pd_gpu");
114 if (IS_ERR(drv_data->pd)) {
115 ret = PTR_ERR(drv_data->pd);
116 /* rk3228 gpu has no power domain,save NULL for compatible*/
117 if (ret != -ENOENT) {
118 dev_err(dev, "get pd_clk failed, %d\n", ret);
125 ret = clk_prepare_enable(drv_data->pd);
127 dev_err(dev, "prepare pd_clk failed, %d\n", ret);
132 drv_data->clk = clk_get_dvfs_node("clk_gpu");
133 if (IS_ERR(drv_data->clk)) {
134 ret = PTR_ERR(drv_data->clk);
135 dev_err(dev, "prepare clk gpu failed, %d\n", ret);
139 ret = dvfs_clk_prepare_enable(drv_data->clk);
141 dev_err(dev, "prepare clk failed, %d\n", ret);
145 drv_data->power_state = true;
150 static void mali_clock_term(struct device *dev)
152 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
154 dvfs_clk_disable_unprepare(drv_data->clk);
156 clk_disable_unprepare(drv_data->pd);
157 drv_data->power_state = false;
160 /*---------------------------------------------------------------------------*/
162 static ssize_t show_available_frequencies(struct device *dev,
163 struct device_attribute *attr,
166 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
170 for (i = 0; i < drv_data->fv_info_length; i++)
171 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%lu\n",
172 drv_data->fv_info[i].freq);
177 static ssize_t show_clock(struct device *dev,
178 struct device_attribute *attr, char *buf)
180 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
182 return scnprintf(buf,
185 dvfs_clk_get_rate(drv_data->clk));
188 static ssize_t set_clock(struct device *dev,
189 struct device_attribute *attr,
190 const char *buf, size_t count)
192 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
197 ret = kstrtoul(buf, 10, &freq);
201 for (level = drv_data->fv_info_length - 1; level > 0; level--) {
202 unsigned long tmp = drv_data->fv_info[level].freq;
208 dev_info(dev, "Using fv_info table %d: for %lu Hz\n", level, freq);
210 ret = mali_set_level(dev, level);
217 static ssize_t show_dvfs_enable(struct device *dev,
218 struct device_attribute *attr,
221 return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_is_enabled(dev));
224 static ssize_t set_dvfs_enable(struct device *dev,
225 struct device_attribute *attr,
229 unsigned long enable;
232 ret = kstrtoul(buf, 0, &enable);
237 mali_dvfs_enable(dev);
238 else if (enable == 0)
239 mali_dvfs_disable(dev);
246 static ssize_t show_utilisation(struct device *dev,
247 struct device_attribute *attr,
250 return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_utilisation(dev));
253 static int error_count_show(struct device *dev,
254 struct device_attribute *attr,
257 return sprintf(buf, "%d\n", mali_group_error);
260 static DEVICE_ATTR(available_frequencies, S_IRUGO, show_available_frequencies, NULL);
261 static DEVICE_ATTR(clock, S_IRUGO | S_IWUSR, show_clock, set_clock);
262 static DEVICE_ATTR(dvfs_enable, S_IRUGO | S_IWUSR, show_dvfs_enable, set_dvfs_enable);
263 static DEVICE_ATTR(utilisation, S_IRUGO, show_utilisation, NULL);
264 static DEVICE_ATTR(error_count, 0644, error_count_show, NULL);
266 static struct attribute *mali_sysfs_entries[] = {
267 &dev_attr_available_frequencies.attr,
268 &dev_attr_clock.attr,
269 &dev_attr_dvfs_enable.attr,
270 &dev_attr_utilisation.attr,
271 &dev_attr_error_count.attr,
275 static const struct attribute_group mali_attr_group = {
276 .attrs = mali_sysfs_entries,
280 * 创建 sysfs_nodes_of_platform_dependent_part.
282 static int mali_create_sysfs(struct device *dev)
286 ret = sysfs_create_group(&dev->kobj, &mali_attr_group);
288 dev_err(dev, "create sysfs group error, %d\n", ret);
293 static void mali_remove_sysfs(struct device *dev)
295 sysfs_remove_group(&dev->kobj, &mali_attr_group);
298 /*---------------------------------------------------------------------------*/
301 * 对 platform_device_of_mali_gpu,
302 * 完成仅和 platform_dependent_part 有关的初始化.
304 _mali_osk_errcode_t mali_platform_init(struct platform_device *pdev)
306 struct device *dev = &pdev->dev;
307 /* mali_driver_private_data. */
308 struct mali_platform_drv_data *mali_drv_data;
311 mali_drv_data = devm_kzalloc(dev, sizeof(*mali_drv_data), GFP_KERNEL);
312 if (!mali_drv_data) {
313 dev_err(dev, "no mem\n");
314 return _MALI_OSK_ERR_NOMEM;
317 dev_set_drvdata(dev, mali_drv_data);
319 mali_drv_data->dev = dev;
323 ret = mali_clock_init(dev);
327 ret = mali_dvfs_init(dev);
331 ret = mali_create_sysfs(dev);
335 mali_drv_data->clock_set_lock =
336 _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,
337 _MALI_OSK_LOCK_ORDER_UTILIZATION);
338 mali_core_scaling_enable = 1;
342 mali_clock_term(dev);
344 return _MALI_OSK_ERR_FAULT;
347 _mali_osk_errcode_t mali_platform_deinit(struct platform_device *pdev)
349 struct device *dev = &pdev->dev;
350 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
352 mali_remove_sysfs(dev);
354 mali_core_scaling_term();
355 mali_clock_term(dev);
356 _mali_osk_mutex_term(drv_data->clock_set_lock);
361 /*---------------------------------------------------------------------------*/
364 * 对 gpu_power_domain(mali_power_domain), "上电, 开 clk" / "下电, 关 clk".
367 * false, 对 gpu_power_domain 上电.
369 static _mali_osk_errcode_t mali_power_domain_control(bool bpower_off)
371 struct mali_platform_drv_data *drv_data = dev_get_drvdata(mali_dev);
375 if (!drv_data->power_state) {
376 D("to ENABLE clk to gpu_dvfs_node.");
377 dvfs_clk_prepare_enable(drv_data->clk);
380 D("to power UP gpu_power_domain.");
381 clk_prepare_enable(drv_data->pd);
384 drv_data->power_state = true;
387 if (drv_data->power_state) {
388 D("to DISABLE clk to gpu_dvfs_node.");
389 dvfs_clk_disable_unprepare(drv_data->clk);
392 D("to power DOWN gpu_power_domain.");
393 clk_disable_unprepare(drv_data->pd);
396 drv_data->power_state = false;
403 _mali_osk_errcode_t mali_platform_power_mode_change(
404 enum mali_power_mode power_mode)
406 bool bpower_off;/* 下电. */
408 switch (power_mode) {
409 case MALI_POWER_MODE_ON:
410 MALI_DEBUG_PRINT(2, ("MALI_POWER_MODE_ON\r\n"));
411 mali_dvfs_enable(mali_dev);
415 case MALI_POWER_MODE_LIGHT_SLEEP:
416 MALI_DEBUG_PRINT(2, ("MALI_POWER_MODE_LIGHT_SLEEP\r\n"));
417 mali_dvfs_disable(mali_dev);
422 case MALI_POWER_MODE_DEEP_SLEEP:
423 MALI_DEBUG_PRINT(2, ("MALI_POWER_MODE_DEEP_SLEEP\r\n"));
424 mali_dvfs_disable(mali_dev);
431 ("power_mode(%d) not support\n", power_mode));
432 return _MALI_OSK_ERR_INVALID_ARGS;
435 mali_power_domain_control(bpower_off);
440 /*---------------------------------------------------------------------------*/
443 * 将注册到 common_part 中的, 对 mali_utilization_event 的 handler,
444 * 即 common_part 会直接将 mali_utilization_event 通知回调到本函数.
446 void mali_gpu_utilization_handler(struct mali_gpu_utilization_data *data)
448 if (data->utilization_pp > 256)
451 if (mali_core_scaling_enable)
452 mali_core_scaling_update(data);
454 /* dev_dbg(mali_dev, "utilization:%d\r\n", data->utilization_pp); */
456 mali_dvfs_event(mali_dev, data->utilization_pp);