2018-09-10 14:27:58 -05:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 Collabora ltd. */
2020-07-10 11:53:57 +02:00
# include <linux/clk.h>
2018-09-10 14:27:58 -05:00
# include <linux/devfreq.h>
2019-11-28 20:54:27 +00:00
# include <linux/devfreq_cooling.h>
2018-09-10 14:27:58 -05:00
# include <linux/platform_device.h>
# include <linux/pm_opp.h>
# include "panfrost_device.h"
2019-04-17 15:47:49 +01:00
# include "panfrost_devfreq.h"
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
static void panfrost_devfreq_update_utilization ( struct panfrost_devfreq * pfdevfreq )
2020-07-10 11:53:56 +02:00
{
2020-07-10 11:54:00 +02:00
ktime_t now , last ;
2020-07-10 11:53:56 +02:00
now = ktime_get ( ) ;
2020-07-10 11:53:59 +02:00
last = pfdevfreq - > time_last_update ;
2020-07-10 11:53:56 +02:00
2020-07-10 11:54:00 +02:00
if ( pfdevfreq - > busy_count > 0 )
2020-07-10 11:53:59 +02:00
pfdevfreq - > busy_time + = ktime_sub ( now , last ) ;
2020-07-10 11:53:56 +02:00
else
2020-07-10 11:53:59 +02:00
pfdevfreq - > idle_time + = ktime_sub ( now , last ) ;
2020-07-10 11:53:56 +02:00
2020-07-10 11:53:59 +02:00
pfdevfreq - > time_last_update = now ;
2020-07-10 11:53:56 +02:00
}
2018-09-10 14:27:58 -05:00
static int panfrost_devfreq_target ( struct device * dev , unsigned long * freq ,
u32 flags )
{
2019-11-18 17:30:02 +00:00
struct dev_pm_opp * opp ;
2018-09-10 14:27:58 -05:00
2019-11-18 17:30:02 +00:00
opp = devfreq_recommended_opp ( dev , freq , flags ) ;
if ( IS_ERR ( opp ) )
return PTR_ERR ( opp ) ;
dev_pm_opp_put ( opp ) ;
2020-09-21 21:10:21 +08:00
return dev_pm_opp_set_rate ( dev , * freq ) ;
2018-09-10 14:27:58 -05:00
}
2020-07-10 11:53:59 +02:00
static void panfrost_devfreq_reset ( struct panfrost_devfreq * pfdevfreq )
2018-09-10 14:27:58 -05:00
{
2020-07-10 11:53:59 +02:00
pfdevfreq - > busy_time = 0 ;
pfdevfreq - > idle_time = 0 ;
pfdevfreq - > time_last_update = ktime_get ( ) ;
2018-09-10 14:27:58 -05:00
}
static int panfrost_devfreq_get_dev_status ( struct device * dev ,
struct devfreq_dev_status * status )
{
2019-10-25 14:41:42 +01:00
struct panfrost_device * pfdev = dev_get_drvdata ( dev ) ;
2020-07-10 11:53:59 +02:00
struct panfrost_devfreq * pfdevfreq = & pfdev - > pfdevfreq ;
2020-07-10 11:54:00 +02:00
unsigned long irqflags ;
status - > current_frequency = clk_get_rate ( pfdev - > clock ) ;
spin_lock_irqsave ( & pfdevfreq - > lock , irqflags ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
panfrost_devfreq_update_utilization ( pfdevfreq ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
status - > total_time = ktime_to_ns ( ktime_add ( pfdevfreq - > busy_time ,
pfdevfreq - > idle_time ) ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
status - > busy_time = ktime_to_ns ( pfdevfreq - > busy_time ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
panfrost_devfreq_reset ( pfdevfreq ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:54:00 +02:00
spin_unlock_irqrestore ( & pfdevfreq - > lock , irqflags ) ;
2020-07-10 11:53:59 +02:00
dev_dbg ( pfdev - > dev , " busy %lu total %lu %lu %% freq %lu MHz \n " ,
status - > busy_time , status - > total_time ,
2018-09-10 14:27:58 -05:00
status - > busy_time / ( status - > total_time / 100 ) ,
status - > current_frequency / 1000 / 1000 ) ;
return 0 ;
}
static struct devfreq_dev_profile panfrost_devfreq_profile = {
2021-01-05 16:41:11 +00:00
. timer = DEVFREQ_TIMER_DELAYED ,
2018-09-10 14:27:58 -05:00
. 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 ;
2019-10-25 14:41:42 +01:00
unsigned long cur_freq ;
2019-11-28 20:54:27 +00:00
struct device * dev = & pfdev - > pdev - > dev ;
struct devfreq * devfreq ;
struct thermal_cooling_device * cooling ;
2020-07-10 11:53:59 +02:00
struct panfrost_devfreq * pfdevfreq = & pfdev - > pfdevfreq ;
2018-09-10 14:27:58 -05:00
2021-04-21 13:28:54 +08:00
if ( pfdev - > comp - > num_supplies > 1 ) {
/*
* GPUs with more than 1 supply require platform - specific handling :
* continue without devfreq
*/
DRM_DEV_INFO ( dev , " More than 1 supply is not supported yet \n " ) ;
return 0 ;
}
2021-03-14 19:34:06 +03:00
ret = devm_pm_opp_set_regulators ( dev , pfdev - > comp - > supply_names ,
pfdev - > comp - > num_supplies ) ;
if ( ret ) {
2020-07-10 11:54:05 +02:00
/* Continue if the optional regulator is missing */
if ( ret ! = - ENODEV ) {
DRM_DEV_ERROR ( dev , " Couldn't set OPP regulators \n " ) ;
2021-03-14 19:34:06 +03:00
return ret ;
2020-07-10 11:54:05 +02:00
}
}
2021-03-14 19:34:06 +03:00
ret = devm_pm_opp_of_add_table ( dev ) ;
2020-07-10 11:54:05 +02:00
if ( ret ) {
/* Optional, continue without devfreq */
if ( ret = = - ENODEV )
ret = 0 ;
2021-03-14 19:34:06 +03:00
return ret ;
2020-07-10 11:54:05 +02:00
}
2020-07-10 11:54:01 +02:00
pfdevfreq - > opp_of_table_added = true ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:54:00 +02:00
spin_lock_init ( & pfdevfreq - > lock ) ;
2020-07-10 11:53:59 +02:00
panfrost_devfreq_reset ( pfdevfreq ) ;
2018-09-10 14:27:58 -05:00
2019-10-25 14:41:42 +01:00
cur_freq = clk_get_rate ( pfdev - > clock ) ;
2018-09-10 14:27:58 -05:00
2019-11-28 20:54:27 +00:00
opp = devfreq_recommended_opp ( dev , & cur_freq , 0 ) ;
2021-03-14 19:34:06 +03:00
if ( IS_ERR ( opp ) )
return PTR_ERR ( opp ) ;
2018-09-10 14:27:58 -05:00
2019-10-25 14:41:42 +01:00
panfrost_devfreq_profile . initial_freq = cur_freq ;
2018-09-10 14:27:58 -05:00
dev_pm_opp_put ( opp ) ;
2021-01-21 17:04:45 +00:00
/*
* Setup default thresholds for the simple_ondemand governor .
* The values are chosen based on experiments .
*/
pfdevfreq - > gov_data . upthreshold = 45 ;
pfdevfreq - > gov_data . downdifferential = 5 ;
2019-11-28 20:54:27 +00:00
devfreq = devm_devfreq_add_device ( dev , & panfrost_devfreq_profile ,
2021-01-21 17:04:45 +00:00
DEVFREQ_GOV_SIMPLE_ONDEMAND ,
& pfdevfreq - > gov_data ) ;
2019-11-28 20:54:27 +00:00
if ( IS_ERR ( devfreq ) ) {
DRM_DEV_ERROR ( dev , " Couldn't initialize GPU devfreq \n " ) ;
2021-03-14 19:34:06 +03:00
return PTR_ERR ( devfreq ) ;
2018-09-10 14:27:58 -05:00
}
2020-07-10 11:53:59 +02:00
pfdevfreq - > devfreq = devfreq ;
2019-11-28 20:54:27 +00:00
2020-12-10 14:30:14 +00:00
cooling = devfreq_cooling_em_register ( devfreq , NULL ) ;
2019-11-28 20:54:27 +00:00
if ( IS_ERR ( cooling ) )
DRM_DEV_INFO ( dev , " Failed to register cooling device \n " ) ;
else
2020-07-10 11:53:59 +02:00
pfdevfreq - > cooling = cooling ;
2018-09-10 14:27:58 -05:00
return 0 ;
}
2019-08-16 10:31:07 +01:00
void panfrost_devfreq_fini ( struct panfrost_device * pfdev )
{
2020-07-10 11:53:59 +02:00
struct panfrost_devfreq * pfdevfreq = & pfdev - > pfdevfreq ;
2020-07-10 11:54:01 +02:00
if ( pfdevfreq - > cooling ) {
2020-07-10 11:53:59 +02:00
devfreq_cooling_unregister ( pfdevfreq - > cooling ) ;
2020-07-10 11:54:01 +02:00
pfdevfreq - > cooling = NULL ;
}
2019-08-16 10:31:07 +01:00
}
2018-09-10 14:27:58 -05:00
void panfrost_devfreq_resume ( struct panfrost_device * pfdev )
{
2020-07-10 11:53:59 +02:00
struct panfrost_devfreq * pfdevfreq = & pfdev - > pfdevfreq ;
if ( ! pfdevfreq - > devfreq )
2019-06-05 17:02:33 +02:00
return ;
2020-07-10 11:53:59 +02:00
panfrost_devfreq_reset ( pfdevfreq ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
devfreq_resume_device ( pfdevfreq - > devfreq ) ;
2018-09-10 14:27:58 -05:00
}
void panfrost_devfreq_suspend ( struct panfrost_device * pfdev )
{
2020-07-10 11:53:59 +02:00
struct panfrost_devfreq * pfdevfreq = & pfdev - > pfdevfreq ;
if ( ! pfdevfreq - > devfreq )
2019-06-05 17:02:33 +02:00
return ;
2020-07-10 11:53:59 +02:00
devfreq_suspend_device ( pfdevfreq - > devfreq ) ;
2018-09-10 14:27:58 -05:00
}
2020-07-10 11:53:59 +02:00
void panfrost_devfreq_record_busy ( struct panfrost_devfreq * pfdevfreq )
2019-10-25 14:41:43 +01:00
{
2020-07-10 11:54:00 +02:00
unsigned long irqflags ;
if ( ! pfdevfreq - > devfreq )
return ;
spin_lock_irqsave ( & pfdevfreq - > lock , irqflags ) ;
2020-07-10 11:53:59 +02:00
panfrost_devfreq_update_utilization ( pfdevfreq ) ;
2020-07-10 11:54:00 +02:00
pfdevfreq - > busy_count + + ;
spin_unlock_irqrestore ( & pfdevfreq - > lock , irqflags ) ;
2018-09-10 14:27:58 -05:00
}
2020-07-10 11:53:59 +02:00
void panfrost_devfreq_record_idle ( struct panfrost_devfreq * pfdevfreq )
2018-09-10 14:27:58 -05:00
{
2020-07-10 11:54:00 +02:00
unsigned long irqflags ;
if ( ! pfdevfreq - > devfreq )
return ;
spin_lock_irqsave ( & pfdevfreq - > lock , irqflags ) ;
2018-09-10 14:27:58 -05:00
2020-07-10 11:53:59 +02:00
panfrost_devfreq_update_utilization ( pfdevfreq ) ;
2020-07-10 11:54:00 +02:00
WARN_ON ( - - pfdevfreq - > busy_count < 0 ) ;
spin_unlock_irqrestore ( & pfdevfreq - > lock , irqflags ) ;
2018-09-10 14:27:58 -05:00
}