linux/drivers/gpu/drm/panfrost/panfrost_devfreq.c
Linus Torvalds b109bc7229 Merge tag 'thermal-v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux
Pull thermal updates from Daniel Lezcano:

 - Add upper and lower limits clamps for the cooling device state in the
   power allocator governor (Michael Kao)

 - Add upper and lower limits support for the power allocator governor
   (Lukasz Luba)

 - Optimize conditions testing for the trip points (Bernard Zhao)

 - Replace spin_lock_irqsave by spin_lock in hard IRQ on the rcar driver
   (Tian Tao)

 - Add MT8516 dt-bindings and device reset optional support (Fabien
   Parent)

 - Add a quiescent period to cool down the PCH when entering S0iX
   (Sumeet Pawnikar)

 - Use bitmap API instead of re-inventing the wheel on sun8i (Yangtao
   Li)

 - Remove useless NULL check in the hwmon driver (Bernard Zhao)

 - Update the current state in the cpufreq cooling device only if the
   frequency change is effective (Zhuguangqing)

 - Improve the schema validation for the rcar DT bindings (Geert
   Uytterhoeven)

 - Fix the user time unit in the documentation (Viresh Kumar)

 - Add PCI ids for Lewisburg PCH (Andres Freund)

 - Add hwmon support on amlogic (Martin Blumenstingl)

 - Fix build failure for PCH entering on in S0iX (Randy Dunlap)

 - Improve the k_* coefficient for the power allocator governor (Lukasz
   Luba)

 - Fix missing const on a sysfs attribute (Rikard Falkeborn)

 - Remove broken interrupt support on rcar to be replaced by a new one
   (Niklas Söderlund)

 - Improve the error code handling at init time on imx8mm (Fabio
   Estevam)

 - Compute interval validity once instead at each temperature reading
   iteration on acerhdf (Daniel Lezcano)

 - Add r8a779a0 support (Niklas Söderlund)

 - Add PCI ids for AlderLake PCH and mmio refactoring (Srinivas
   Pandruvada)

 - Add RFIM and mailbox support on int340x (Srinivas Pandruvada)

 - Use macro for temperature calculation on PCH (Sumeet Pawnikar)

 - Simplify return conditions at probe time on Broadcom (Zheng Yongjun)

 - Fix workload name on PCH (Srinivas Pandruvada)

 - Migrate the devfreq cooling device code to the energy model API
   (Lukasz Luba)

 - Emit a warning if the thermal_zone_device_update is called without
   the .get_temp() ops (Daniel Lezcano)

 - Add critical and hot ops for the thermal zone (Daniel Lezcano)

 - Remove notification usage when critical is reached on rcar (Daniel
   Lezcano)

 - Fix devfreq build when ENERGY_MODEL is not set (Lukasz Luba)

* tag 'thermal-v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (45 commits)
  thermal/drivers/devfreq_cooling: Fix the build when !ENERGY_MODEL
  thermal/drivers/rcar: Remove notification usage
  thermal/core: Add critical and hot ops
  thermal/core: Emit a warning if the thermal zone is updated without ops
  drm/panfrost: Register devfreq cooling and attempt to add Energy Model
  thermal: devfreq_cooling: remove old power model and use EM
  thermal: devfreq_cooling: add new registration functions with Energy Model
  thermal: devfreq_cooling: use a copy of device status
  thermal: devfreq_cooling: change tracing function and arguments
  thermal: int340x: processor_thermal: Correct workload type name
  thermal: broadcom: simplify the return expression of bcm2711_thermal_probe()
  thermal: intel: pch: use macro for temperature calculation
  thermal: int340x: processor_thermal: Add mailbox driver
  thermal: int340x: processor_thermal: Add RFIM driver
  thermal: int340x: processor_thermal: Add AlderLake PCI device id
  thermal: int340x: processor_thermal: Refactor MMIO interface
  thermal: rcar_gen3_thermal: Add r8a779a0 support
  dt-bindings: thermal: rcar-gen3-thermal: Add r8a779a0 support
  platform/x86/drivers/acerhdf: Check the interval value when it is set
  platform/x86/drivers/acerhdf: Use module_param_cb to set/get polling interval
  ...
2020-12-15 16:21:37 -08:00

227 lines
5.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Collabora ltd. */
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/devfreq_cooling.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
static void panfrost_devfreq_update_utilization(struct panfrost_devfreq *pfdevfreq)
{
ktime_t now, last;
now = ktime_get();
last = pfdevfreq->time_last_update;
if (pfdevfreq->busy_count > 0)
pfdevfreq->busy_time += ktime_sub(now, last);
else
pfdevfreq->idle_time += ktime_sub(now, last);
pfdevfreq->time_last_update = now;
}
static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct dev_pm_opp *opp;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp))
return PTR_ERR(opp);
dev_pm_opp_put(opp);
return dev_pm_opp_set_rate(dev, *freq);
}
static void panfrost_devfreq_reset(struct panfrost_devfreq *pfdevfreq)
{
pfdevfreq->busy_time = 0;
pfdevfreq->idle_time = 0;
pfdevfreq->time_last_update = ktime_get();
}
static int panfrost_devfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *status)
{
struct panfrost_device *pfdev = dev_get_drvdata(dev);
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
unsigned long irqflags;
status->current_frequency = clk_get_rate(pfdev->clock);
spin_lock_irqsave(&pfdevfreq->lock, irqflags);
panfrost_devfreq_update_utilization(pfdevfreq);
status->total_time = ktime_to_ns(ktime_add(pfdevfreq->busy_time,
pfdevfreq->idle_time));
status->busy_time = ktime_to_ns(pfdevfreq->busy_time);
panfrost_devfreq_reset(pfdevfreq);
spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
status->busy_time, status->total_time,
status->busy_time / (status->total_time / 100),
status->current_frequency / 1000 / 1000);
return 0;
}
static struct devfreq_dev_profile panfrost_devfreq_profile = {
.polling_ms = 50, /* ~3 frames */
.target = panfrost_devfreq_target,
.get_dev_status = panfrost_devfreq_get_dev_status,
};
int panfrost_devfreq_init(struct panfrost_device *pfdev)
{
int ret;
struct dev_pm_opp *opp;
unsigned long cur_freq;
struct device *dev = &pfdev->pdev->dev;
struct devfreq *devfreq;
struct opp_table *opp_table;
struct thermal_cooling_device *cooling;
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
opp_table = dev_pm_opp_set_regulators(dev, pfdev->comp->supply_names,
pfdev->comp->num_supplies);
if (IS_ERR(opp_table)) {
ret = PTR_ERR(opp_table);
/* Continue if the optional regulator is missing */
if (ret != -ENODEV) {
DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n");
goto err_fini;
}
} else {
pfdevfreq->regulators_opp_table = opp_table;
}
ret = dev_pm_opp_of_add_table(dev);
if (ret) {
/* Optional, continue without devfreq */
if (ret == -ENODEV)
ret = 0;
goto err_fini;
}
pfdevfreq->opp_of_table_added = true;
spin_lock_init(&pfdevfreq->lock);
panfrost_devfreq_reset(pfdevfreq);
cur_freq = clk_get_rate(pfdev->clock);
opp = devfreq_recommended_opp(dev, &cur_freq, 0);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
goto err_fini;
}
panfrost_devfreq_profile.initial_freq = cur_freq;
dev_pm_opp_put(opp);
devfreq = devm_devfreq_add_device(dev, &panfrost_devfreq_profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
if (IS_ERR(devfreq)) {
DRM_DEV_ERROR(dev, "Couldn't initialize GPU devfreq\n");
ret = PTR_ERR(devfreq);
goto err_fini;
}
pfdevfreq->devfreq = devfreq;
cooling = devfreq_cooling_em_register(devfreq, NULL);
if (IS_ERR(cooling))
DRM_DEV_INFO(dev, "Failed to register cooling device\n");
else
pfdevfreq->cooling = cooling;
return 0;
err_fini:
panfrost_devfreq_fini(pfdev);
return ret;
}
void panfrost_devfreq_fini(struct panfrost_device *pfdev)
{
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
if (pfdevfreq->cooling) {
devfreq_cooling_unregister(pfdevfreq->cooling);
pfdevfreq->cooling = NULL;
}
if (pfdevfreq->opp_of_table_added) {
dev_pm_opp_of_remove_table(&pfdev->pdev->dev);
pfdevfreq->opp_of_table_added = false;
}
if (pfdevfreq->regulators_opp_table) {
dev_pm_opp_put_regulators(pfdevfreq->regulators_opp_table);
pfdevfreq->regulators_opp_table = NULL;
}
}
void panfrost_devfreq_resume(struct panfrost_device *pfdev)
{
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
if (!pfdevfreq->devfreq)
return;
panfrost_devfreq_reset(pfdevfreq);
devfreq_resume_device(pfdevfreq->devfreq);
}
void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
{
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
if (!pfdevfreq->devfreq)
return;
devfreq_suspend_device(pfdevfreq->devfreq);
}
void panfrost_devfreq_record_busy(struct panfrost_devfreq *pfdevfreq)
{
unsigned long irqflags;
if (!pfdevfreq->devfreq)
return;
spin_lock_irqsave(&pfdevfreq->lock, irqflags);
panfrost_devfreq_update_utilization(pfdevfreq);
pfdevfreq->busy_count++;
spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
}
void panfrost_devfreq_record_idle(struct panfrost_devfreq *pfdevfreq)
{
unsigned long irqflags;
if (!pfdevfreq->devfreq)
return;
spin_lock_irqsave(&pfdevfreq->lock, irqflags);
panfrost_devfreq_update_utilization(pfdevfreq);
WARN_ON(--pfdevfreq->busy_count < 0);
spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
}