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        .timer = DEVFREQ_TIMER_DELAYED,
  85        .polling_ms = 50, /* ~3 frames */
  86        .target = lima_devfreq_target,
  87        .get_dev_status = lima_devfreq_get_dev_status,
  88};
  89
  90void lima_devfreq_fini(struct lima_device *ldev)
  91{
  92        struct lima_devfreq *devfreq = &ldev->devfreq;
  93
  94        if (devfreq->cooling) {
  95                devfreq_cooling_unregister(devfreq->cooling);
  96                devfreq->cooling = NULL;
  97        }
  98
  99        if (devfreq->devfreq) {
 100                devm_devfreq_remove_device(ldev->dev, devfreq->devfreq);
 101                devfreq->devfreq = NULL;
 102        }
 103}
 104
 105int lima_devfreq_init(struct lima_device *ldev)
 106{
 107        struct thermal_cooling_device *cooling;
 108        struct device *dev = ldev->dev;
 109        struct devfreq *devfreq;
 110        struct lima_devfreq *ldevfreq = &ldev->devfreq;
 111        struct dev_pm_opp *opp;
 112        unsigned long cur_freq;
 113        int ret;
 114
 115        if (!device_property_present(dev, "operating-points-v2"))
 116                /* Optional, continue without devfreq */
 117                return 0;
 118
 119        spin_lock_init(&ldevfreq->lock);
 120
 121        ret = devm_pm_opp_set_clkname(dev, "core");
 122        if (ret)
 123                return ret;
 124
 125        ret = devm_pm_opp_set_regulators(dev, (const char *[]){ "mali" }, 1);
 126        if (ret) {
 127                /* Continue if the optional regulator is missing */
 128                if (ret != -ENODEV)
 129                        return ret;
 130        }
 131
 132        ret = devm_pm_opp_of_add_table(dev);
 133        if (ret)
 134                return ret;
 135
 136        lima_devfreq_reset(ldevfreq);
 137
 138        cur_freq = clk_get_rate(ldev->clk_gpu);
 139
 140        opp = devfreq_recommended_opp(dev, &cur_freq, 0);
 141        if (IS_ERR(opp))
 142                return PTR_ERR(opp);
 143
 144        lima_devfreq_profile.initial_freq = cur_freq;
 145        dev_pm_opp_put(opp);
 146
 147        /*
 148         * Setup default thresholds for the simple_ondemand governor.
 149         * The values are chosen based on experiments.
 150         */
 151        ldevfreq->gov_data.upthreshold = 30;
 152        ldevfreq->gov_data.downdifferential = 5;
 153
 154        devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
 155                                          DEVFREQ_GOV_SIMPLE_ONDEMAND,
 156                                          &ldevfreq->gov_data);
 157        if (IS_ERR(devfreq)) {
 158                dev_err(dev, "Couldn't initialize GPU devfreq\n");
 159                return PTR_ERR(devfreq);
 160        }
 161
 162        ldevfreq->devfreq = devfreq;
 163
 164        cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
 165        if (IS_ERR(cooling))
 166                dev_info(dev, "Failed to register cooling device\n");
 167        else
 168                ldevfreq->cooling = cooling;
 169
 170        return 0;
 171}
 172
 173void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
 174{
 175        unsigned long irqflags;
 176
 177        if (!devfreq->devfreq)
 178                return;
 179
 180        spin_lock_irqsave(&devfreq->lock, irqflags);
 181
 182        lima_devfreq_update_utilization(devfreq);
 183
 184        devfreq->busy_count++;
 185
 186        spin_unlock_irqrestore(&devfreq->lock, irqflags);
 187}
 188
 189void lima_devfreq_record_idle(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        WARN_ON(--devfreq->busy_count < 0);
 201
 202        spin_unlock_irqrestore(&devfreq->lock, irqflags);
 203}
 204
 205int lima_devfreq_resume(struct lima_devfreq *devfreq)
 206{
 207        unsigned long irqflags;
 208
 209        if (!devfreq->devfreq)
 210                return 0;
 211
 212        spin_lock_irqsave(&devfreq->lock, irqflags);
 213
 214        lima_devfreq_reset(devfreq);
 215
 216        spin_unlock_irqrestore(&devfreq->lock, irqflags);
 217
 218        return devfreq_resume_device(devfreq->devfreq);
 219}
 220
 221int lima_devfreq_suspend(struct lima_devfreq *devfreq)
 222{
 223        if (!devfreq->devfreq)
 224                return 0;
 225
 226        return devfreq_suspend_device(devfreq->devfreq);
 227}
 228