root/drivers/gpu/drm/panfrost/panfrost_devfreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. panfrost_devfreq_target
  2. panfrost_devfreq_reset
  3. panfrost_devfreq_get_dev_status
  4. panfrost_devfreq_get_cur_freq
  5. panfrost_devfreq_init
  6. panfrost_devfreq_fini
  7. panfrost_devfreq_resume
  8. panfrost_devfreq_suspend
  9. panfrost_devfreq_update_utilization
  10. panfrost_devfreq_record_transition

   1 // SPDX-License-Identifier: GPL-2.0
   2 /* Copyright 2019 Collabora ltd. */
   3 #include <linux/devfreq.h>
   4 #include <linux/platform_device.h>
   5 #include <linux/pm_opp.h>
   6 #include <linux/clk.h>
   7 #include <linux/regulator/consumer.h>
   8 
   9 #include "panfrost_device.h"
  10 #include "panfrost_devfreq.h"
  11 #include "panfrost_features.h"
  12 #include "panfrost_issues.h"
  13 #include "panfrost_gpu.h"
  14 #include "panfrost_regs.h"
  15 
  16 static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot);
  17 
  18 static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
  19                                    u32 flags)
  20 {
  21         struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev));
  22         struct dev_pm_opp *opp;
  23         unsigned long old_clk_rate = pfdev->devfreq.cur_freq;
  24         unsigned long target_volt, target_rate;
  25         int err;
  26 
  27         opp = devfreq_recommended_opp(dev, freq, flags);
  28         if (IS_ERR(opp))
  29                 return PTR_ERR(opp);
  30 
  31         target_rate = dev_pm_opp_get_freq(opp);
  32         target_volt = dev_pm_opp_get_voltage(opp);
  33         dev_pm_opp_put(opp);
  34 
  35         if (old_clk_rate == target_rate)
  36                 return 0;
  37 
  38         /*
  39          * If frequency scaling from low to high, adjust voltage first.
  40          * If frequency scaling from high to low, adjust frequency first.
  41          */
  42         if (old_clk_rate < target_rate) {
  43                 err = regulator_set_voltage(pfdev->regulator, target_volt,
  44                                             target_volt);
  45                 if (err) {
  46                         dev_err(dev, "Cannot set voltage %lu uV\n",
  47                                 target_volt);
  48                         return err;
  49                 }
  50         }
  51 
  52         err = clk_set_rate(pfdev->clock, target_rate);
  53         if (err) {
  54                 dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate,
  55                         err);
  56                 if (pfdev->regulator)
  57                         regulator_set_voltage(pfdev->regulator,
  58                                               pfdev->devfreq.cur_volt,
  59                                               pfdev->devfreq.cur_volt);
  60                 return err;
  61         }
  62 
  63         if (old_clk_rate > target_rate) {
  64                 err = regulator_set_voltage(pfdev->regulator, target_volt,
  65                                             target_volt);
  66                 if (err)
  67                         dev_err(dev, "Cannot set voltage %lu uV\n", target_volt);
  68         }
  69 
  70         pfdev->devfreq.cur_freq = target_rate;
  71         pfdev->devfreq.cur_volt = target_volt;
  72 
  73         return 0;
  74 }
  75 
  76 static void panfrost_devfreq_reset(struct panfrost_device *pfdev)
  77 {
  78         ktime_t now = ktime_get();
  79         int i;
  80 
  81         for (i = 0; i < NUM_JOB_SLOTS; i++) {
  82                 pfdev->devfreq.slot[i].busy_time = 0;
  83                 pfdev->devfreq.slot[i].idle_time = 0;
  84                 pfdev->devfreq.slot[i].time_last_update = now;
  85         }
  86 }
  87 
  88 static int panfrost_devfreq_get_dev_status(struct device *dev,
  89                                            struct devfreq_dev_status *status)
  90 {
  91         struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev));
  92         int i;
  93 
  94         for (i = 0; i < NUM_JOB_SLOTS; i++) {
  95                 panfrost_devfreq_update_utilization(pfdev, i);
  96         }
  97 
  98         status->current_frequency = clk_get_rate(pfdev->clock);
  99         status->total_time = ktime_to_ns(ktime_add(pfdev->devfreq.slot[0].busy_time,
 100                                                    pfdev->devfreq.slot[0].idle_time));
 101 
 102         status->busy_time = 0;
 103         for (i = 0; i < NUM_JOB_SLOTS; i++) {
 104                 status->busy_time += ktime_to_ns(pfdev->devfreq.slot[i].busy_time);
 105         }
 106 
 107         /* We're scheduling only to one core atm, so don't divide for now */
 108         /* status->busy_time /= NUM_JOB_SLOTS; */
 109 
 110         panfrost_devfreq_reset(pfdev);
 111 
 112         dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", status->busy_time,
 113                 status->total_time,
 114                 status->busy_time / (status->total_time / 100),
 115                 status->current_frequency / 1000 / 1000);
 116 
 117         return 0;
 118 }
 119 
 120 static int panfrost_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
 121 {
 122         struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev));
 123 
 124         *freq = pfdev->devfreq.cur_freq;
 125 
 126         return 0;
 127 }
 128 
 129 static struct devfreq_dev_profile panfrost_devfreq_profile = {
 130         .polling_ms = 50, /* ~3 frames */
 131         .target = panfrost_devfreq_target,
 132         .get_dev_status = panfrost_devfreq_get_dev_status,
 133         .get_cur_freq = panfrost_devfreq_get_cur_freq,
 134 };
 135 
 136 int panfrost_devfreq_init(struct panfrost_device *pfdev)
 137 {
 138         int ret;
 139         struct dev_pm_opp *opp;
 140 
 141         ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev);
 142         if (ret == -ENODEV) /* Optional, continue without devfreq */
 143                 return 0;
 144         else if (ret)
 145                 return ret;
 146 
 147         panfrost_devfreq_reset(pfdev);
 148 
 149         pfdev->devfreq.cur_freq = clk_get_rate(pfdev->clock);
 150 
 151         opp = devfreq_recommended_opp(&pfdev->pdev->dev, &pfdev->devfreq.cur_freq, 0);
 152         if (IS_ERR(opp))
 153                 return PTR_ERR(opp);
 154 
 155         panfrost_devfreq_profile.initial_freq = pfdev->devfreq.cur_freq;
 156         dev_pm_opp_put(opp);
 157 
 158         pfdev->devfreq.devfreq = devm_devfreq_add_device(&pfdev->pdev->dev,
 159                         &panfrost_devfreq_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND,
 160                         NULL);
 161         if (IS_ERR(pfdev->devfreq.devfreq)) {
 162                 DRM_DEV_ERROR(&pfdev->pdev->dev, "Couldn't initialize GPU devfreq\n");
 163                 ret = PTR_ERR(pfdev->devfreq.devfreq);
 164                 pfdev->devfreq.devfreq = NULL;
 165                 dev_pm_opp_of_remove_table(&pfdev->pdev->dev);
 166                 return ret;
 167         }
 168 
 169         return 0;
 170 }
 171 
 172 void panfrost_devfreq_fini(struct panfrost_device *pfdev)
 173 {
 174         dev_pm_opp_of_remove_table(&pfdev->pdev->dev);
 175 }
 176 
 177 void panfrost_devfreq_resume(struct panfrost_device *pfdev)
 178 {
 179         int i;
 180 
 181         if (!pfdev->devfreq.devfreq)
 182                 return;
 183 
 184         panfrost_devfreq_reset(pfdev);
 185         for (i = 0; i < NUM_JOB_SLOTS; i++)
 186                 pfdev->devfreq.slot[i].busy = false;
 187 
 188         devfreq_resume_device(pfdev->devfreq.devfreq);
 189 }
 190 
 191 void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
 192 {
 193         if (!pfdev->devfreq.devfreq)
 194                 return;
 195 
 196         devfreq_suspend_device(pfdev->devfreq.devfreq);
 197 }
 198 
 199 static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot)
 200 {
 201         struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot];
 202         ktime_t now;
 203         ktime_t last;
 204 
 205         if (!pfdev->devfreq.devfreq)
 206                 return;
 207 
 208         now = ktime_get();
 209         last = pfdev->devfreq.slot[slot].time_last_update;
 210 
 211         /* If we last recorded a transition to busy, we have been idle since */
 212         if (devfreq_slot->busy)
 213                 pfdev->devfreq.slot[slot].busy_time += ktime_sub(now, last);
 214         else
 215                 pfdev->devfreq.slot[slot].idle_time += ktime_sub(now, last);
 216 
 217         pfdev->devfreq.slot[slot].time_last_update = now;
 218 }
 219 
 220 /* The job scheduler is expected to call this at every transition busy <-> idle */
 221 void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot)
 222 {
 223         struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot];
 224 
 225         panfrost_devfreq_update_utilization(pfdev, slot);
 226         devfreq_slot->busy = !devfreq_slot->busy;
 227 }

/* [<][>][^][v][top][bottom][index][help] */