2005-04-17 02:20:36 +04:00
/*
* linux / drivers / cpufreq / freq_table . c
*
* Copyright ( C ) 2002 - 2003 Dominik Brodowski
2008-05-22 10:52:05 +04:00
*
* 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 .
*
2005-04-17 02:20:36 +04:00
*/
2012-10-23 03:29:03 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/cpufreq.h>
2013-08-06 21:23:03 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
/*********************************************************************
* FREQUENCY TABLE HELPERS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int cpufreq_frequency_table_cpuinfo ( struct cpufreq_policy * policy ,
struct cpufreq_frequency_table * table )
{
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos ;
2005-04-17 02:20:36 +04:00
unsigned int min_freq = ~ 0 ;
unsigned int max_freq = 0 ;
2014-04-26 00:15:38 +04:00
unsigned int freq ;
2005-04-17 02:20:36 +04:00
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table ) {
freq = pos - > frequency ;
2005-04-17 02:20:36 +04:00
2013-12-20 18:24:49 +04:00
if ( ! cpufreq_boost_enabled ( )
2014-04-26 00:15:38 +04:00
& & ( pos - > flags & CPUFREQ_BOOST_FREQ ) )
2013-12-20 18:24:49 +04:00
continue ;
2014-04-26 00:15:38 +04:00
pr_debug ( " table entry %u: %u kHz \n " , ( int ) ( pos - table ) , freq ) ;
2005-04-17 02:20:36 +04:00
if ( freq < min_freq )
min_freq = freq ;
if ( freq > max_freq )
max_freq = freq ;
}
policy - > min = policy - > cpuinfo . min_freq = min_freq ;
policy - > max = policy - > cpuinfo . max_freq = max_freq ;
if ( policy - > min = = ~ 0 )
return - EINVAL ;
else
return 0 ;
}
EXPORT_SYMBOL_GPL ( cpufreq_frequency_table_cpuinfo ) ;
int cpufreq_frequency_table_verify ( struct cpufreq_policy * policy ,
struct cpufreq_frequency_table * table )
{
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos ;
unsigned int freq , next_larger = ~ 0 ;
2013-10-02 12:43:15 +04:00
bool found = false ;
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " request for verification of policy (%u - %u kHz) for cpu %u \n " ,
2006-10-26 14:50:58 +04:00
policy - > min , policy - > max , policy - > cpu ) ;
2005-04-17 02:20:36 +04:00
2013-10-02 12:43:19 +04:00
cpufreq_verify_within_cpu_limits ( policy ) ;
2005-04-17 02:20:36 +04:00
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table ) {
freq = pos - > frequency ;
2013-10-02 12:43:15 +04:00
if ( ( freq > = policy - > min ) & & ( freq < = policy - > max ) ) {
found = true ;
break ;
}
if ( ( next_larger > freq ) & & ( freq > policy - > max ) )
2005-04-17 02:20:36 +04:00
next_larger = freq ;
}
2013-10-02 12:43:15 +04:00
if ( ! found ) {
2005-04-17 02:20:36 +04:00
policy - > max = next_larger ;
2013-10-02 12:43:19 +04:00
cpufreq_verify_within_cpu_limits ( policy ) ;
2013-10-02 12:43:15 +04:00
}
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " verification lead to (%u - %u kHz) for cpu %u \n " ,
2006-10-26 14:50:58 +04:00
policy - > min , policy - > max , policy - > cpu ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( cpufreq_frequency_table_verify ) ;
2013-10-03 18:57:55 +04:00
/*
2014-03-10 13:23:33 +04:00
* Generic routine to verify policy & frequency table , requires driver to set
* policy - > freq_table prior to it .
2013-10-03 18:57:55 +04:00
*/
int cpufreq_generic_frequency_table_verify ( struct cpufreq_policy * policy )
{
struct cpufreq_frequency_table * table =
cpufreq_frequency_get_table ( policy - > cpu ) ;
if ( ! table )
return - ENODEV ;
return cpufreq_frequency_table_verify ( policy , table ) ;
}
EXPORT_SYMBOL_GPL ( cpufreq_generic_frequency_table_verify ) ;
2005-04-17 02:20:36 +04:00
int cpufreq_frequency_table_target ( struct cpufreq_policy * policy ,
struct cpufreq_frequency_table * table ,
unsigned int target_freq ,
unsigned int relation ,
unsigned int * index )
{
2006-05-31 02:09:31 +04:00
struct cpufreq_frequency_table optimal = {
2013-03-30 14:55:15 +04:00
. driver_data = ~ 0 ,
2006-05-31 02:09:31 +04:00
. frequency = 0 ,
} ;
struct cpufreq_frequency_table suboptimal = {
2013-03-30 14:55:15 +04:00
. driver_data = ~ 0 ,
2006-05-31 02:09:31 +04:00
. frequency = 0 ,
} ;
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos ;
2014-06-30 20:59:33 +04:00
unsigned int freq , diff , i = 0 ;
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " request for target %u kHz (relation: %u) for cpu %u \n " ,
2006-10-26 14:50:58 +04:00
target_freq , relation , policy - > cpu ) ;
2005-04-17 02:20:36 +04:00
switch ( relation ) {
case CPUFREQ_RELATION_H :
suboptimal . frequency = ~ 0 ;
break ;
case CPUFREQ_RELATION_L :
2014-06-30 20:59:33 +04:00
case CPUFREQ_RELATION_C :
2005-04-17 02:20:36 +04:00
optimal . frequency = ~ 0 ;
break ;
}
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table ) {
freq = pos - > frequency ;
i = pos - table ;
2005-04-17 02:20:36 +04:00
if ( ( freq < policy - > min ) | | ( freq > policy - > max ) )
continue ;
2014-05-14 22:05:52 +04:00
if ( freq = = target_freq ) {
optimal . driver_data = i ;
break ;
}
2009-01-18 09:56:41 +03:00
switch ( relation ) {
2005-04-17 02:20:36 +04:00
case CPUFREQ_RELATION_H :
2014-05-14 22:05:52 +04:00
if ( freq < target_freq ) {
2005-04-17 02:20:36 +04:00
if ( freq > = optimal . frequency ) {
optimal . frequency = freq ;
2013-03-30 14:55:15 +04:00
optimal . driver_data = i ;
2005-04-17 02:20:36 +04:00
}
} else {
if ( freq < = suboptimal . frequency ) {
suboptimal . frequency = freq ;
2013-03-30 14:55:15 +04:00
suboptimal . driver_data = i ;
2005-04-17 02:20:36 +04:00
}
}
break ;
case CPUFREQ_RELATION_L :
2014-05-14 22:05:52 +04:00
if ( freq > target_freq ) {
2005-04-17 02:20:36 +04:00
if ( freq < = optimal . frequency ) {
optimal . frequency = freq ;
2013-03-30 14:55:15 +04:00
optimal . driver_data = i ;
2005-04-17 02:20:36 +04:00
}
} else {
if ( freq > = suboptimal . frequency ) {
suboptimal . frequency = freq ;
2013-03-30 14:55:15 +04:00
suboptimal . driver_data = i ;
2005-04-17 02:20:36 +04:00
}
}
break ;
2014-06-30 20:59:33 +04:00
case CPUFREQ_RELATION_C :
diff = abs ( freq - target_freq ) ;
if ( diff < optimal . frequency | |
( diff = = optimal . frequency & &
freq > table [ optimal . driver_data ] . frequency ) ) {
optimal . frequency = diff ;
optimal . driver_data = i ;
}
break ;
2005-04-17 02:20:36 +04:00
}
}
2013-03-30 14:55:15 +04:00
if ( optimal . driver_data > i ) {
if ( suboptimal . driver_data > i )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2013-03-30 14:55:15 +04:00
* index = suboptimal . driver_data ;
2005-04-17 02:20:36 +04:00
} else
2013-03-30 14:55:15 +04:00
* index = optimal . driver_data ;
2005-04-17 02:20:36 +04:00
2014-03-28 17:41:45 +04:00
pr_debug ( " target index is %u, freq is:%u kHz \n " , * index ,
table [ * index ] . frequency ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( cpufreq_frequency_table_target ) ;
2013-12-03 09:50:46 +04:00
int cpufreq_frequency_table_get_index ( struct cpufreq_policy * policy ,
unsigned int freq )
{
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos , * table ;
2013-12-03 09:50:46 +04:00
table = cpufreq_frequency_get_table ( policy - > cpu ) ;
if ( unlikely ( ! table ) ) {
pr_debug ( " %s: Unable to find frequency table \n " , __func__ ) ;
return - ENOENT ;
}
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table )
if ( pos - > frequency = = freq )
return pos - table ;
2013-12-03 09:50:46 +04:00
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( cpufreq_frequency_table_get_index ) ;
2005-04-17 02:20:36 +04:00
/**
2007-11-22 01:52:15 +03:00
* show_available_freqs - show available frequencies for the specified CPU
2005-04-17 02:20:36 +04:00
*/
2013-12-20 18:24:49 +04:00
static ssize_t show_available_freqs ( struct cpufreq_policy * policy , char * buf ,
bool show_boost )
2005-04-17 02:20:36 +04:00
{
ssize_t count = 0 ;
2014-04-26 00:15:38 +04:00
struct cpufreq_frequency_table * pos , * table = policy - > freq_table ;
2005-04-17 02:20:36 +04:00
2014-03-10 13:23:33 +04:00
if ( ! table )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2014-04-26 00:15:38 +04:00
cpufreq_for_each_valid_entry ( pos , table ) {
2013-12-20 18:24:49 +04:00
/*
* show_boost = true and driver_data = BOOST freq
* display BOOST freqs
*
* show_boost = false and driver_data = BOOST freq
* show_boost = true and driver_data ! = BOOST freq
* continue - do not display anything
*
* show_boost = false and driver_data ! = BOOST freq
* display NON BOOST freqs
*/
2014-04-26 00:15:38 +04:00
if ( show_boost ^ ( pos - > flags & CPUFREQ_BOOST_FREQ ) )
2013-12-20 18:24:49 +04:00
continue ;
2014-04-26 00:15:38 +04:00
count + = sprintf ( & buf [ count ] , " %d " , pos - > frequency ) ;
2005-04-17 02:20:36 +04:00
}
count + = sprintf ( & buf [ count ] , " \n " ) ;
return count ;
}
2013-12-20 18:24:49 +04:00
# define cpufreq_attr_available_freq(_name) \
struct freq_attr cpufreq_freq_attr_ # # _name # # _freqs = \
__ATTR_RO ( _name # # _frequencies )
/**
* show_scaling_available_frequencies - show available normal frequencies for
* the specified CPU
*/
static ssize_t scaling_available_frequencies_show ( struct cpufreq_policy * policy ,
char * buf )
{
return show_available_freqs ( policy , buf , false ) ;
}
cpufreq_attr_available_freq ( scaling_available ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL_GPL ( cpufreq_freq_attr_scaling_available_freqs ) ;
2013-12-20 18:24:49 +04:00
/**
* show_available_boost_freqs - show available boost frequencies for
* the specified CPU
*/
static ssize_t scaling_boost_frequencies_show ( struct cpufreq_policy * policy ,
char * buf )
{
return show_available_freqs ( policy , buf , true ) ;
}
cpufreq_attr_available_freq ( scaling_boost ) ;
EXPORT_SYMBOL_GPL ( cpufreq_freq_attr_scaling_boost_freqs ) ;
2013-10-03 18:57:55 +04:00
struct freq_attr * cpufreq_generic_attr [ ] = {
& cpufreq_freq_attr_scaling_available_freqs ,
2013-12-20 18:24:49 +04:00
# ifdef CONFIG_CPU_FREQ_BOOST_SW
& cpufreq_freq_attr_scaling_boost_freqs ,
# endif
2013-10-03 18:57:55 +04:00
NULL ,
} ;
EXPORT_SYMBOL_GPL ( cpufreq_generic_attr ) ;
2013-09-16 17:26:03 +04:00
int cpufreq_table_validate_and_show ( struct cpufreq_policy * policy ,
struct cpufreq_frequency_table * table )
{
int ret = cpufreq_frequency_table_cpuinfo ( policy , table ) ;
if ( ! ret )
2014-03-10 13:23:33 +04:00
policy - > freq_table = table ;
2013-09-16 17:26:03 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( cpufreq_table_validate_and_show ) ;
2014-03-10 13:23:33 +04:00
struct cpufreq_policy * cpufreq_cpu_get_raw ( unsigned int cpu ) ;
2013-01-14 17:23:03 +04:00
2005-04-17 02:20:36 +04:00
struct cpufreq_frequency_table * cpufreq_frequency_get_table ( unsigned int cpu )
{
2014-03-10 13:23:33 +04:00
struct cpufreq_policy * policy = cpufreq_cpu_get_raw ( cpu ) ;
return policy ? policy - > freq_table : NULL ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( cpufreq_frequency_get_table ) ;
2009-01-18 09:56:41 +03:00
MODULE_AUTHOR ( " Dominik Brodowski <linux@brodo.de> " ) ;
MODULE_DESCRIPTION ( " CPUfreq frequency table helpers " ) ;
MODULE_LICENSE ( " GPL " ) ;