MALI: utgard: upgrade DDK to r6p1-01rel0
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / mali400 / mali / linux / mali_devfreq.c
1 /*
2  * Copyright (C) 2011-2016 ARM Limited. All rights reserved.
3  * 
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  * 
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9  */
10
11 #include "mali_osk_mali.h"
12 #include "mali_kernel_common.h"
13
14 #include <linux/clk.h>
15 #include <linux/devfreq.h>
16 #include <linux/regulator/consumer.h>
17 #include <linux/regulator/driver.h>
18 #ifdef CONFIG_DEVFREQ_THERMAL
19 #include <linux/devfreq_cooling.h>
20 #endif
21
22 #include <linux/version.h>
23 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
24 #include <linux/pm_opp.h>
25 #else /* Linux >= 3.13 */
26 /* In 3.13 the OPP include header file, types, and functions were all
27  * renamed. Use the old filename for the include, and define the new names to
28  * the old, when an old kernel is detected.
29  */
30 #include <linux/opp.h>
31 #define dev_pm_opp opp
32 #define dev_pm_opp_get_voltage opp_get_voltage
33 #define dev_pm_opp_get_opp_count opp_get_opp_count
34 #define dev_pm_opp_find_freq_ceil opp_find_freq_ceil
35 #endif /* Linux >= 3.13 */
36
37 #include "mali_pm_metrics.h"
38
39 static int
40 mali_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
41 {
42         struct mali_device *mdev = dev_get_drvdata(dev);
43         struct dev_pm_opp *opp;
44         unsigned long freq = 0;
45         unsigned long voltage;
46         int err;
47
48         freq = *target_freq;
49
50         rcu_read_lock();
51         opp = devfreq_recommended_opp(dev, &freq, flags);
52         voltage = dev_pm_opp_get_voltage(opp);
53         rcu_read_unlock();
54         if (IS_ERR_OR_NULL(opp)) {
55                 MALI_PRINT_ERROR(("Failed to get opp (%ld)\n", PTR_ERR(opp)));
56                 return PTR_ERR(opp);
57         }
58
59         MALI_DEBUG_PRINT(2, ("mali_devfreq_target:set_freq = %lld flags = 0x%x\n", freq, flags));
60         /*
61          * Only update if there is a change of frequency
62          */
63         if (mdev->current_freq == freq) {
64                 *target_freq = freq;
65                 mali_pm_reset_dvfs_utilisation(mdev);
66                 return 0;
67         }
68
69 #ifdef CONFIG_REGULATOR
70         if (mdev->regulator && mdev->current_voltage != voltage
71             && mdev->current_freq < freq) {
72                 err = regulator_set_voltage(mdev->regulator, voltage, voltage);
73                 if (err) {
74                         MALI_PRINT_ERROR(("Failed to increase voltage (%d)\n", err));
75                         return err;
76                 }
77         }
78 #endif
79
80         err = clk_set_rate(mdev->clock, freq);
81         if (err) {
82                 MALI_PRINT_ERROR(("Failed to set clock %lu (target %lu)\n", freq, *target_freq));
83                 return err;
84         }
85
86 #ifdef CONFIG_REGULATOR
87         if (mdev->regulator && mdev->current_voltage != voltage
88             && mdev->current_freq > freq) {
89                 err = regulator_set_voltage(mdev->regulator, voltage, voltage);
90                 if (err) {
91                         MALI_PRINT_ERROR(("Failed to decrease voltage (%d)\n", err));
92                         return err;
93                 }
94         }
95 #endif
96
97         *target_freq = freq;
98         mdev->current_voltage = voltage;
99         mdev->current_freq = freq;
100
101         mali_pm_reset_dvfs_utilisation(mdev);
102
103         return err;
104 }
105
106 static int
107 mali_devfreq_cur_freq(struct device *dev, unsigned long *freq)
108 {
109         struct mali_device *mdev = dev_get_drvdata(dev);
110
111         *freq = mdev->current_freq;
112
113         MALI_DEBUG_PRINT(2, ("mali_devfreq_cur_freq: freq = %d \n", *freq));
114         return 0;
115 }
116
117 static int
118 mali_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
119 {
120         struct mali_device *mdev = dev_get_drvdata(dev);
121
122         stat->current_frequency = mdev->current_freq;
123
124         mali_pm_get_dvfs_utilisation(mdev,
125                                      &stat->total_time, &stat->busy_time);
126
127         stat->private_data = NULL;
128
129 #ifdef CONFIG_DEVFREQ_THERMAL
130         memcpy(&mdev->devfreq->last_status, stat, sizeof(*stat));
131 #endif
132
133         return 0;
134 }
135
136 /* setup platform specific opp in platform.c*/
137 int __weak setup_opps(void)
138 {
139         return 0;
140 }
141
142 /* term platform specific opp in platform.c*/
143 int __weak term_opps(struct device *dev)
144 {
145         return 0;
146 }
147
148 static int mali_devfreq_init_freq_table(struct mali_device *mdev,
149                                         struct devfreq_dev_profile *dp)
150 {
151         int err, count;
152         int i = 0;
153         unsigned long freq = 0;
154         struct dev_pm_opp *opp;
155
156         err = setup_opps();
157         if (err)
158                 return err;
159
160         rcu_read_lock();
161         count = dev_pm_opp_get_opp_count(mdev->dev);
162         if (count < 0) {
163                 rcu_read_unlock();
164                 return count;
165         }
166         rcu_read_unlock();
167
168         MALI_DEBUG_PRINT(2, ("mali devfreq table count %d\n", count));
169
170         dp->freq_table = kmalloc_array(count, sizeof(dp->freq_table[0]),
171                                        GFP_KERNEL);
172         if (!dp->freq_table)
173                 return -ENOMEM;
174
175         rcu_read_lock();
176         for (i = 0; i < count; i++, freq++) {
177                 opp = dev_pm_opp_find_freq_ceil(mdev->dev, &freq);
178                 if (IS_ERR(opp))
179                         break;
180
181                 dp->freq_table[i] = freq;
182                 MALI_DEBUG_PRINT(2, ("mali devfreq table array[%d] = %d\n", i, freq));
183         }
184         rcu_read_unlock();
185
186         if (count != i)
187                 MALI_PRINT_ERROR(("Unable to enumerate all OPPs (%d!=%d)\n",
188                                   count, i));
189
190         dp->max_state = i;
191
192         return 0;
193 }
194
195 static void mali_devfreq_term_freq_table(struct mali_device *mdev)
196 {
197         struct devfreq_dev_profile *dp = mdev->devfreq->profile;
198
199         kfree(dp->freq_table);
200         term_opps(mdev->dev);
201 }
202
203 static void mali_devfreq_exit(struct device *dev)
204 {
205         struct mali_device *mdev = dev_get_drvdata(dev);
206
207         mali_devfreq_term_freq_table(mdev);
208 }
209
210 int mali_devfreq_init(struct mali_device *mdev)
211 {
212 #ifdef CONFIG_DEVFREQ_THERMAL
213         struct devfreq_cooling_power *callbacks = NULL;
214         _mali_osk_device_data data;
215 #endif
216         struct devfreq_dev_profile *dp;
217         int err;
218
219         MALI_DEBUG_PRINT(2, ("Init Mali devfreq\n"));
220
221         if (!mdev->clock)
222                 return -ENODEV;
223
224         mdev->current_freq = clk_get_rate(mdev->clock);
225
226         dp = &mdev->devfreq_profile;
227
228         dp->initial_freq = mdev->current_freq;
229         dp->polling_ms = 100;
230         dp->target = mali_devfreq_target;
231         dp->get_dev_status = mali_devfreq_status;
232         dp->get_cur_freq = mali_devfreq_cur_freq;
233         dp->exit = mali_devfreq_exit;
234
235         if (mali_devfreq_init_freq_table(mdev, dp))
236                 return -EFAULT;
237
238         mdev->devfreq = devfreq_add_device(mdev->dev, dp,
239                                            "simple_ondemand", NULL);
240         if (IS_ERR(mdev->devfreq)) {
241                 mali_devfreq_term_freq_table(mdev);
242                 return PTR_ERR(mdev->devfreq);
243         }
244
245         err = devfreq_register_opp_notifier(mdev->dev, mdev->devfreq);
246         if (err) {
247                 MALI_PRINT_ERROR(("Failed to register OPP notifier (%d)\n", err));
248                 goto opp_notifier_failed;
249         }
250
251 #ifdef CONFIG_DEVFREQ_THERMAL
252         /* Initilization last_status it will be used when first power allocate called */
253         mdev->devfreq->last_status.current_frequency = mdev->current_freq;
254
255         if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) {
256                 if (NULL != data.gpu_cooling_ops) {
257                         callbacks = data.gpu_cooling_ops;
258                         MALI_DEBUG_PRINT(2, ("Mali GPU Thermal: Callback handler installed \n"));
259                 }
260         }
261
262         if (callbacks) {
263                 mdev->devfreq_cooling = of_devfreq_cooling_register_power(
264                                                 mdev->dev->of_node,
265                                                 mdev->devfreq,
266                                                 callbacks);
267                 if (IS_ERR_OR_NULL(mdev->devfreq_cooling)) {
268                         err = PTR_ERR(mdev->devfreq_cooling);
269                         MALI_PRINT_ERROR(("Failed to register cooling device (%d)\n", err));
270                         goto cooling_failed;
271                 } else {
272                         MALI_DEBUG_PRINT(2, ("Mali GPU Thermal Cooling installed \n"));
273                 }
274         }
275 #endif
276
277         return 0;
278
279 #ifdef CONFIG_DEVFREQ_THERMAL
280 cooling_failed:
281         devfreq_unregister_opp_notifier(mdev->dev, mdev->devfreq);
282 #endif /* CONFIG_DEVFREQ_THERMAL */
283 opp_notifier_failed:
284         err = devfreq_remove_device(mdev->devfreq);
285         if (err)
286                 MALI_PRINT_ERROR(("Failed to terminate devfreq (%d)\n", err));
287         else
288                 mdev->devfreq = NULL;
289
290         return err;
291 }
292
293 void mali_devfreq_term(struct mali_device *mdev)
294 {
295         int err;
296
297         MALI_DEBUG_PRINT(2, ("Term Mali devfreq\n"));
298
299 #ifdef CONFIG_DEVFREQ_THERMAL
300         devfreq_cooling_unregister(mdev->devfreq_cooling);
301 #endif
302
303         devfreq_unregister_opp_notifier(mdev->dev, mdev->devfreq);
304
305         err = devfreq_remove_device(mdev->devfreq);
306         if (err)
307                 MALI_PRINT_ERROR(("Failed to terminate devfreq (%d)\n", err));
308         else
309                 mdev->devfreq = NULL;
310 }