linux/drivers/gpu/drm/lima/lima_devfreq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
   4 *
   5 * Based on panfrost_devfreq.c:
   6 *   Copyright 2019 Collabora ltd.
   7 */
   8#include <linux/clk.h>
   9#include <linux/devfreq.h>
  10#include <linux/devfreq_cooling.h>
  11#include <linux/device.h>
  12#include <linux/platform_device.h>
  13#include <linux/pm_opp.h>
  14#include <linux/property.h>
  15
  16#include "lima_device.h"
  17#include "lima_devfreq.h"
  18
  19static void lima_devfreq_update_utilization(struct lima_devfreq *devfreq)
  20{
  21        ktime_t now, last;
  22
  23        now = ktime_get();
  24        last = devfreq->time_last_update;
  25
  26        if (devfreq->busy_count > 0)
  27                devfreq->busy_time += ktime_sub(now, last);
  28        else
  29                devfreq->idle_time += ktime_sub(now, last);
  30
  31        devfreq->time_last_update = now;
  32}
  33
  34static int lima_devfreq_target(struct device *dev, unsigned long *freq,
  35                               u32 flags)
  36{
  37        struct dev_pm_opp *opp;
  38
  39        opp = devfreq_recommended_opp(dev, freq, flags);
  40        if (IS_ERR(opp))
  41                return PTR_ERR(opp);
  42        dev_pm_opp_put(opp);
  43
  44        return dev_pm_opp_set_rate(dev, *freq);
  45}
  46
  47static void lima_devfreq_reset(struct lima_devfreq *devfreq)
  48{
  49        devfreq->busy_time = 0;
  50        devfreq->idle_time = 0;
  51        devfreq->time_last_update = ktime_get();
  52}
  53
  54static int lima_devfreq_get_dev_status(struct device *dev,
  55                                       struct devfreq_dev_status *status)
  56{
  57        struct lima_device *ldev = dev_get_drvdata(dev);
  58        struct lima_devfreq *devfreq = &ldev->devfreq;
  59        unsigned long irqflags;
  60
  61        status->current_frequency = clk_get_rate(ldev->clk_gpu);
  62
  63        spin_lock_irqsave(&devfreq->lock, irqflags);
  64
  65        lima_devfreq_update_utilization(devfreq);
  66
  67        status->total_time = ktime_to_ns(ktime_add(devfreq->busy_time,
  68                                                   devfreq->idle_time));
  69        status->busy_time = ktime_to_ns(devfreq->busy_time);
  70
  71        lima_devfreq_reset(devfreq);
  72
  73        spin_unlock_irqrestore(&devfreq->lock, irqflags);
  74
  75        dev_dbg(ldev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
  76                status->busy_time, status->total_time,
  77                status->busy_time / (status->total_time / 100),
  78                status->current_frequency / 1000 / 1000);
  79
  80        return 0;
  81}
  82
  83static struct devfreq_dev_profile lima_devfreq_profile = {
  84        .polling_ms = 50, /* ~3 frames */
  85        .target = lima_devfreq_target,
  86        .get_dev_status = lima_devfreq_get_dev_status,
  87};
  88
  89void lima_devfreq_fini(struct lima_device *ldev)
  90{
  91        struct lima_devfreq *devfreq = &ldev->devfreq;
  92
  93        if (devfreq->cooling) {
  94                devfreq_cooling_unregister(devfreq->cooling);
  95                devfreq->cooling = NULL;
  96        }
  97
  98        if (devfreq->devfreq) {
  99                devm_devfreq_remove_device(ldev->dev, devfreq->devfreq);
 100                devfreq->devfreq = NULL;
 101        }
 102
 103        dev_pm_opp_of_remove_table(ldev->dev);
 104
 105        dev_pm_opp_put_regulators(devfreq->regulators_opp_table);
 106        dev_pm_opp_put_clkname(devfreq->clkname_opp_table);
 107        devfreq->regulators_opp_table = NULL;
 108        devfreq->clkname_opp_table = NULL;
 109}
 110
 111int lima_devfreq_init(struct lima_device *ldev)
 112{
 113        struct thermal_cooling_device *cooling;
 114        struct device *dev = ldev->dev;
 115        struct opp_table *opp_table;
 116        struct devfreq *devfreq;
 117        struct lima_devfreq *ldevfreq = &ldev->devfreq;
 118        struct dev_pm_opp *opp;
 119        unsigned long cur_freq;
 120        int ret;
 121
 122        if (!device_property_present(dev, "operating-points-v2"))
 123                /* Optional, continue without devfreq */
 124                return 0;
 125
 126        spin_lock_init(&ldevfreq->lock);
 127
 128        opp_table = dev_pm_opp_set_clkname(dev, "core");
 129        if (IS_ERR(opp_table)) {
 130                ret = PTR_ERR(opp_table);
 131                goto err_fini;
 132        }
 133
 134        ldevfreq->clkname_opp_table = opp_table;
 135
 136        opp_table = dev_pm_opp_set_regulators(dev,
 137                                              (const char *[]){ "mali" },
 138                                              1);
 139        if (IS_ERR(opp_table)) {
 140                ret = PTR_ERR(opp_table);
 141
 142                /* Continue if the optional regulator is missing */
 143                if (ret != -ENODEV)
 144                        goto err_fini;
 145        } else {
 146                ldevfreq->regulators_opp_table = opp_table;
 147        }
 148
 149        ret = dev_pm_opp_of_add_table(dev);
 150        if (ret)
 151                goto err_fini;
 152
 153        lima_devfreq_reset(ldevfreq);
 154
 155        cur_freq = clk_get_rate(ldev->clk_gpu);
 156
 157        opp = devfreq_recommended_opp(dev, &cur_freq, 0);
 158        if (IS_ERR(opp)) {
 159                ret = PTR_ERR(opp);
 160                goto err_fini;
 161        }
 162
 163        lima_devfreq_profile.initial_freq = cur_freq;
 164        dev_pm_opp_put(opp);
 165
 166        devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
 167                                          DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
 168        if (IS_ERR(devfreq)) {
 169                dev_err(dev, "Couldn't initialize GPU devfreq\n");
 170                ret = PTR_ERR(devfreq);
 171                goto err_fini;
 172        }
 173
 174        ldevfreq->devfreq = devfreq;
 175
 176        cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
 177        if (IS_ERR(cooling))
 178                dev_info(dev, "Failed to register cooling device\n");
 179        else
 180                ldevfreq->cooling = cooling;
 181
 182        return 0;
 183
 184err_fini:
 185        lima_devfreq_fini(ldev);
 186        return ret;
 187}
 188
 189void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
 190{
 191        unsigned long irqflags;
 192
 193        if (!devfreq->devfreq)
 194                return;
 195
 196        spin_lock_irqsave(&devfreq->lock, irqflags);
 197
 198        lima_devfreq_update_utilization(devfreq);
 199
 200        devfreq->busy_count++;
 201
 202        spin_unlock_irqrestore(&devfreq->lock, irqflags);
 203}
 204
 205void lima_devfreq_record_idle(struct lima_devfreq *devfreq)
 206{
 207        unsigned long irqflags;
 208
 209        if (!devfreq->devfreq)
 210                return;
 211
 212        spin_lock_irqsave(&devfreq->lock, irqflags);
 213
 214        lima_devfreq_update_utilization(devfreq);
 215
 216        WARN_ON(--devfreq->busy_count < 0);
 217
 218        spin_unlock_irqrestore(&devfreq->lock, irqflags);
 219}
 220
 221int lima_devfreq_resume(struct lima_devfreq *devfreq)
 222{
 223        unsigned long irqflags;
 224
 225        if (!devfreq->devfreq)
 226                return 0;
 227
 228        spin_lock_irqsave(&devfreq->lock, irqflags);
 229
 230        lima_devfreq_reset(devfreq);
 231
 232        spin_unlock_irqrestore(&devfreq->lock, irqflags);
 233
 234        return devfreq_resume_device(devfreq->devfreq);
 235}
 236
 237int lima_devfreq_suspend(struct lima_devfreq *devfreq)
 238{
 239        if (!devfreq->devfreq)
 240                return 0;
 241
 242        return devfreq_suspend_device(devfreq->devfreq);
 243}
 244