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 "mali_platform.h"
40 * anchor_of_device_of_mali_gpu.
42 static struct device *mali_dev;
45 * 设置 current_dvfs_level.
48 * 待设置为 current 的 dvfs_level 实例,
49 * 在 dvfs_level_list 中的 index,
50 * 即 index_of_new_current_level.
56 int mali_set_level(struct device *dev, int level)
58 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
59 /* gpu_clk_freq_of_new_current_level. */
62 /* index_of_old_current_level. */
63 unsigned int current_level;
65 _mali_osk_mutex_wait(drv_data->clock_set_lock);
67 current_level = drv_data->dvfs.current_level;
68 freq = drv_data->fv_info[level].freq;
70 if (level == current_level) {
71 D("we are already in the target level, to exit.");
72 _mali_osk_mutex_signal(drv_data->clock_set_lock);
76 /* .KP : 调用 dvfs_module 的接口, 将 cpu_clk 设置为 'freq'. */
77 ret = dvfs_clk_set_rate(drv_data->clk, freq);
79 _mali_osk_mutex_signal(drv_data->clock_set_lock);
83 D("have set gpu_clk to %lu of new_level %d, the old_level is %d.",
87 /* update index_of_current_dvfs_level. */
88 drv_data->dvfs.current_level = level;
90 _mali_osk_mutex_signal(drv_data->clock_set_lock);
96 * 初始化 gpu_dvfs_node 和 gpu_power_domain.
98 static int mali_clock_init(struct device *dev)
100 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
102 unsigned long rate = 200 * 1000 * 1000;
104 D("to get clk_mali.");
105 drv_data->clock = clk_get(drv_data->dev, "clk_mali");
106 if (IS_ERR_OR_NULL(drv_data->clock)) {
107 err = PTR_ERR(drv_data->clock);
109 drv_data->clock = NULL;
110 E("fail to get clk_mali, err : %d", err);
113 D("to preare and enable clk_mali.");
114 err = clk_prepare_enable(drv_data->clock);
116 E("Failed to prepare and enable clock (%d)\n", err);
119 I("to set freq_of_clk_gpu to %lu.", rate);
120 err = clk_set_rate(drv_data->clock, rate);
122 E("Failed to set clock.");
126 D("success to init clk_mali.");
130 static void mali_clock_term(struct device *dev)
134 /*---------------------------------------------------------------------------*/
136 static ssize_t show_available_frequencies(struct device *dev,
137 struct device_attribute *attr,
140 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
144 for (i = 0; i < drv_data->fv_info_length; i++)
145 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%lu\n",
146 drv_data->fv_info[i].freq);
151 static ssize_t show_clock(struct device *dev,
152 struct device_attribute *attr, char *buf)
154 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
156 return scnprintf(buf,
159 dvfs_clk_get_rate(drv_data->clk));
162 static ssize_t set_clock(struct device *dev,
163 struct device_attribute *attr,
164 const char *buf, size_t count)
166 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
171 ret = kstrtoul(buf, 10, &freq);
175 for (level = drv_data->fv_info_length - 1; level > 0; level--) {
176 unsigned long tmp = drv_data->fv_info[level].freq;
182 dev_info(dev, "Using fv_info table %d: for %lu Hz\n", level, freq);
184 ret = mali_set_level(dev, level);
191 static ssize_t show_dvfs_enable(struct device *dev,
192 struct device_attribute *attr,
195 return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_is_enabled(dev));
198 static ssize_t set_dvfs_enable(struct device *dev,
199 struct device_attribute *attr,
203 unsigned long enable;
206 ret = kstrtoul(buf, 0, &enable);
211 mali_dvfs_enable(dev);
212 else if (enable == 0)
213 mali_dvfs_disable(dev);
220 static ssize_t show_utilisation(struct device *dev,
221 struct device_attribute *attr,
224 return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_utilisation(dev));
227 static ssize_t error_count_show(struct device *dev,
228 struct device_attribute *attr,
231 return sprintf(buf, "%d\n", mali_group_error);
234 static DEVICE_ATTR(available_frequencies,
236 show_available_frequencies,
238 static DEVICE_ATTR(clock, S_IRUGO | S_IWUSR, show_clock, set_clock);
239 static DEVICE_ATTR(dvfs_enable,
243 static DEVICE_ATTR(utilisation, S_IRUGO, show_utilisation, NULL);
244 static DEVICE_ATTR(error_count, 0644, error_count_show, NULL);
246 static struct attribute *mali_sysfs_entries[] = {
247 &dev_attr_available_frequencies.attr,
248 &dev_attr_clock.attr,
249 &dev_attr_dvfs_enable.attr,
250 &dev_attr_utilisation.attr,
251 &dev_attr_error_count.attr,
255 static const struct attribute_group mali_attr_group = {
256 .attrs = mali_sysfs_entries,
260 * 创建 sysfs_nodes_of_platform_dependent_part.
262 static int mali_create_sysfs(struct device *dev)
266 ret = sysfs_create_group(&dev->kobj, &mali_attr_group);
268 dev_err(dev, "create sysfs group error, %d\n", ret);
273 static void mali_remove_sysfs(struct device *dev)
275 sysfs_remove_group(&dev->kobj, &mali_attr_group);
278 /*---------------------------------------------------------------------------*/
281 * 对 platform_device_of_mali_gpu,
282 * 完成仅和 platform_dependent_part 有关的初始化.
284 _mali_osk_errcode_t mali_platform_init(struct platform_device *pdev)
286 struct device *dev = &pdev->dev;
287 /* mali_driver_private_data. */
288 struct mali_platform_drv_data *mali_drv_data;
291 mali_drv_data = devm_kzalloc(dev, sizeof(*mali_drv_data), GFP_KERNEL);
293 return _MALI_OSK_ERR_NOMEM;
295 dev_set_drvdata(dev, mali_drv_data);
297 mali_drv_data->dev = dev;
301 D("to c all mali_clock_init.");
302 ret = mali_clock_init(dev);
306 D("to call mali_dvfs_init.");
307 ret = mali_dvfs_init(dev);
311 D("to call mali_create_sysfs.");
312 ret = mali_create_sysfs(dev);
316 mali_drv_data->clock_set_lock =
317 _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,
318 _MALI_OSK_LOCK_ORDER_UTILIZATION);
322 mali_clock_term(dev);
324 return _MALI_OSK_ERR_FAULT;
327 _mali_osk_errcode_t mali_platform_deinit(struct platform_device *pdev)
329 struct device *dev = &pdev->dev;
330 struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
332 mali_remove_sysfs(dev);
334 mali_clock_term(dev);
335 _mali_osk_mutex_term(drv_data->clock_set_lock);
340 /*---------------------------------------------------------------------------*/
343 * 对 gpu_power_domain(mali_power_domain),
344 * "上电, 开 clk" / "下电, 关 clk".
347 * false, 对 gpu_power_domain 上电.
350 static _mali_osk_errcode_t mali_power_domain_control(bool bpower_off)
352 struct mali_platform_drv_data *drv_data = dev_get_drvdata(mali_dev);
356 if (!drv_data->power_state) {
357 D("to ENABLE clk to gpu_dvfs_node.");
358 dvfs_clk_prepare_enable(drv_data->clk);
361 D("to power UP gpu_power_domain.");
362 clk_prepare_enable(drv_data->pd);
365 drv_data->power_state = true;
368 if (drv_data->power_state) {
369 D("to DISABLE clk to gpu_dvfs_node.");
370 dvfs_clk_disable_unprepare(drv_data->clk);
373 D("to power DOWN gpu_power_domain.");
374 clk_disable_unprepare(drv_data->pd);
377 drv_data->power_state = false;
384 _mali_osk_errcode_t mali_platform_power_mode_change(
385 enum mali_power_mode power_mode)
390 /*---------------------------------------------------------------------------*/
393 * 将注册到 common_part 中的, 对 mali_utilization_event 的 handler,
394 * 即 common_part 会直接将 mali_utilization_event 通知回调到本函数.
396 void mali_gpu_utilization_handler(struct mali_gpu_utilization_data *data)
398 if (data->utilization_pp > 256)
401 /* dev_dbg(mali_dev, "utilization:%d\r\n", data->utilization_pp); */
403 mali_dvfs_event(mali_dev, data->utilization_pp);