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 >
*
*/
2012-10-10 15:42:24 +04:00
# include <linux/module.h>
2010-12-08 17:13:42 +03:00
# include <linux/kernel.h>
# include <linux/cpufreq.h>
# include <linux/delay.h>
2011-05-15 21:19:51 +04:00
# include <linux/slab.h>
2012-10-10 15:42:24 +04:00
# include <linux/platform_device.h>
2012-10-10 15:42:29 +04:00
# include <linux/clk.h>
2011-05-15 21:19:51 +04:00
# include <mach/id.h>
2010-12-08 17:13:42 +03:00
2012-10-10 15:42:25 +04:00
static struct cpufreq_frequency_table * freq_table ;
2012-10-10 15:42:29 +04:00
static struct clk * armss_clk ;
2011-05-15 21:19:51 +04:00
2012-12-10 19:25:38 +04:00
static struct freq_attr * dbx500_cpufreq_attr [ ] = {
2011-05-15 21:19:51 +04:00
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
2010-12-08 17:13:42 +03:00
} ;
2012-12-10 19:25:38 +04:00
static int dbx500_cpufreq_verify_speed ( struct cpufreq_policy * policy )
2010-12-08 17:13:42 +03:00
{
return cpufreq_frequency_table_verify ( policy , freq_table ) ;
}
2012-12-10 19:25:38 +04:00
static int dbx500_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
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 */
2011-08-25 10:31:20 +04:00
for_each_cpu ( freqs . cpu , policy - > cpus )
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
2010-12-08 17:13:42 +03:00
2012-10-10 15:42:29 +04:00
/* update armss clk frequency */
if ( clk_set_rate ( armss_clk , freq_table [ idx ] . frequency * 1000 ) ) {
2012-12-10 19:25:38 +04:00
pr_err ( " dbx500-cpufreq: Failed to update armss clk \n " ) ;
2011-05-15 21:19:51 +04:00
return - EINVAL ;
2010-12-08 17:13:42 +03:00
}
2011-05-15 21:19:51 +04:00
/* post change notification */
2011-08-25 10:31:20 +04:00
for_each_cpu ( freqs . cpu , policy - > cpus )
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
2010-12-08 17:13:42 +03:00
2011-05-15 21:19:51 +04:00
return 0 ;
2010-12-08 17:13:42 +03:00
}
2012-12-10 19:25:38 +04:00
static unsigned int dbx500_cpufreq_getspeed ( unsigned int cpu )
2010-12-08 17:13:42 +03:00
{
2012-10-10 15:42:25 +04:00
int i = 0 ;
2012-10-10 15:42:29 +04:00
unsigned long freq = clk_get_rate ( armss_clk ) / 1000 ;
2012-10-10 15:42:25 +04:00
while ( freq_table [ i ] . frequency ! = CPUFREQ_TABLE_END ) {
2012-10-10 15:42:29 +04:00
if ( freq < = freq_table [ i ] . frequency )
2012-10-10 15:42:25 +04:00
return freq_table [ i ] . frequency ;
i + + ;
}
2012-10-10 15:42:29 +04:00
/* We could not find a corresponding frequency. */
2012-12-10 19:25:38 +04:00
pr_err ( " dbx500-cpufreq: Failed to find cpufreq speed \n " ) ;
2012-10-10 15:42:25 +04:00
return 0 ;
2010-12-08 17:13:42 +03:00
}
2012-12-10 19:25:38 +04:00
static int __cpuinit dbx500_cpufreq_init ( struct cpufreq_policy * policy )
2010-12-08 17:13:42 +03:00
{
2012-10-10 15:42:25 +04:00
int i = 0 ;
int res ;
2012-01-13 19:20:03 +04:00
2012-10-10 15:42:29 +04:00
armss_clk = clk_get ( NULL , " armss " ) ;
if ( IS_ERR ( armss_clk ) ) {
2012-12-10 19:25:38 +04:00
pr_err ( " dbx500-cpufreq : Failed to get armss clk \n " ) ;
2012-10-10 15:42:29 +04:00
return PTR_ERR ( armss_clk ) ;
}
2012-12-10 19:25:38 +04:00
pr_info ( " dbx500-cpufreq : Available frequencies: \n " ) ;
2012-10-10 15:42:25 +04:00
while ( freq_table [ i ] . frequency ! = CPUFREQ_TABLE_END ) {
2011-11-04 16:04:41 +04:00
pr_info ( " %d Mhz \n " , freq_table [ i ] . frequency / 1000 ) ;
2012-10-10 15:42:25 +04:00
i + + ;
}
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 {
2012-12-10 19:25:38 +04:00
pr_err ( " dbx500-cpufreq : Failed to read policy table \n " ) ;
2012-10-10 15:42:29 +04:00
clk_put ( armss_clk ) ;
2010-12-08 17:13:42 +03:00
return res ;
}
policy - > min = policy - > cpuinfo . min_freq ;
policy - > max = policy - > cpuinfo . max_freq ;
2012-12-10 19:25:38 +04:00
policy - > cur = dbx500_cpufreq_getspeed ( policy - > cpu ) ;
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 */
2012-03-29 09:08:30 +04:00
cpumask_copy ( policy - > cpus , cpu_present_mask ) ;
2010-12-08 17:13:42 +03:00
policy - > shared_type = CPUFREQ_SHARED_TYPE_ALL ;
return 0 ;
}
2012-12-10 19:25:38 +04:00
static struct cpufreq_driver dbx500_cpufreq_driver = {
2011-05-15 21:19:51 +04:00
. flags = CPUFREQ_STICKY ,
2012-12-10 19:25:38 +04:00
. verify = dbx500_cpufreq_verify_speed ,
. target = dbx500_cpufreq_target ,
. get = dbx500_cpufreq_getspeed ,
. init = dbx500_cpufreq_init ,
. name = " DBX500 " ,
. attr = dbx500_cpufreq_attr ,
2010-12-08 17:13:42 +03:00
} ;
2012-12-10 19:25:38 +04:00
static int dbx500_cpufreq_probe ( struct platform_device * pdev )
2012-10-10 15:42:24 +04:00
{
2012-10-10 15:42:25 +04:00
freq_table = dev_get_platdata ( & pdev - > dev ) ;
if ( ! freq_table ) {
2012-12-10 19:25:38 +04:00
pr_err ( " dbx500-cpufreq: Failed to fetch cpufreq table \n " ) ;
2012-10-10 15:42:25 +04:00
return - ENODEV ;
}
2012-12-10 19:25:38 +04:00
return cpufreq_register_driver ( & dbx500_cpufreq_driver ) ;
2012-10-10 15:42:24 +04:00
}
2012-12-10 19:25:38 +04:00
static struct platform_driver dbx500_cpufreq_plat_driver = {
2012-10-10 15:42:24 +04:00
. driver = {
2012-12-10 19:25:38 +04:00
. name = " cpufreq-ux500 " ,
2012-10-10 15:42:24 +04:00
. owner = THIS_MODULE ,
} ,
2012-12-10 19:25:38 +04:00
. probe = dbx500_cpufreq_probe ,
2012-10-10 15:42:24 +04:00
} ;
2012-12-10 19:25:38 +04:00
static int __init dbx500_cpufreq_register ( void )
2010-12-08 17:13:42 +03:00
{
2012-01-23 14:54:44 +04:00
if ( ! cpu_is_u8500_family ( ) )
2011-05-15 21:19:51 +04:00
return - ENODEV ;
2010-12-08 17:13:42 +03:00
2012-12-10 19:25:38 +04:00
pr_info ( " cpufreq for DBX500 started \n " ) ;
return platform_driver_register ( & dbx500_cpufreq_plat_driver ) ;
2010-12-08 17:13:42 +03:00
}
2012-12-10 19:25:38 +04:00
device_initcall ( dbx500_cpufreq_register ) ;
2012-10-10 15:42:24 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2012-12-10 19:25:38 +04:00
MODULE_DESCRIPTION ( " cpufreq driver for DBX500 " ) ;