2 * Copyright (C) 2011-2016 ARM Limited. All rights reserved.
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.
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.
11 #include "mali_osk_mali.h"
12 #include "mali_kernel_common.h"
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>
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.
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 */
37 #include "mali_pm_metrics.h"
40 mali_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
42 struct mali_device *mdev = dev_get_drvdata(dev);
43 struct dev_pm_opp *opp;
44 unsigned long freq = 0;
45 unsigned long voltage;
51 opp = devfreq_recommended_opp(dev, &freq, flags);
52 voltage = dev_pm_opp_get_voltage(opp);
54 if (IS_ERR_OR_NULL(opp)) {
55 MALI_PRINT_ERROR(("Failed to get opp (%ld)\n", PTR_ERR(opp)));
59 MALI_DEBUG_PRINT(2, ("mali_devfreq_target:set_freq = %lld flags = 0x%x\n", freq, flags));
61 * Only update if there is a change of frequency
63 if (mdev->current_freq == freq) {
65 mali_pm_reset_dvfs_utilisation(mdev);
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);
74 MALI_PRINT_ERROR(("Failed to increase voltage (%d)\n", err));
80 err = clk_set_rate(mdev->clock, freq);
82 MALI_PRINT_ERROR(("Failed to set clock %lu (target %lu)\n", freq, *target_freq));
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);
91 MALI_PRINT_ERROR(("Failed to decrease voltage (%d)\n", err));
98 mdev->current_voltage = voltage;
99 mdev->current_freq = freq;
101 mali_pm_reset_dvfs_utilisation(mdev);
107 mali_devfreq_cur_freq(struct device *dev, unsigned long *freq)
109 struct mali_device *mdev = dev_get_drvdata(dev);
111 *freq = mdev->current_freq;
113 MALI_DEBUG_PRINT(2, ("mali_devfreq_cur_freq: freq = %d \n", *freq));
118 mali_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
120 struct mali_device *mdev = dev_get_drvdata(dev);
122 stat->current_frequency = mdev->current_freq;
124 mali_pm_get_dvfs_utilisation(mdev,
125 &stat->total_time, &stat->busy_time);
127 stat->private_data = NULL;
129 #ifdef CONFIG_DEVFREQ_THERMAL
130 memcpy(&mdev->devfreq->last_status, stat, sizeof(*stat));
136 /* setup platform specific opp in platform.c*/
137 int __weak setup_opps(void)
142 /* term platform specific opp in platform.c*/
143 int __weak term_opps(struct device *dev)
148 static int mali_devfreq_init_freq_table(struct mali_device *mdev,
149 struct devfreq_dev_profile *dp)
153 unsigned long freq = 0;
154 struct dev_pm_opp *opp;
161 count = dev_pm_opp_get_opp_count(mdev->dev);
168 MALI_DEBUG_PRINT(2, ("mali devfreq table count %d\n", count));
170 dp->freq_table = kmalloc_array(count, sizeof(dp->freq_table[0]),
176 for (i = 0; i < count; i++, freq++) {
177 opp = dev_pm_opp_find_freq_ceil(mdev->dev, &freq);
181 dp->freq_table[i] = freq;
182 MALI_DEBUG_PRINT(2, ("mali devfreq table array[%d] = %d\n", i, freq));
187 MALI_PRINT_ERROR(("Unable to enumerate all OPPs (%d!=%d)\n",
195 static void mali_devfreq_term_freq_table(struct mali_device *mdev)
197 struct devfreq_dev_profile *dp = mdev->devfreq->profile;
199 kfree(dp->freq_table);
200 term_opps(mdev->dev);
203 static void mali_devfreq_exit(struct device *dev)
205 struct mali_device *mdev = dev_get_drvdata(dev);
207 mali_devfreq_term_freq_table(mdev);
210 int mali_devfreq_init(struct mali_device *mdev)
212 #ifdef CONFIG_DEVFREQ_THERMAL
213 struct devfreq_cooling_power *callbacks = NULL;
214 _mali_osk_device_data data;
216 struct devfreq_dev_profile *dp;
219 MALI_DEBUG_PRINT(2, ("Init Mali devfreq\n"));
224 mdev->current_freq = clk_get_rate(mdev->clock);
226 dp = &mdev->devfreq_profile;
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;
235 if (mali_devfreq_init_freq_table(mdev, dp))
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);
245 err = devfreq_register_opp_notifier(mdev->dev, mdev->devfreq);
247 MALI_PRINT_ERROR(("Failed to register OPP notifier (%d)\n", err));
248 goto opp_notifier_failed;
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;
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"));
263 mdev->devfreq_cooling = of_devfreq_cooling_register_power(
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));
272 MALI_DEBUG_PRINT(2, ("Mali GPU Thermal Cooling installed \n"));
279 #ifdef CONFIG_DEVFREQ_THERMAL
281 devfreq_unregister_opp_notifier(mdev->dev, mdev->devfreq);
282 #endif /* CONFIG_DEVFREQ_THERMAL */
284 err = devfreq_remove_device(mdev->devfreq);
286 MALI_PRINT_ERROR(("Failed to terminate devfreq (%d)\n", err));
288 mdev->devfreq = NULL;
293 void mali_devfreq_term(struct mali_device *mdev)
297 MALI_DEBUG_PRINT(2, ("Term Mali devfreq\n"));
299 #ifdef CONFIG_DEVFREQ_THERMAL
300 devfreq_cooling_unregister(mdev->devfreq_cooling);
303 devfreq_unregister_opp_notifier(mdev->dev, mdev->devfreq);
305 err = devfreq_remove_device(mdev->devfreq);
307 MALI_PRINT_ERROR(("Failed to terminate devfreq (%d)\n", err));
309 mdev->devfreq = NULL;