233 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 else29 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 const char *regulator_names[] = { "mali", NULL };115 116 if (!device_property_present(dev, "operating-points-v2"))117 /* Optional, continue without devfreq */118 return 0;119 120 spin_lock_init(&ldevfreq->lock);121 122 /*123 * clkname is set separately so it is not affected by the optional124 * regulator setting which may return error.125 */126 ret = devm_pm_opp_set_clkname(dev, "core");127 if (ret)128 return ret;129 130 ret = devm_pm_opp_set_regulators(dev, regulator_names);131 if (ret) {132 /* Continue if the optional regulator is missing */133 if (ret != -ENODEV)134 return ret;135 }136 137 ret = devm_pm_opp_of_add_table(dev);138 if (ret)139 return ret;140 141 lima_devfreq_reset(ldevfreq);142 143 cur_freq = clk_get_rate(ldev->clk_gpu);144 145 opp = devfreq_recommended_opp(dev, &cur_freq, 0);146 if (IS_ERR(opp))147 return PTR_ERR(opp);148 149 lima_devfreq_profile.initial_freq = cur_freq;150 dev_pm_opp_put(opp);151 152 /*153 * Setup default thresholds for the simple_ondemand governor.154 * The values are chosen based on experiments.155 */156 ldevfreq->gov_data.upthreshold = 30;157 ldevfreq->gov_data.downdifferential = 5;158 159 devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,160 DEVFREQ_GOV_SIMPLE_ONDEMAND,161 &ldevfreq->gov_data);162 if (IS_ERR(devfreq)) {163 dev_err(dev, "Couldn't initialize GPU devfreq\n");164 return PTR_ERR(devfreq);165 }166 167 ldevfreq->devfreq = devfreq;168 169 cooling = of_devfreq_cooling_register(dev->of_node, devfreq);170 if (IS_ERR(cooling))171 dev_info(dev, "Failed to register cooling device\n");172 else173 ldevfreq->cooling = cooling;174 175 return 0;176}177 178void lima_devfreq_record_busy(struct lima_devfreq *devfreq)179{180 unsigned long irqflags;181 182 if (!devfreq->devfreq)183 return;184 185 spin_lock_irqsave(&devfreq->lock, irqflags);186 187 lima_devfreq_update_utilization(devfreq);188 189 devfreq->busy_count++;190 191 spin_unlock_irqrestore(&devfreq->lock, irqflags);192}193 194void lima_devfreq_record_idle(struct lima_devfreq *devfreq)195{196 unsigned long irqflags;197 198 if (!devfreq->devfreq)199 return;200 201 spin_lock_irqsave(&devfreq->lock, irqflags);202 203 lima_devfreq_update_utilization(devfreq);204 205 WARN_ON(--devfreq->busy_count < 0);206 207 spin_unlock_irqrestore(&devfreq->lock, irqflags);208}209 210int lima_devfreq_resume(struct lima_devfreq *devfreq)211{212 unsigned long irqflags;213 214 if (!devfreq->devfreq)215 return 0;216 217 spin_lock_irqsave(&devfreq->lock, irqflags);218 219 lima_devfreq_reset(devfreq);220 221 spin_unlock_irqrestore(&devfreq->lock, irqflags);222 223 return devfreq_resume_device(devfreq->devfreq);224}225 226int lima_devfreq_suspend(struct lima_devfreq *devfreq)227{228 if (!devfreq->devfreq)229 return 0;230 231 return devfreq_suspend_device(devfreq->devfreq);232}233