123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- /*
- *
- * (C) COPYRIGHT 2014-2015 ARM Limited. All rights reserved.
- *
- * This program is free software and is provided to you under the terms of the
- * GNU General Public License version 2 as published by the Free Software
- * Foundation, and any use by you of this program is subject to the terms
- * of such GNU licence.
- *
- * A copy of the licence is included with the program, and can also be obtained
- * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
- #include <mali_kbase.h>
- #include <mali_kbase_config_defaults.h>
- #include <backend/gpu/mali_kbase_pm_internal.h>
- #ifdef CONFIG_DEVFREQ_THERMAL
- #include <backend/gpu/mali_kbase_power_model_simple.h>
- #endif
- #include <linux/clk.h>
- #include <linux/devfreq.h>
- #ifdef CONFIG_DEVFREQ_THERMAL
- #include <linux/devfreq_cooling.h>
- #endif
- #include <linux/version.h>
- #include <linux/pm_opp.h>
- static int
- kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
- {
- struct kbase_device *kbdev = dev_get_drvdata(dev);
- struct dev_pm_opp *opp;
- unsigned long freq = 0;
- unsigned long voltage;
- int err;
- freq = *target_freq;
- rcu_read_lock();
- opp = devfreq_recommended_opp(dev, &freq, flags);
- voltage = dev_pm_opp_get_voltage(opp);
- rcu_read_unlock();
- if (IS_ERR_OR_NULL(opp)) {
- dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
- return PTR_ERR(opp);
- }
- /*
- * Only update if there is a change of frequency
- */
- if (kbdev->current_freq == freq) {
- *target_freq = freq;
- return 0;
- }
- #ifdef CONFIG_REGULATOR
- if (kbdev->regulator && kbdev->current_voltage != voltage
- && kbdev->current_freq < freq) {
- err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
- if (err) {
- dev_err(dev, "Failed to increase voltage (%d)\n", err);
- return err;
- }
- }
- #endif
- err = clk_set_rate(kbdev->clock, freq);
- if (err) {
- dev_err(dev, "Failed to set clock %lu (target %lu)\n",
- freq, *target_freq);
- return err;
- }
- #ifdef CONFIG_REGULATOR
- if (kbdev->regulator && kbdev->current_voltage != voltage
- && kbdev->current_freq > freq) {
- err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
- if (err) {
- dev_err(dev, "Failed to decrease voltage (%d)\n", err);
- return err;
- }
- }
- #endif
- *target_freq = freq;
- kbdev->current_voltage = voltage;
- kbdev->current_freq = freq;
- kbase_pm_reset_dvfs_utilisation(kbdev);
- return err;
- }
- static int
- kbase_devfreq_cur_freq(struct device *dev, unsigned long *freq)
- {
- struct kbase_device *kbdev = dev_get_drvdata(dev);
- *freq = kbdev->current_freq;
- return 0;
- }
- static int
- kbase_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
- {
- struct kbase_device *kbdev = dev_get_drvdata(dev);
- stat->current_frequency = kbdev->current_freq;
- kbase_pm_get_dvfs_utilisation(kbdev,
- &stat->total_time, &stat->busy_time);
- stat->private_data = NULL;
- #ifdef CONFIG_DEVFREQ_THERMAL
- if (kbdev->devfreq_cooling)
- memcpy(&kbdev->devfreq_cooling->last_status, stat,
- sizeof(*stat));
- #endif
- return 0;
- }
- static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev,
- struct devfreq_dev_profile *dp)
- {
- int count;
- int i = 0;
- unsigned long freq = 0;
- struct dev_pm_opp *opp;
- rcu_read_lock();
- count = dev_pm_opp_get_opp_count(kbdev->dev);
- if (count < 0) {
- rcu_read_unlock();
- return count;
- }
- rcu_read_unlock();
- dp->freq_table = kmalloc_array(count, sizeof(dp->freq_table[0]),
- GFP_KERNEL);
- if (!dp->freq_table)
- return -ENOMEM;
- rcu_read_lock();
- for (i = 0; i < count; i++, freq++) {
- opp = dev_pm_opp_find_freq_ceil(kbdev->dev, &freq);
- if (IS_ERR(opp))
- break;
- dp->freq_table[i] = freq;
- }
- rcu_read_unlock();
- if (count != i)
- dev_warn(kbdev->dev, "Unable to enumerate all OPPs (%d!=%d\n",
- count, i);
- dp->max_state = i;
- return 0;
- }
- static void kbase_devfreq_term_freq_table(struct kbase_device *kbdev)
- {
- struct devfreq_dev_profile *dp = kbdev->devfreq->profile;
- kfree(dp->freq_table);
- }
- static void kbase_devfreq_exit(struct device *dev)
- {
- struct kbase_device *kbdev = dev_get_drvdata(dev);
- kbase_devfreq_term_freq_table(kbdev);
- }
- int kbase_devfreq_init(struct kbase_device *kbdev)
- {
- struct devfreq_dev_profile *dp;
- int err;
- if (!kbdev->clock)
- return -ENODEV;
- kbdev->current_freq = clk_get_rate(kbdev->clock);
- dp = &kbdev->devfreq_profile;
- dp->initial_freq = kbdev->current_freq;
- dp->polling_ms = 100;
- dp->target = kbase_devfreq_target;
- dp->get_dev_status = kbase_devfreq_status;
- dp->get_cur_freq = kbase_devfreq_cur_freq;
- dp->exit = kbase_devfreq_exit;
- if (kbase_devfreq_init_freq_table(kbdev, dp))
- return -EFAULT;
- kbdev->devfreq = devfreq_add_device(kbdev->dev, dp,
- "simple_ondemand", NULL);
- if (IS_ERR(kbdev->devfreq)) {
- kbase_devfreq_term_freq_table(kbdev);
- return PTR_ERR(kbdev->devfreq);
- }
- err = devfreq_register_opp_notifier(kbdev->dev, kbdev->devfreq);
- if (err) {
- dev_err(kbdev->dev,
- "Failed to register OPP notifier (%d)\n", err);
- goto opp_notifier_failed;
- }
- #ifdef CONFIG_DEVFREQ_THERMAL
- err = kbase_power_model_simple_init(kbdev);
- if (err && err != -ENODEV && err != -EPROBE_DEFER) {
- dev_err(kbdev->dev,
- "Failed to initialize simple power model (%d)\n",
- err);
- goto cooling_failed;
- }
- if (err == -EPROBE_DEFER)
- goto cooling_failed;
- if (err != -ENODEV) {
- kbdev->devfreq_cooling = of_devfreq_cooling_register_power(
- kbdev->dev->of_node,
- kbdev->devfreq,
- &power_model_simple_ops);
- if (IS_ERR_OR_NULL(kbdev->devfreq_cooling)) {
- err = PTR_ERR(kbdev->devfreq_cooling);
- dev_err(kbdev->dev,
- "Failed to register cooling device (%d)\n",
- err);
- goto cooling_failed;
- }
- } else {
- err = 0;
- }
- #endif
- return 0;
- #ifdef CONFIG_DEVFREQ_THERMAL
- cooling_failed:
- devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq);
- #endif /* CONFIG_DEVFREQ_THERMAL */
- opp_notifier_failed:
- if (devfreq_remove_device(kbdev->devfreq))
- dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err);
- else
- kbdev->devfreq = NULL;
- return err;
- }
- void kbase_devfreq_term(struct kbase_device *kbdev)
- {
- int err;
- dev_dbg(kbdev->dev, "Term Mali devfreq\n");
- #ifdef CONFIG_DEVFREQ_THERMAL
- if (kbdev->devfreq_cooling)
- devfreq_cooling_unregister(kbdev->devfreq_cooling);
- #endif
- devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq);
- err = devfreq_remove_device(kbdev->devfreq);
- if (err)
- dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err);
- else
- kbdev->devfreq = NULL;
- }
|