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"
39 * 是否使能 core_scaling 机制.
40 * .DP : core_scaling : 根据当前 mali_utilization_data,
41 * 配置 mali_gpu 中具体使用的 pp_core 的个数.
43 static int mali_core_scaling_enable;
48 * anchor_of_device_of_mali_gpu.
50 static struct device *mali_dev;
53 * 设置 current_dvfs_level.
56 * 待设置为 current 的 dvfs_level 实例,
57 * 在 dvfs_level_list 中的 index,
58 * 即 index_of_new_current_level.
64 int mali_set_level(struct device *dev, int level)
66 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
67 /* gpu_clk_freq_of_new_current_level. */
70 /* index_of_old_current_level. */
71 unsigned int current_level;
73 _mali_osk_mutex_wait(drv_data->clock_set_lock);
75 current_level = drv_data->dvfs.current_level;
76 freq = drv_data->fv_info[level].freq;
78 if (level == current_level) {
79 D("we are already in the target level, to exit.");
80 _mali_osk_mutex_signal(drv_data->clock_set_lock);
84 /* .KP : 调用 dvfs_module 的接口, 将 cpu_clk 设置为 'freq'. */
85 ret = dvfs_clk_set_rate(drv_data->clk, freq);
87 _mali_osk_mutex_signal(drv_data->clock_set_lock);
91 D("have set gpu_clk to %lu of new_level %d, the old_level is %d.",
95 /* update index_of_current_dvfs_level. */
96 drv_data->dvfs.current_level = level;
98 _mali_osk_mutex_signal(drv_data->clock_set_lock);
104 * 初始化 gpu_dvfs_node 和 gpu_power_domain.
106 static int mali_clock_init(struct device *dev)
108 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
110 unsigned long rate = 200 * 1000 * 1000;
112 D("to get clk_mali.");
113 drv_data->clock = clk_get(drv_data->dev, "clk_mali");
114 if (IS_ERR_OR_NULL(drv_data->clock)) {
115 err = PTR_ERR(drv_data->clock);
117 drv_data->clock = NULL;
118 E("fail to get clk_mali, err : %d", err);
121 D("to preare and enable clk_mali.");
122 err = clk_prepare_enable(drv_data->clock);
124 E("Failed to prepare and enable clock (%d)\n", err);
127 I("to set freq_of_clk_gpu to %lu.", rate);
128 err = clk_set_rate(drv_data->clock, rate);
130 E("Failed to set clock.");
134 D("success to init clk_mali.");
138 static void mali_clock_term(struct device *dev)
142 /*---------------------------------------------------------------------------*/
144 static ssize_t show_available_frequencies(struct device *dev,
145 struct device_attribute *attr,
148 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
152 for (i = 0; i < drv_data->fv_info_length; i++)
153 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%lu\n",
154 drv_data->fv_info[i].freq);
159 static ssize_t show_clock(struct device *dev,
160 struct device_attribute *attr, char *buf)
162 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
164 return scnprintf(buf,
167 dvfs_clk_get_rate(drv_data->clk));
170 static ssize_t set_clock(struct device *dev,
171 struct device_attribute *attr,
172 const char *buf, size_t count)
174 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
179 ret = kstrtoul(buf, 10, &freq);
183 for (level = drv_data->fv_info_length - 1; level > 0; level--) {
184 unsigned long tmp = drv_data->fv_info[level].freq;
190 dev_info(dev, "Using fv_info table %d: for %lu Hz\n", level, freq);
192 ret = mali_set_level(dev, level);
199 static ssize_t show_dvfs_enable(struct device *dev,
200 struct device_attribute *attr,
203 return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_is_enabled(dev));
206 static ssize_t set_dvfs_enable(struct device *dev,
207 struct device_attribute *attr,
211 unsigned long enable;
214 ret = kstrtoul(buf, 0, &enable);
219 mali_dvfs_enable(dev);
220 else if (enable == 0)
221 mali_dvfs_disable(dev);
228 static ssize_t show_utilisation(struct device *dev,
229 struct device_attribute *attr,
232 return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_utilisation(dev));
235 static int error_count_show(struct device *dev,
236 struct device_attribute *attr,
239 return sprintf(buf, "%d\n", mali_group_error);
242 static DEVICE_ATTR(available_frequencies,
244 show_available_frequencies,
246 static DEVICE_ATTR(clock, S_IRUGO | S_IWUSR, show_clock, set_clock);
247 static DEVICE_ATTR(dvfs_enable,
251 static DEVICE_ATTR(utilisation, S_IRUGO, show_utilisation, NULL);
252 static DEVICE_ATTR(error_count, 0644, error_count_show, NULL);
254 static struct attribute *mali_sysfs_entries[] = {
255 &dev_attr_available_frequencies.attr,
256 &dev_attr_clock.attr,
257 &dev_attr_dvfs_enable.attr,
258 &dev_attr_utilisation.attr,
259 &dev_attr_error_count.attr,
263 static const struct attribute_group mali_attr_group = {
264 .attrs = mali_sysfs_entries,
268 * 创建 sysfs_nodes_of_platform_dependent_part.
270 static int mali_create_sysfs(struct device *dev)
274 ret = sysfs_create_group(&dev->kobj, &mali_attr_group);
276 dev_err(dev, "create sysfs group error, %d\n", ret);
281 static void mali_remove_sysfs(struct device *dev)
283 sysfs_remove_group(&dev->kobj, &mali_attr_group);
286 /*---------------------------------------------------------------------------*/
289 * 对 platform_device_of_mali_gpu,
290 * 完成仅和 platform_dependent_part 有关的初始化.
292 _mali_osk_errcode_t mali_platform_init(struct platform_device *pdev)
294 struct device *dev = &pdev->dev;
295 /* mali_driver_private_data. */
296 struct mali_platform_drv_data *mali_drv_data;
299 mali_drv_data = devm_kzalloc(dev, sizeof(*mali_drv_data), GFP_KERNEL);
301 return _MALI_OSK_ERR_NOMEM;
303 dev_set_drvdata(dev, mali_drv_data);
305 mali_drv_data->dev = dev;
309 D("to c all mali_clock_init.");
310 ret = mali_clock_init(dev);
314 D("to call mali_dvfs_init.");
315 ret = mali_dvfs_init(dev);
319 D("to call mali_create_sysfs.");
320 ret = mali_create_sysfs(dev);
324 mali_drv_data->clock_set_lock =
325 _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,
326 _MALI_OSK_LOCK_ORDER_UTILIZATION);
327 mali_core_scaling_enable = 1;
331 mali_clock_term(dev);
333 return _MALI_OSK_ERR_FAULT;
336 _mali_osk_errcode_t mali_platform_deinit(struct platform_device *pdev)
338 struct device *dev = &pdev->dev;
339 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
341 mali_remove_sysfs(dev);
343 mali_core_scaling_term();
344 mali_clock_term(dev);
345 _mali_osk_mutex_term(drv_data->clock_set_lock);
350 /*---------------------------------------------------------------------------*/
353 * 对 gpu_power_domain(mali_power_domain),
354 * "上电, 开 clk" / "下电, 关 clk".
357 * false, 对 gpu_power_domain 上电.
360 static _mali_osk_errcode_t mali_power_domain_control(bool bpower_off)
362 struct mali_platform_drv_data *drv_data = dev_get_drvdata(mali_dev);
366 if (!drv_data->power_state) {
367 D("to ENABLE clk to gpu_dvfs_node.");
368 dvfs_clk_prepare_enable(drv_data->clk);
371 D("to power UP gpu_power_domain.");
372 clk_prepare_enable(drv_data->pd);
375 drv_data->power_state = true;
378 if (drv_data->power_state) {
379 D("to DISABLE clk to gpu_dvfs_node.");
380 dvfs_clk_disable_unprepare(drv_data->clk);
383 D("to power DOWN gpu_power_domain.");
384 clk_disable_unprepare(drv_data->pd);
387 drv_data->power_state = false;
394 _mali_osk_errcode_t mali_platform_power_mode_change(
395 enum mali_power_mode power_mode)
400 /*---------------------------------------------------------------------------*/
403 * 将注册到 common_part 中的, 对 mali_utilization_event 的 handler,
404 * 即 common_part 会直接将 mali_utilization_event 通知回调到本函数.
406 void mali_gpu_utilization_handler(struct mali_gpu_utilization_data *data)
408 if (data->utilization_pp > 256)
411 if (mali_core_scaling_enable)
412 mali_core_scaling_update(data);
414 /* dev_dbg(mali_dev, "utilization:%d\r\n", data->utilization_pp); */
416 mali_dvfs_event(mali_dev, data->utilization_pp);