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

panfrost_devfreq.c (5436B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright 2019 Collabora ltd. */
      3
      4#include <linux/clk.h>
      5#include <linux/devfreq.h>
      6#include <linux/devfreq_cooling.h>
      7#include <linux/platform_device.h>
      8#include <linux/pm_opp.h>
      9
     10#include "panfrost_device.h"
     11#include "panfrost_devfreq.h"
     12
     13static void panfrost_devfreq_update_utilization(struct panfrost_devfreq *pfdevfreq)
     14{
     15	ktime_t now, last;
     16
     17	now = ktime_get();
     18	last = pfdevfreq->time_last_update;
     19
     20	if (pfdevfreq->busy_count > 0)
     21		pfdevfreq->busy_time += ktime_sub(now, last);
     22	else
     23		pfdevfreq->idle_time += ktime_sub(now, last);
     24
     25	pfdevfreq->time_last_update = now;
     26}
     27
     28static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
     29				   u32 flags)
     30{
     31	struct dev_pm_opp *opp;
     32
     33	opp = devfreq_recommended_opp(dev, freq, flags);
     34	if (IS_ERR(opp))
     35		return PTR_ERR(opp);
     36	dev_pm_opp_put(opp);
     37
     38	return dev_pm_opp_set_rate(dev, *freq);
     39}
     40
     41static void panfrost_devfreq_reset(struct panfrost_devfreq *pfdevfreq)
     42{
     43	pfdevfreq->busy_time = 0;
     44	pfdevfreq->idle_time = 0;
     45	pfdevfreq->time_last_update = ktime_get();
     46}
     47
     48static int panfrost_devfreq_get_dev_status(struct device *dev,
     49					   struct devfreq_dev_status *status)
     50{
     51	struct panfrost_device *pfdev = dev_get_drvdata(dev);
     52	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
     53	unsigned long irqflags;
     54
     55	status->current_frequency = clk_get_rate(pfdev->clock);
     56
     57	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
     58
     59	panfrost_devfreq_update_utilization(pfdevfreq);
     60
     61	status->total_time = ktime_to_ns(ktime_add(pfdevfreq->busy_time,
     62						   pfdevfreq->idle_time));
     63
     64	status->busy_time = ktime_to_ns(pfdevfreq->busy_time);
     65
     66	panfrost_devfreq_reset(pfdevfreq);
     67
     68	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
     69
     70	dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
     71		status->busy_time, status->total_time,
     72		status->busy_time / (status->total_time / 100),
     73		status->current_frequency / 1000 / 1000);
     74
     75	return 0;
     76}
     77
     78static struct devfreq_dev_profile panfrost_devfreq_profile = {
     79	.timer = DEVFREQ_TIMER_DELAYED,
     80	.polling_ms = 50, /* ~3 frames */
     81	.target = panfrost_devfreq_target,
     82	.get_dev_status = panfrost_devfreq_get_dev_status,
     83};
     84
     85int panfrost_devfreq_init(struct panfrost_device *pfdev)
     86{
     87	int ret;
     88	struct dev_pm_opp *opp;
     89	unsigned long cur_freq;
     90	struct device *dev = &pfdev->pdev->dev;
     91	struct devfreq *devfreq;
     92	struct thermal_cooling_device *cooling;
     93	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
     94
     95	if (pfdev->comp->num_supplies > 1) {
     96		/*
     97		 * GPUs with more than 1 supply require platform-specific handling:
     98		 * continue without devfreq
     99		 */
    100		DRM_DEV_INFO(dev, "More than 1 supply is not supported yet\n");
    101		return 0;
    102	}
    103
    104	ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names,
    105					 pfdev->comp->num_supplies);
    106	if (ret) {
    107		/* Continue if the optional regulator is missing */
    108		if (ret != -ENODEV) {
    109			if (ret != -EPROBE_DEFER)
    110				DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n");
    111			return ret;
    112		}
    113	}
    114
    115	ret = devm_pm_opp_of_add_table(dev);
    116	if (ret) {
    117		/* Optional, continue without devfreq */
    118		if (ret == -ENODEV)
    119			ret = 0;
    120		return ret;
    121	}
    122	pfdevfreq->opp_of_table_added = true;
    123
    124	spin_lock_init(&pfdevfreq->lock);
    125
    126	panfrost_devfreq_reset(pfdevfreq);
    127
    128	cur_freq = clk_get_rate(pfdev->clock);
    129
    130	opp = devfreq_recommended_opp(dev, &cur_freq, 0);
    131	if (IS_ERR(opp))
    132		return PTR_ERR(opp);
    133
    134	panfrost_devfreq_profile.initial_freq = cur_freq;
    135	dev_pm_opp_put(opp);
    136
    137	/*
    138	 * Setup default thresholds for the simple_ondemand governor.
    139	 * The values are chosen based on experiments.
    140	 */
    141	pfdevfreq->gov_data.upthreshold = 45;
    142	pfdevfreq->gov_data.downdifferential = 5;
    143
    144	devfreq = devm_devfreq_add_device(dev, &panfrost_devfreq_profile,
    145					  DEVFREQ_GOV_SIMPLE_ONDEMAND,
    146					  &pfdevfreq->gov_data);
    147	if (IS_ERR(devfreq)) {
    148		DRM_DEV_ERROR(dev, "Couldn't initialize GPU devfreq\n");
    149		return PTR_ERR(devfreq);
    150	}
    151	pfdevfreq->devfreq = devfreq;
    152
    153	cooling = devfreq_cooling_em_register(devfreq, NULL);
    154	if (IS_ERR(cooling))
    155		DRM_DEV_INFO(dev, "Failed to register cooling device\n");
    156	else
    157		pfdevfreq->cooling = cooling;
    158
    159	return 0;
    160}
    161
    162void panfrost_devfreq_fini(struct panfrost_device *pfdev)
    163{
    164	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
    165
    166	if (pfdevfreq->cooling) {
    167		devfreq_cooling_unregister(pfdevfreq->cooling);
    168		pfdevfreq->cooling = NULL;
    169	}
    170}
    171
    172void panfrost_devfreq_resume(struct panfrost_device *pfdev)
    173{
    174	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
    175
    176	if (!pfdevfreq->devfreq)
    177		return;
    178
    179	panfrost_devfreq_reset(pfdevfreq);
    180
    181	devfreq_resume_device(pfdevfreq->devfreq);
    182}
    183
    184void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
    185{
    186	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
    187
    188	if (!pfdevfreq->devfreq)
    189		return;
    190
    191	devfreq_suspend_device(pfdevfreq->devfreq);
    192}
    193
    194void panfrost_devfreq_record_busy(struct panfrost_devfreq *pfdevfreq)
    195{
    196	unsigned long irqflags;
    197
    198	if (!pfdevfreq->devfreq)
    199		return;
    200
    201	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
    202
    203	panfrost_devfreq_update_utilization(pfdevfreq);
    204
    205	pfdevfreq->busy_count++;
    206
    207	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
    208}
    209
    210void panfrost_devfreq_record_idle(struct panfrost_devfreq *pfdevfreq)
    211{
    212	unsigned long irqflags;
    213
    214	if (!pfdevfreq->devfreq)
    215		return;
    216
    217	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
    218
    219	panfrost_devfreq_update_utilization(pfdevfreq);
    220
    221	WARN_ON(--pfdevfreq->busy_count < 0);
    222
    223	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
    224}