2022-06-28 08:34:11 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-03-22 07:44:03 +03:00
/*
* linux / drivers / devfreq / governor_passive . c
*
* Copyright ( C ) 2016 Samsung Electronics
* Author : Chanwoo Choi < cw00 . choi @ samsung . com >
* Author : MyungJoo Ham < myungjoo . ham @ samsung . com >
*/
# include <linux/module.h>
2021-03-02 09:58:21 +03:00
# include <linux/cpu.h>
# include <linux/cpufreq.h>
# include <linux/cpumask.h>
# include <linux/slab.h>
2016-03-22 07:44:03 +03:00
# include <linux/device.h>
# include <linux/devfreq.h>
2022-06-10 10:54:28 +03:00
# include <linux/units.h>
2016-03-22 07:44:03 +03:00
# include "governor.h"
2022-04-26 21:49:19 +03:00
static struct devfreq_cpu_data *
get_parent_cpu_data ( struct devfreq_passive_data * p_data ,
struct cpufreq_policy * policy )
{
struct devfreq_cpu_data * parent_cpu_data ;
if ( ! p_data | | ! policy )
return NULL ;
list_for_each_entry ( parent_cpu_data , & p_data - > cpu_data_list , node )
if ( parent_cpu_data - > first_cpu = = cpumask_first ( policy - > related_cpus ) )
return parent_cpu_data ;
return NULL ;
}
2022-06-15 02:09:47 +03:00
static void delete_parent_cpu_data ( struct devfreq_passive_data * p_data )
{
struct devfreq_cpu_data * parent_cpu_data , * tmp ;
list_for_each_entry_safe ( parent_cpu_data , tmp , & p_data - > cpu_data_list , node ) {
list_del ( & parent_cpu_data - > node ) ;
if ( parent_cpu_data - > opp_table )
dev_pm_opp_put_opp_table ( parent_cpu_data - > opp_table ) ;
kfree ( parent_cpu_data ) ;
}
}
2021-03-02 09:58:21 +03:00
static unsigned long get_target_freq_by_required_opp ( struct device * p_dev ,
struct opp_table * p_opp_table ,
struct opp_table * opp_table ,
unsigned long * freq )
{
struct dev_pm_opp * opp = NULL , * p_opp = NULL ;
unsigned long target_freq ;
if ( ! p_dev | | ! p_opp_table | | ! opp_table | | ! freq )
return 0 ;
p_opp = devfreq_recommended_opp ( p_dev , freq , 0 ) ;
if ( IS_ERR ( p_opp ) )
return 0 ;
opp = dev_pm_opp_xlate_required_opp ( p_opp_table , opp_table , p_opp ) ;
dev_pm_opp_put ( p_opp ) ;
if ( IS_ERR ( opp ) )
return 0 ;
target_freq = dev_pm_opp_get_freq ( opp ) ;
dev_pm_opp_put ( opp ) ;
return target_freq ;
}
static int get_target_freq_with_cpufreq ( struct devfreq * devfreq ,
unsigned long * target_freq )
{
struct devfreq_passive_data * p_data =
( struct devfreq_passive_data * ) devfreq - > data ;
struct devfreq_cpu_data * parent_cpu_data ;
2022-04-26 21:49:19 +03:00
struct cpufreq_policy * policy ;
2021-03-02 09:58:21 +03:00
unsigned long cpu , cpu_cur , cpu_min , cpu_max , cpu_percent ;
unsigned long dev_min , dev_max ;
unsigned long freq = 0 ;
2022-04-26 21:49:19 +03:00
int ret = 0 ;
2021-03-02 09:58:21 +03:00
for_each_online_cpu ( cpu ) {
2022-04-26 21:49:19 +03:00
policy = cpufreq_cpu_get ( cpu ) ;
if ( ! policy ) {
ret = - EINVAL ;
continue ;
}
parent_cpu_data = get_parent_cpu_data ( p_data , policy ) ;
if ( ! parent_cpu_data ) {
cpufreq_cpu_put ( policy ) ;
2021-03-02 09:58:21 +03:00
continue ;
2022-04-26 21:49:19 +03:00
}
2021-03-02 09:58:21 +03:00
/* Get target freq via required opps */
cpu_cur = parent_cpu_data - > cur_freq * HZ_PER_KHZ ;
freq = get_target_freq_by_required_opp ( parent_cpu_data - > dev ,
parent_cpu_data - > opp_table ,
devfreq - > opp_table , & cpu_cur ) ;
if ( freq ) {
* target_freq = max ( freq , * target_freq ) ;
2022-04-26 21:49:19 +03:00
cpufreq_cpu_put ( policy ) ;
2021-03-02 09:58:21 +03:00
continue ;
}
/* Use interpolation if required opps is not available */
devfreq_get_freq_range ( devfreq , & dev_min , & dev_max ) ;
cpu_min = parent_cpu_data - > min_freq ;
cpu_max = parent_cpu_data - > max_freq ;
cpu_cur = parent_cpu_data - > cur_freq ;
cpu_percent = ( ( cpu_cur - cpu_min ) * 100 ) / ( cpu_max - cpu_min ) ;
freq = dev_min + mult_frac ( dev_max - dev_min , cpu_percent , 100 ) ;
* target_freq = max ( freq , * target_freq ) ;
2022-04-26 21:49:19 +03:00
cpufreq_cpu_put ( policy ) ;
2021-03-02 09:58:21 +03:00
}
2022-04-26 21:49:19 +03:00
return ret ;
2021-03-02 09:58:21 +03:00
}
static int get_target_freq_with_devfreq ( struct devfreq * devfreq ,
2016-03-22 07:44:03 +03:00
unsigned long * freq )
{
struct devfreq_passive_data * p_data
= ( struct devfreq_passive_data * ) devfreq - > data ;
struct devfreq * parent_devfreq = ( struct devfreq * ) p_data - > parent ;
unsigned long child_freq = ULONG_MAX ;
2021-02-04 11:14:24 +03:00
int i , count ;
2016-03-22 07:44:03 +03:00
2021-03-02 11:22:50 +03:00
/* Get target freq via required opps */
child_freq = get_target_freq_by_required_opp ( parent_devfreq - > dev . parent ,
parent_devfreq - > opp_table ,
devfreq - > opp_table , freq ) ;
if ( child_freq )
goto out ;
2016-03-22 07:44:03 +03:00
2021-03-02 11:22:50 +03:00
/* Use interpolation if required opps is not available */
2022-06-20 01:03:51 +03:00
for ( i = 0 ; i < parent_devfreq - > max_state ; i + + )
if ( parent_devfreq - > freq_table [ i ] = = * freq )
2016-03-22 07:44:03 +03:00
break ;
2022-06-20 01:03:51 +03:00
if ( i = = parent_devfreq - > max_state )
2021-02-04 11:14:24 +03:00
return - EINVAL ;
2016-03-22 07:44:03 +03:00
2022-06-20 01:03:51 +03:00
if ( i < devfreq - > max_state ) {
child_freq = devfreq - > freq_table [ i ] ;
2016-03-22 07:44:03 +03:00
} else {
2022-06-20 01:03:51 +03:00
count = devfreq - > max_state ;
child_freq = devfreq - > freq_table [ count - 1 ] ;
2016-03-22 07:44:03 +03:00
}
2021-03-02 11:22:50 +03:00
out :
2016-03-22 07:44:03 +03:00
* freq = child_freq ;
2021-02-04 11:14:24 +03:00
return 0 ;
2016-03-22 07:44:03 +03:00
}
2021-03-02 09:58:21 +03:00
static int devfreq_passive_get_target_freq ( struct devfreq * devfreq ,
unsigned long * freq )
{
struct devfreq_passive_data * p_data =
( struct devfreq_passive_data * ) devfreq - > data ;
int ret ;
if ( ! p_data )
return - EINVAL ;
/*
* If the devfreq device with passive governor has the specific method
* to determine the next frequency , should use the get_target_freq ( )
* of struct devfreq_passive_data .
*/
if ( p_data - > get_target_freq )
return p_data - > get_target_freq ( devfreq , freq ) ;
switch ( p_data - > parent_type ) {
case DEVFREQ_PARENT_DEV :
ret = get_target_freq_with_devfreq ( devfreq , freq ) ;
break ;
case CPUFREQ_PARENT_DEV :
ret = get_target_freq_with_cpufreq ( devfreq , freq ) ;
break ;
default :
ret = - EINVAL ;
dev_err ( & devfreq - > dev , " Invalid parent type \n " ) ;
break ;
}
return ret ;
}
static int cpufreq_passive_notifier_call ( struct notifier_block * nb ,
unsigned long event , void * ptr )
{
struct devfreq_passive_data * p_data =
container_of ( nb , struct devfreq_passive_data , nb ) ;
struct devfreq * devfreq = ( struct devfreq * ) p_data - > this ;
struct devfreq_cpu_data * parent_cpu_data ;
struct cpufreq_freqs * freqs = ptr ;
unsigned int cur_freq ;
int ret ;
2022-04-26 21:49:19 +03:00
if ( event ! = CPUFREQ_POSTCHANGE | | ! freqs )
2021-03-02 09:58:21 +03:00
return 0 ;
2022-04-26 21:49:19 +03:00
parent_cpu_data = get_parent_cpu_data ( p_data , freqs - > policy ) ;
if ( ! parent_cpu_data | | parent_cpu_data - > cur_freq = = freqs - > new )
2021-03-02 09:58:21 +03:00
return 0 ;
cur_freq = parent_cpu_data - > cur_freq ;
parent_cpu_data - > cur_freq = freqs - > new ;
mutex_lock ( & devfreq - > lock ) ;
ret = devfreq_update_target ( devfreq , freqs - > new ) ;
mutex_unlock ( & devfreq - > lock ) ;
if ( ret ) {
parent_cpu_data - > cur_freq = cur_freq ;
dev_err ( & devfreq - > dev , " failed to update the frequency. \n " ) ;
return ret ;
}
return 0 ;
}
static int cpufreq_passive_unregister_notifier ( struct devfreq * devfreq )
{
struct devfreq_passive_data * p_data
= ( struct devfreq_passive_data * ) devfreq - > data ;
2022-06-15 02:09:47 +03:00
int ret ;
2021-03-02 09:58:21 +03:00
if ( p_data - > nb . notifier_call ) {
ret = cpufreq_unregister_notifier ( & p_data - > nb ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
if ( ret < 0 )
return ret ;
}
2022-06-15 02:09:47 +03:00
delete_parent_cpu_data ( p_data ) ;
2021-03-02 09:58:21 +03:00
2022-06-15 02:09:47 +03:00
return 0 ;
2021-03-02 09:58:21 +03:00
}
static int cpufreq_passive_register_notifier ( struct devfreq * devfreq )
{
struct devfreq_passive_data * p_data
= ( struct devfreq_passive_data * ) devfreq - > data ;
struct device * dev = devfreq - > dev . parent ;
struct opp_table * opp_table = NULL ;
struct devfreq_cpu_data * parent_cpu_data ;
struct cpufreq_policy * policy ;
struct device * cpu_dev ;
unsigned int cpu ;
int ret ;
2022-04-26 21:49:19 +03:00
p_data - > cpu_data_list
= ( struct list_head ) LIST_HEAD_INIT ( p_data - > cpu_data_list ) ;
2021-03-02 09:58:21 +03:00
p_data - > nb . notifier_call = cpufreq_passive_notifier_call ;
ret = cpufreq_register_notifier ( & p_data - > nb , CPUFREQ_TRANSITION_NOTIFIER ) ;
if ( ret ) {
dev_err ( dev , " failed to register cpufreq notifier \n " ) ;
p_data - > nb . notifier_call = NULL ;
goto err ;
}
for_each_possible_cpu ( cpu ) {
policy = cpufreq_cpu_get ( cpu ) ;
if ( ! policy ) {
ret = - EPROBE_DEFER ;
goto err ;
}
2022-04-26 21:49:19 +03:00
parent_cpu_data = get_parent_cpu_data ( p_data , policy ) ;
if ( parent_cpu_data ) {
cpufreq_cpu_put ( policy ) ;
continue ;
}
2021-03-02 09:58:21 +03:00
parent_cpu_data = kzalloc ( sizeof ( * parent_cpu_data ) ,
GFP_KERNEL ) ;
if ( ! parent_cpu_data ) {
ret = - ENOMEM ;
goto err_put_policy ;
}
cpu_dev = get_cpu_device ( cpu ) ;
if ( ! cpu_dev ) {
dev_err ( dev , " failed to get cpu device \n " ) ;
ret = - ENODEV ;
goto err_free_cpu_data ;
}
opp_table = dev_pm_opp_get_opp_table ( cpu_dev ) ;
if ( IS_ERR ( opp_table ) ) {
dev_err ( dev , " failed to get opp_table of cpu%d \n " , cpu ) ;
ret = PTR_ERR ( opp_table ) ;
goto err_free_cpu_data ;
}
parent_cpu_data - > dev = cpu_dev ;
parent_cpu_data - > opp_table = opp_table ;
parent_cpu_data - > first_cpu = cpumask_first ( policy - > related_cpus ) ;
parent_cpu_data - > cur_freq = policy - > cur ;
parent_cpu_data - > min_freq = policy - > cpuinfo . min_freq ;
parent_cpu_data - > max_freq = policy - > cpuinfo . max_freq ;
2022-04-26 21:49:19 +03:00
list_add_tail ( & parent_cpu_data - > node , & p_data - > cpu_data_list ) ;
2021-03-02 09:58:21 +03:00
cpufreq_cpu_put ( policy ) ;
}
mutex_lock ( & devfreq - > lock ) ;
ret = devfreq_update_target ( devfreq , 0L ) ;
mutex_unlock ( & devfreq - > lock ) ;
if ( ret )
dev_err ( dev , " failed to update the frequency \n " ) ;
return ret ;
err_free_cpu_data :
kfree ( parent_cpu_data ) ;
err_put_policy :
cpufreq_cpu_put ( policy ) ;
err :
return ret ;
}
2016-03-22 07:44:03 +03:00
static int devfreq_passive_notifier_call ( struct notifier_block * nb ,
unsigned long event , void * ptr )
{
struct devfreq_passive_data * data
= container_of ( nb , struct devfreq_passive_data , nb ) ;
struct devfreq * devfreq = ( struct devfreq * ) data - > this ;
struct devfreq * parent = ( struct devfreq * ) data - > parent ;
struct devfreq_freqs * freqs = ( struct devfreq_freqs * ) ptr ;
unsigned long freq = freqs - > new ;
2020-10-07 16:02:39 +03:00
int ret = 0 ;
2016-03-22 07:44:03 +03:00
2020-10-07 16:02:39 +03:00
mutex_lock_nested ( & devfreq - > lock , SINGLE_DEPTH_NESTING ) ;
2016-03-22 07:44:03 +03:00
switch ( event ) {
case DEVFREQ_PRECHANGE :
if ( parent - > previous_freq > freq )
2020-10-07 16:02:39 +03:00
ret = devfreq_update_target ( devfreq , freq ) ;
2016-03-22 07:44:03 +03:00
break ;
case DEVFREQ_POSTCHANGE :
if ( parent - > previous_freq < freq )
2020-10-07 16:02:39 +03:00
ret = devfreq_update_target ( devfreq , freq ) ;
2016-03-22 07:44:03 +03:00
break ;
}
2020-10-07 16:02:39 +03:00
mutex_unlock ( & devfreq - > lock ) ;
if ( ret < 0 )
dev_warn ( & devfreq - > dev ,
" failed to update devfreq using passive governor \n " ) ;
2016-03-22 07:44:03 +03:00
return NOTIFY_DONE ;
}
2021-03-02 09:58:21 +03:00
static int devfreq_passive_unregister_notifier ( struct devfreq * devfreq )
{
struct devfreq_passive_data * p_data
= ( struct devfreq_passive_data * ) devfreq - > data ;
struct devfreq * parent = ( struct devfreq * ) p_data - > parent ;
struct notifier_block * nb = & p_data - > nb ;
return devfreq_unregister_notifier ( parent , nb , DEVFREQ_TRANSITION_NOTIFIER ) ;
}
static int devfreq_passive_register_notifier ( struct devfreq * devfreq )
2016-03-22 07:44:03 +03:00
{
struct devfreq_passive_data * p_data
= ( struct devfreq_passive_data * ) devfreq - > data ;
struct devfreq * parent = ( struct devfreq * ) p_data - > parent ;
struct notifier_block * nb = & p_data - > nb ;
if ( ! parent )
return - EPROBE_DEFER ;
2021-03-02 09:58:21 +03:00
nb - > notifier_call = devfreq_passive_notifier_call ;
return devfreq_register_notifier ( parent , nb , DEVFREQ_TRANSITION_NOTIFIER ) ;
}
static int devfreq_passive_event_handler ( struct devfreq * devfreq ,
unsigned int event , void * data )
{
struct devfreq_passive_data * p_data
= ( struct devfreq_passive_data * ) devfreq - > data ;
2022-05-19 04:07:53 +03:00
int ret = 0 ;
2021-03-02 09:58:21 +03:00
if ( ! p_data )
return - EINVAL ;
2022-06-14 14:06:59 +03:00
p_data - > this = devfreq ;
2021-03-02 09:58:21 +03:00
2016-03-22 07:44:03 +03:00
switch ( event ) {
case DEVFREQ_GOV_START :
2021-03-02 09:58:21 +03:00
if ( p_data - > parent_type = = DEVFREQ_PARENT_DEV )
ret = devfreq_passive_register_notifier ( devfreq ) ;
else if ( p_data - > parent_type = = CPUFREQ_PARENT_DEV )
ret = cpufreq_passive_register_notifier ( devfreq ) ;
2016-03-22 07:44:03 +03:00
break ;
case DEVFREQ_GOV_STOP :
2021-03-02 09:58:21 +03:00
if ( p_data - > parent_type = = DEVFREQ_PARENT_DEV )
WARN_ON ( devfreq_passive_unregister_notifier ( devfreq ) ) ;
else if ( p_data - > parent_type = = CPUFREQ_PARENT_DEV )
WARN_ON ( cpufreq_passive_unregister_notifier ( devfreq ) ) ;
2016-03-22 07:44:03 +03:00
break ;
default :
break ;
}
return ret ;
}
static struct devfreq_governor devfreq_passive = {
2017-10-23 04:32:12 +03:00
. name = DEVFREQ_GOV_PASSIVE ,
2020-10-05 08:48:01 +03:00
. flags = DEVFREQ_GOV_FLAG_IMMUTABLE ,
2016-03-22 07:44:03 +03:00
. get_target_freq = devfreq_passive_get_target_freq ,
. event_handler = devfreq_passive_event_handler ,
} ;
static int __init devfreq_passive_init ( void )
{
return devfreq_add_governor ( & devfreq_passive ) ;
}
subsys_initcall ( devfreq_passive_init ) ;
static void __exit devfreq_passive_exit ( void )
{
int ret ;
ret = devfreq_remove_governor ( & devfreq_passive ) ;
if ( ret )
pr_err ( " %s: failed remove governor %d \n " , __func__ , ret ) ;
}
module_exit ( devfreq_passive_exit ) ;
MODULE_AUTHOR ( " Chanwoo Choi <cw00.choi@samsung.com> " ) ;
MODULE_AUTHOR ( " MyungJoo Ham <myungjoo.ham@samsung.com> " ) ;
MODULE_DESCRIPTION ( " DEVFREQ Passive governor " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;