rk3228: gpu: fix rk3228 gpu pd issue
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / mali400 / mali / platform / rk30 / mali_platform.c
1 /*
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
5  * ALL RIGHTS RESERVED
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.
9  */
10
11 /**
12  * @file mali_platform.c
13  * Platform specific Mali driver functions for a default platform
14  */
15 #include <linux/workqueue.h>
16 #include <linux/kernel.h>
17 #include <linux/slab.h>
18 #include <linux/gfp.h>
19 #include <linux/fs.h>
20 #include <linux/clk.h>
21 #include <linux/device.h>
22 #include <linux/regulator/driver.h>
23 #include <linux/miscdevice.h>
24 #include <linux/uaccess.h>
25 #include <linux/cpufreq.h>
26 #include <linux/of.h>
27
28 #include "mali_kernel_common.h"
29 #include "mali_osk.h"
30 #include "arm_core_scaling.h"
31 #include "mali_platform.h"
32
33
34 static int mali_core_scaling_enable;
35
36 u32 mali_group_error;
37
38 struct device *mali_dev;
39
40 int mali_set_level(struct device *dev, int level)
41 {
42         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
43         unsigned long freq;
44         int ret;
45         unsigned int current_level;
46
47         _mali_osk_mutex_wait(drv_data->clock_set_lock);
48
49         current_level = drv_data->dvfs.current_level;
50         freq = drv_data->fv_info[level].freq;
51
52         if (level == current_level) {
53                 _mali_osk_mutex_signal(drv_data->clock_set_lock);
54                 return 0;
55         }
56
57         ret = dvfs_clk_set_rate(drv_data->clk, freq);
58         if (ret) {
59                 _mali_osk_mutex_signal(drv_data->clock_set_lock);
60                 return ret;
61         }
62
63         dev_dbg(dev, "set freq %lu\n", freq);
64
65         drv_data->dvfs.current_level = level;
66
67         _mali_osk_mutex_signal(drv_data->clock_set_lock);
68
69         return 0;
70 }
71
72 static int mali_clock_init(struct device *dev)
73 {
74         int ret;
75
76         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
77
78         drv_data->pd = devm_clk_get(dev, "pd_gpu");
79         if (IS_ERR(drv_data->pd)) {
80                 ret = PTR_ERR(drv_data->pd);
81                 /* rk3228 gpu has no power domain,save NULL for compatible*/
82                 if (ret != -ENOENT) {
83                         dev_err(dev, "get pd_clk failed, %d\n", ret);
84                         return ret;
85                 }
86                 drv_data->pd = NULL;
87         }
88
89         if (drv_data->pd) {
90                 ret = clk_prepare_enable(drv_data->pd);
91                 if (ret) {
92                         dev_err(dev, "prepare pd_clk failed, %d\n", ret);
93                         return ret;
94                 }
95         }
96
97         drv_data->clk = clk_get_dvfs_node("clk_gpu");
98         if (IS_ERR(drv_data->clk)) {
99                 ret = PTR_ERR(drv_data->clk);
100                 dev_err(dev, "prepare clk gpu failed, %d\n", ret);
101                 return ret;
102         }
103
104         ret = dvfs_clk_prepare_enable(drv_data->clk);
105         if (ret) {
106                 dev_err(dev, "prepare clk failed, %d\n", ret);
107                 return ret;
108         }
109
110         drv_data->power_state = true;
111
112         return 0;
113 }
114
115 static void mali_clock_term(struct device *dev)
116 {
117         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
118
119         dvfs_clk_disable_unprepare(drv_data->clk);
120         if (drv_data->pd)
121                 clk_disable_unprepare(drv_data->pd);
122         drv_data->power_state = false;
123 }
124
125 static ssize_t show_available_frequencies(struct device *dev,
126                                           struct device_attribute *attr,
127                                           char *buf)
128 {
129         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
130         ssize_t ret = 0;
131         u32 i;
132
133         for (i = 0; i < drv_data->fv_info_length; i++)
134                 ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%lu\n",
135                                  drv_data->fv_info[i].freq);
136
137         return ret;
138 }
139
140 static ssize_t show_clock(struct device *dev,
141                           struct device_attribute *attr, char *buf)
142 {
143         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
144
145         return scnprintf(buf,
146                          PAGE_SIZE,
147                          "%lu\n",
148                          dvfs_clk_get_rate(drv_data->clk));
149 }
150
151 static ssize_t set_clock(struct device *dev, struct device_attribute *attr,
152                          const char *buf, size_t count)
153 {
154         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
155         unsigned long freq;
156         ssize_t ret;
157         u32 level;
158
159         ret = kstrtoul(buf, 10, &freq);
160         if (ret)
161                 return ret;
162
163         for (level = drv_data->fv_info_length - 1; level > 0; level--) {
164                 unsigned long tmp  = drv_data->fv_info[level].freq;
165
166                 if (tmp <= freq)
167                         break;
168         }
169
170         dev_info(dev, "Using fv_info table %d: for %lu Hz\n", level, freq);
171
172         ret = mali_set_level(dev, level);
173         if (ret)
174                 return ret;
175
176         return count;
177 }
178
179 static ssize_t show_dvfs_enable(struct device *dev,
180                                 struct device_attribute *attr,
181                                 char *buf)
182 {
183         return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_is_enabled(dev));
184 }
185
186 static ssize_t set_dvfs_enable(struct device *dev,
187                                struct device_attribute *attr,
188                                const char *buf,
189                                size_t count)
190 {
191         unsigned long enable;
192         ssize_t ret;
193
194         ret = kstrtoul(buf, 0, &enable);
195         if (ret)
196                 return ret;
197
198         if (enable == 1)
199                 mali_dvfs_enable(dev);
200         else if (enable == 0)
201                 mali_dvfs_disable(dev);
202         else
203                 return -EINVAL;
204
205         return count;
206 }
207
208 static ssize_t show_utilisation(struct device *dev,
209                                 struct device_attribute *attr,
210                                 char *buf)
211 {
212         return scnprintf(buf, PAGE_SIZE, "%u\n", mali_dvfs_utilisation(dev));
213 }
214
215 static int error_count_show(struct device *dev,
216                             struct device_attribute *attr,
217                             char *buf)
218 {
219         return sprintf(buf, "%d\n", mali_group_error);
220 }
221
222 DEVICE_ATTR(available_frequencies, S_IRUGO, show_available_frequencies, NULL);
223 DEVICE_ATTR(clock, S_IRUGO | S_IWUSR, show_clock, set_clock);
224 DEVICE_ATTR(dvfs_enable, S_IRUGO | S_IWUSR, show_dvfs_enable, set_dvfs_enable);
225 DEVICE_ATTR(utilisation, S_IRUGO, show_utilisation, NULL);
226 DEVICE_ATTR(error_count, 0644, error_count_show, NULL);
227
228 static struct attribute *mali_sysfs_entries[] = {
229         &dev_attr_available_frequencies.attr,
230         &dev_attr_clock.attr,
231         &dev_attr_dvfs_enable.attr,
232         &dev_attr_utilisation.attr,
233         &dev_attr_error_count.attr,
234         NULL,
235 };
236
237 static const struct attribute_group mali_attr_group = {
238         .attrs  = mali_sysfs_entries,
239 };
240
241 static int mali_create_sysfs(struct device *dev)
242 {
243         int ret;
244
245         ret = sysfs_create_group(&dev->kobj, &mali_attr_group);
246         if (ret)
247                 dev_err(dev, "create sysfs group error, %d\n", ret);
248
249         return ret;
250 }
251
252 void mali_remove_sysfs(struct device *dev)
253 {
254         sysfs_remove_group(&dev->kobj, &mali_attr_group);
255 }
256
257 _mali_osk_errcode_t mali_platform_init(struct platform_device *pdev)
258 {
259         struct device *dev = &pdev->dev;
260         struct mali_platform_drv_data *mali_drv_data;
261         int ret;
262
263         mali_drv_data = devm_kzalloc(dev, sizeof(*mali_drv_data), GFP_KERNEL);
264         if (!mali_drv_data) {
265                 dev_err(dev, "no mem\n");
266                 return _MALI_OSK_ERR_NOMEM;
267         }
268
269         dev_set_drvdata(dev, mali_drv_data);
270
271         mali_drv_data->dev = dev;
272
273         mali_dev = dev;
274
275         ret = mali_clock_init(dev);
276         if (ret)
277                 goto err_init;
278
279         ret = mali_dvfs_init(dev);
280         if (ret)
281                 goto err_init;
282
283         ret = mali_create_sysfs(dev);
284         if (ret)
285                 goto term_clk;
286
287         mali_drv_data->clock_set_lock =
288                 _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,
289                                      _MALI_OSK_LOCK_ORDER_UTILIZATION);
290         mali_core_scaling_enable = 1;
291
292         return 0;
293 term_clk:
294         mali_clock_term(dev);
295 err_init:
296         return _MALI_OSK_ERR_FAULT;
297 }
298
299 _mali_osk_errcode_t mali_platform_deinit(struct platform_device *pdev)
300 {
301         struct device *dev = &pdev->dev;
302         struct mali_platform_drv_data *drv_data = dev_get_drvdata(dev);
303
304         mali_core_scaling_term();
305         mali_clock_term(dev);
306         _mali_osk_mutex_term(drv_data->clock_set_lock);
307
308         return 0;
309 }
310
311 _mali_osk_errcode_t mali_power_domain_control(u32 bpower_off)
312 {
313         struct mali_platform_drv_data *drv_data = dev_get_drvdata(mali_dev);
314
315         if (bpower_off == 0) {
316                 if (!drv_data->power_state) {
317                         dvfs_clk_prepare_enable(drv_data->clk);
318                         if (drv_data->pd)
319                                 clk_prepare_enable(drv_data->pd);
320                         drv_data->power_state = true;
321                 }
322         } else if (bpower_off == 1) {
323                 if (drv_data->power_state) {
324                         dvfs_clk_disable_unprepare(drv_data->clk);
325                         if (drv_data->pd)
326                                 clk_disable_unprepare(drv_data->pd);
327                         drv_data->power_state = false;
328                 }
329         }
330
331         return 0;
332 }
333
334 _mali_osk_errcode_t mali_platform_power_mode_change(
335                         enum mali_power_mode power_mode)
336 {
337         switch (power_mode) {
338         case MALI_POWER_MODE_ON:
339                 MALI_DEBUG_PRINT(2, ("MALI_POWER_MODE_ON\r\n"));
340                 mali_power_domain_control(MALI_POWER_MODE_ON);
341                 break;
342         case MALI_POWER_MODE_LIGHT_SLEEP:
343                 MALI_DEBUG_PRINT(2, ("MALI_POWER_MODE_LIGHT_SLEEP\r\n"));
344                 mali_power_domain_control(MALI_POWER_MODE_LIGHT_SLEEP);
345                 break;
346         case MALI_POWER_MODE_DEEP_SLEEP:
347                 MALI_DEBUG_PRINT(2, ("MALI_POWER_MODE_DEEP_SLEEP\r\n"));
348                 mali_power_domain_control(MALI_POWER_MODE_DEEP_SLEEP);
349                 break;
350         default:
351                 MALI_DEBUG_PRINT(2,
352                                  (":power_mode(%d) not support \r\n",
353                                         power_mode));
354         }
355
356         return 0;
357 }
358 void mali_gpu_utilization_handler(struct mali_gpu_utilization_data *data)
359 {
360         if (data->utilization_pp > 256)
361                 return;
362
363         if (mali_core_scaling_enable)
364                 mali_core_scaling_update(data);
365
366         /* dev_dbg(mali_dev, "utilization:%d\r\n", data->utilization_pp); */
367
368         mali_dvfs_event(mali_dev, data->utilization_pp);
369 }