brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · bc8fb4e Raw
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