2010-12-08 17:13:42 +03:00
/*
* Copyright ( C ) STMicroelectronics 2009
* Copyright ( C ) ST - Ericsson SA 2010
*
* License Terms : GNU General Public License v2
* Author : Sundar Iyer < sundar . iyer @ stericsson . com >
* Author : Martin Persson < martin . persson @ stericsson . com >
* Author : Jonas Aaberg < jonas . aberg @ stericsson . com >
*
*/
# include <linux/kernel.h>
# include <linux/cpufreq.h>
# include <linux/delay.h>
2011-05-15 21:19:51 +04:00
# include <linux/slab.h>
2011-05-16 00:53:56 +04:00
# include <linux/mfd/db8500-prcmu.h>
2011-05-15 21:19:51 +04:00
# include <mach/id.h>
2010-12-08 17:13:42 +03:00
static struct cpufreq_frequency_table freq_table [ ] = {
[ 0 ] = {
. index = 0 ,
2011-05-15 21:19:51 +04:00
. frequency = 300000 ,
2010-12-08 17:13:42 +03:00
} ,
[ 1 ] = {
. index = 1 ,
2011-05-15 21:19:51 +04:00
. frequency = 600000 ,
2010-12-08 17:13:42 +03:00
} ,
[ 2 ] = {
2011-05-15 21:19:51 +04:00
/* Used for MAX_OPP, if available */
2010-12-08 17:13:42 +03:00
. index = 2 ,
2011-05-15 21:19:51 +04:00
. frequency = CPUFREQ_TABLE_END ,
2010-12-08 17:13:42 +03:00
} ,
[ 3 ] = {
. index = 3 ,
. frequency = CPUFREQ_TABLE_END ,
} ,
} ;
2011-05-15 21:19:51 +04:00
static enum arm_opp idx2opp [ ] = {
ARM_50_OPP ,
ARM_100_OPP ,
ARM_MAX_OPP
} ;
static struct freq_attr * db8500_cpufreq_attr [ ] = {
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
2010-12-08 17:13:42 +03:00
} ;
2011-05-15 21:19:51 +04:00
static int db8500_cpufreq_verify_speed ( struct cpufreq_policy * policy )
2010-12-08 17:13:42 +03:00
{
return cpufreq_frequency_table_verify ( policy , freq_table ) ;
}
2011-05-15 21:19:51 +04:00
static int db8500_cpufreq_target ( struct cpufreq_policy * policy ,
2010-12-08 17:13:42 +03:00
unsigned int target_freq ,
unsigned int relation )
{
struct cpufreq_freqs freqs ;
2011-05-15 21:19:51 +04:00
unsigned int idx ;
2010-12-08 17:13:42 +03:00
2011-05-15 21:19:51 +04:00
/* scale the target frequency to one of the extremes supported */
2010-12-08 17:13:42 +03:00
if ( target_freq < policy - > cpuinfo . min_freq )
target_freq = policy - > cpuinfo . min_freq ;
if ( target_freq > policy - > cpuinfo . max_freq )
target_freq = policy - > cpuinfo . max_freq ;
2011-05-15 21:19:51 +04:00
/* Lookup the next frequency */
if ( cpufreq_frequency_table_target
( policy , freq_table , target_freq , relation , & idx ) ) {
return - EINVAL ;
2010-12-08 17:13:42 +03:00
}
freqs . old = policy - > cur ;
2011-05-15 21:19:51 +04:00
freqs . new = freq_table [ idx ] . frequency ;
2010-12-08 17:13:42 +03:00
freqs . cpu = policy - > cpu ;
2011-05-15 21:19:51 +04:00
if ( freqs . old = = freqs . new )
2010-12-08 17:13:42 +03:00
return 0 ;
2011-05-15 21:19:51 +04:00
/* pre-change notification */
2010-12-08 17:13:42 +03:00
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
2011-05-15 21:19:51 +04:00
/* request the PRCM unit for opp change */
if ( prcmu_set_arm_opp ( idx2opp [ idx ] ) ) {
pr_err ( " db8500-cpufreq: Failed to set OPP level \n " ) ;
return - EINVAL ;
2010-12-08 17:13:42 +03:00
}
2011-05-15 21:19:51 +04:00
/* post change notification */
2010-12-08 17:13:42 +03:00
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
2011-05-15 21:19:51 +04:00
return 0 ;
2010-12-08 17:13:42 +03:00
}
2011-05-15 21:19:51 +04:00
static unsigned int db8500_cpufreq_getspeed ( unsigned int cpu )
2010-12-08 17:13:42 +03:00
{
int i ;
2011-05-15 21:19:51 +04:00
/* request the prcm to get the current ARM opp */
for ( i = 0 ; prcmu_get_arm_opp ( ) ! = idx2opp [ i ] ; i + + )
2010-12-08 17:13:42 +03:00
;
return freq_table [ i ] . frequency ;
}
2011-05-15 21:19:51 +04:00
static int __cpuinit db8500_cpufreq_init ( struct cpufreq_policy * policy )
2010-12-08 17:13:42 +03:00
{
int res ;
2011-05-15 21:19:51 +04:00
int i ;
2010-12-08 17:13:42 +03:00
2011-05-15 21:19:51 +04:00
BUILD_BUG_ON ( ARRAY_SIZE ( idx2opp ) + 1 ! = ARRAY_SIZE ( freq_table ) ) ;
2010-12-08 17:13:42 +03:00
2011-05-15 21:19:51 +04:00
if ( cpu_is_u8500v2 ( ) & & ! prcmu_is_u8400 ( ) ) {
freq_table [ 0 ] . frequency = 400000 ;
freq_table [ 1 ] . frequency = 800000 ;
2010-12-08 17:13:42 +03:00
if ( prcmu_has_arm_maxopp ( ) )
2011-05-15 21:19:51 +04:00
freq_table [ 2 ] . frequency = 1000000 ;
2010-12-08 17:13:42 +03:00
}
/* get policy fields based on the table */
res = cpufreq_frequency_table_cpuinfo ( policy , freq_table ) ;
if ( ! res )
cpufreq_frequency_table_get_attr ( freq_table , policy - > cpu ) ;
else {
2011-05-15 21:19:51 +04:00
pr_err ( " db8500-cpufreq : Failed to read policy table \n " ) ;
2010-12-08 17:13:42 +03:00
return res ;
}
policy - > min = policy - > cpuinfo . min_freq ;
policy - > max = policy - > cpuinfo . max_freq ;
2011-05-15 21:19:51 +04:00
policy - > cur = db8500_cpufreq_getspeed ( policy - > cpu ) ;
for ( i = 0 ; freq_table [ i ] . frequency ! = policy - > cur ; i + + )
;
2010-12-08 17:13:42 +03:00
policy - > governor = CPUFREQ_DEFAULT_GOVERNOR ;
/*
* FIXME : Need to take time measurement across the target ( )
* function with no / some / all drivers in the notification
* list .
*/
2011-05-15 21:19:51 +04:00
policy - > cpuinfo . transition_latency = 20 * 1000 ; /* in ns */
2010-12-08 17:13:42 +03:00
/* policy sharing between dual CPUs */
cpumask_copy ( policy - > cpus , & cpu_present_map ) ;
policy - > shared_type = CPUFREQ_SHARED_TYPE_ALL ;
return 0 ;
}
2011-05-15 21:19:51 +04:00
static struct cpufreq_driver db8500_cpufreq_driver = {
. flags = CPUFREQ_STICKY ,
. verify = db8500_cpufreq_verify_speed ,
. target = db8500_cpufreq_target ,
. get = db8500_cpufreq_getspeed ,
. init = db8500_cpufreq_init ,
. name = " DB8500 " ,
. attr = db8500_cpufreq_attr ,
2010-12-08 17:13:42 +03:00
} ;
2011-05-15 21:19:51 +04:00
static int __init db8500_cpufreq_register ( void )
2010-12-08 17:13:42 +03:00
{
2011-05-15 21:19:51 +04:00
if ( ! cpu_is_u8500v20_or_later ( ) )
return - ENODEV ;
2010-12-08 17:13:42 +03:00
2011-05-15 21:19:51 +04:00
pr_info ( " cpufreq for DB8500 started \n " ) ;
return cpufreq_register_driver ( & db8500_cpufreq_driver ) ;
2010-12-08 17:13:42 +03:00
}
2011-05-15 21:19:51 +04:00
device_initcall ( db8500_cpufreq_register ) ;