cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lima_devfreq.c (5222B)


      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}