2011-10-02 02:19:34 +04:00
/*
* linux / drivers / devfreq / governor_simpleondemand . c
*
* Copyright ( C ) 2011 Samsung Electronics
* MyungJoo Ham < myungjoo . ham @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/errno.h>
2012-10-30 00:01:46 +04:00
# include <linux/module.h>
2011-10-02 02:19:34 +04:00
# include <linux/devfreq.h>
# include <linux/math64.h>
2012-10-26 03:50:09 +04:00
# include "governor.h"
2011-10-02 02:19:34 +04:00
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
# define DFSO_UPTHRESHOLD (90)
# define DFSO_DOWNDIFFERENCTIAL (5)
static int devfreq_simple_ondemand_func ( struct devfreq * df ,
unsigned long * freq )
{
2015-08-14 20:56:56 +03:00
int err ;
struct devfreq_dev_status * stat ;
2011-10-02 02:19:34 +04:00
unsigned long long a , b ;
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD ;
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL ;
struct devfreq_simple_ondemand_data * data = df - > data ;
2011-12-09 11:42:19 +04:00
unsigned long max = ( df - > max_freq ) ? df - > max_freq : UINT_MAX ;
2011-10-02 02:19:34 +04:00
2015-08-14 20:56:56 +03:00
err = devfreq_update_stats ( df ) ;
2011-10-02 02:19:34 +04:00
if ( err )
return err ;
2015-08-14 20:56:56 +03:00
stat = & df - > last_status ;
2011-10-02 02:19:34 +04:00
if ( data ) {
if ( data - > upthreshold )
dfso_upthreshold = data - > upthreshold ;
if ( data - > downdifferential )
dfso_downdifferential = data - > downdifferential ;
}
if ( dfso_upthreshold > 100 | |
dfso_upthreshold < dfso_downdifferential )
return - EINVAL ;
/* Assume MAX if it is going to be divided by zero */
2015-08-14 20:56:56 +03:00
if ( stat - > total_time = = 0 ) {
2011-12-09 11:42:19 +04:00
* freq = max ;
2011-10-02 02:19:34 +04:00
return 0 ;
}
/* Prevent overflow */
2015-08-14 20:56:56 +03:00
if ( stat - > busy_time > = ( 1 < < 24 ) | | stat - > total_time > = ( 1 < < 24 ) ) {
stat - > busy_time > > = 7 ;
stat - > total_time > > = 7 ;
2011-10-02 02:19:34 +04:00
}
/* Set MAX if it's busy enough */
2015-08-14 20:56:56 +03:00
if ( stat - > busy_time * 100 >
stat - > total_time * dfso_upthreshold ) {
2011-12-09 11:42:19 +04:00
* freq = max ;
2011-10-02 02:19:34 +04:00
return 0 ;
}
/* Set MAX if we do not know the initial frequency */
2015-08-14 20:56:56 +03:00
if ( stat - > current_frequency = = 0 ) {
2011-12-09 11:42:19 +04:00
* freq = max ;
2011-10-02 02:19:34 +04:00
return 0 ;
}
/* Keep the current frequency */
2015-08-14 20:56:56 +03:00
if ( stat - > busy_time * 100 >
stat - > total_time * ( dfso_upthreshold - dfso_downdifferential ) ) {
* freq = stat - > current_frequency ;
2011-10-02 02:19:34 +04:00
return 0 ;
}
/* Set the desired frequency based on the load */
2015-08-14 20:56:56 +03:00
a = stat - > busy_time ;
a * = stat - > current_frequency ;
b = div_u64 ( a , stat - > total_time ) ;
2011-10-02 02:19:34 +04:00
b * = 100 ;
b = div_u64 ( b , ( dfso_upthreshold - dfso_downdifferential / 2 ) ) ;
* freq = ( unsigned long ) b ;
2011-12-09 11:42:19 +04:00
if ( df - > min_freq & & * freq < df - > min_freq )
* freq = df - > min_freq ;
if ( df - > max_freq & & * freq > df - > max_freq )
* freq = df - > max_freq ;
2011-10-02 02:19:34 +04:00
return 0 ;
}
2012-10-26 03:50:09 +04:00
static int devfreq_simple_ondemand_handler ( struct devfreq * devfreq ,
unsigned int event , void * data )
{
switch ( event ) {
case DEVFREQ_GOV_START :
devfreq_monitor_start ( devfreq ) ;
break ;
case DEVFREQ_GOV_STOP :
devfreq_monitor_stop ( devfreq ) ;
break ;
case DEVFREQ_GOV_INTERVAL :
devfreq_interval_update ( devfreq , ( unsigned int * ) data ) ;
break ;
2012-10-26 03:50:18 +04:00
case DEVFREQ_GOV_SUSPEND :
devfreq_monitor_suspend ( devfreq ) ;
break ;
case DEVFREQ_GOV_RESUME :
devfreq_monitor_resume ( devfreq ) ;
break ;
2012-10-26 03:50:09 +04:00
default :
break ;
}
return 0 ;
}
2012-10-30 00:01:45 +04:00
static struct devfreq_governor devfreq_simple_ondemand = {
2011-10-02 02:19:34 +04:00
. name = " simple_ondemand " ,
. get_target_freq = devfreq_simple_ondemand_func ,
2012-10-26 03:50:09 +04:00
. event_handler = devfreq_simple_ondemand_handler ,
2011-10-02 02:19:34 +04:00
} ;
2012-10-30 00:01:44 +04:00
static int __init devfreq_simple_ondemand_init ( void )
{
return devfreq_add_governor ( & devfreq_simple_ondemand ) ;
}
subsys_initcall ( devfreq_simple_ondemand_init ) ;
static void __exit devfreq_simple_ondemand_exit ( void )
{
int ret ;
ret = devfreq_remove_governor ( & devfreq_simple_ondemand ) ;
if ( ret )
pr_err ( " %s: failed remove governor %d \n " , __func__ , ret ) ;
return ;
}
module_exit ( devfreq_simple_ondemand_exit ) ;
2012-10-30 00:01:46 +04:00
MODULE_LICENSE ( " GPL " ) ;