2009-09-22 21:14:00 +05:30
/*
* CPU frequency scaling for DaVinci
*
* Copyright ( C ) 2009 Texas Instruments Incorporated - http : //www.ti.com/
*
* Based on linux / arch / arm / plat - omap / cpu - omap . c . Original Copyright follows :
*
* Copyright ( C ) 2005 Nokia Corporation
* Written by Tony Lindgren < tony @ atomide . com >
*
* Based on cpu - sa1110 . c , Copyright ( C ) 2001 Russell King
*
* Copyright ( C ) 2007 - 2008 Texas Instruments , Inc .
* Updated to support OMAP3
* Rajendra Nayak < rnayak @ ti . 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/types.h>
# include <linux/cpufreq.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/platform_device.h>
2011-07-31 16:17:29 -04:00
# include <linux/export.h>
2009-09-22 21:14:00 +05:30
# include <mach/hardware.h>
# include <mach/cpufreq.h>
# include <mach/common.h>
struct davinci_cpufreq {
struct device * dev ;
struct clk * armclk ;
2010-07-20 16:46:50 +05:30
struct clk * asyncclk ;
unsigned long asyncrate ;
2009-09-22 21:14:00 +05:30
} ;
static struct davinci_cpufreq cpufreq ;
static int davinci_verify_speed ( struct cpufreq_policy * policy )
{
struct davinci_cpufreq_config * pdata = cpufreq . dev - > platform_data ;
struct cpufreq_frequency_table * freq_table = pdata - > freq_table ;
struct clk * armclk = cpufreq . armclk ;
if ( freq_table )
return cpufreq_frequency_table_verify ( policy , freq_table ) ;
if ( policy - > cpu )
return - EINVAL ;
cpufreq_verify_within_limits ( policy , policy - > cpuinfo . min_freq ,
policy - > cpuinfo . max_freq ) ;
policy - > min = clk_round_rate ( armclk , policy - > min * 1000 ) / 1000 ;
policy - > max = clk_round_rate ( armclk , policy - > max * 1000 ) / 1000 ;
cpufreq_verify_within_limits ( policy , policy - > cpuinfo . min_freq ,
policy - > cpuinfo . max_freq ) ;
return 0 ;
}
static unsigned int davinci_getspeed ( unsigned int cpu )
{
if ( cpu )
return 0 ;
return clk_get_rate ( cpufreq . armclk ) / 1000 ;
}
static int davinci_target ( struct cpufreq_policy * policy ,
unsigned int target_freq , unsigned int relation )
{
int ret = 0 ;
unsigned int idx ;
struct cpufreq_freqs freqs ;
struct davinci_cpufreq_config * pdata = cpufreq . dev - > platform_data ;
struct clk * armclk = cpufreq . armclk ;
freqs . old = davinci_getspeed ( 0 ) ;
freqs . new = clk_round_rate ( armclk , target_freq * 1000 ) / 1000 ;
if ( freqs . old = = freqs . new )
return ret ;
2012-03-08 20:36:44 +01:00
dev_dbg ( cpufreq . dev , " transition: %u --> %u \n " , freqs . old , freqs . new ) ;
2009-09-22 21:14:00 +05:30
ret = cpufreq_frequency_table_target ( policy , pdata - > freq_table ,
freqs . new , relation , & idx ) ;
if ( ret )
return - EINVAL ;
2013-03-24 11:56:43 +05:30
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_PRECHANGE ) ;
2009-09-22 21:14:00 +05:30
/* if moving to higher frequency, up the voltage beforehand */
2010-07-20 16:46:48 +05:30
if ( pdata - > set_voltage & & freqs . new > freqs . old ) {
ret = pdata - > set_voltage ( idx ) ;
if ( ret )
goto out ;
}
2009-09-22 21:14:00 +05:30
ret = clk_set_rate ( armclk , idx ) ;
2010-07-20 16:46:48 +05:30
if ( ret )
goto out ;
2009-09-22 21:14:00 +05:30
2010-07-20 16:46:50 +05:30
if ( cpufreq . asyncclk ) {
ret = clk_set_rate ( cpufreq . asyncclk , cpufreq . asyncrate ) ;
if ( ret )
goto out ;
}
2009-09-22 21:14:00 +05:30
/* if moving to lower freq, lower the voltage after lowering freq */
if ( pdata - > set_voltage & & freqs . new < freqs . old )
pdata - > set_voltage ( idx ) ;
2010-07-20 16:46:48 +05:30
out :
2013-06-19 11:18:20 +05:30
if ( ret )
freqs . new = freqs . old ;
2013-03-24 11:56:43 +05:30
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_POSTCHANGE ) ;
2009-09-22 21:14:00 +05:30
return ret ;
}
2011-02-28 15:51:33 +05:30
static int davinci_cpu_init ( struct cpufreq_policy * policy )
2009-09-22 21:14:00 +05:30
{
int result = 0 ;
struct davinci_cpufreq_config * pdata = cpufreq . dev - > platform_data ;
struct cpufreq_frequency_table * freq_table = pdata - > freq_table ;
if ( policy - > cpu ! = 0 )
return - EINVAL ;
2009-10-22 15:12:16 +05:30
/* Finish platform specific initialization */
if ( pdata - > init ) {
result = pdata - > init ( ) ;
if ( result )
return result ;
}
2013-04-01 12:57:48 +00:00
policy - > cur = davinci_getspeed ( 0 ) ;
result = cpufreq_frequency_table_cpuinfo ( policy , freq_table ) ;
if ( result ) {
pr_err ( " %s: cpufreq_frequency_table_cpuinfo() failed " ,
__func__ ) ;
return result ;
2009-09-22 21:14:00 +05:30
}
2013-04-01 12:57:48 +00:00
cpufreq_frequency_table_get_attr ( freq_table , policy - > cpu ) ;
2009-09-22 21:14:00 +05:30
/*
* Time measurement across the target ( ) function yields ~ 1500 - 1800u s
* time taken with no drivers on notification list .
2011-03-30 22:57:33 -03:00
* Setting the latency to 2000 us to accommodate addition of drivers
2009-09-22 21:14:00 +05:30
* to pre / post change notification list .
*/
policy - > cpuinfo . transition_latency = 2000 * 1000 ;
return 0 ;
}
static int davinci_cpu_exit ( struct cpufreq_policy * policy )
{
cpufreq_frequency_table_put_attr ( policy - > cpu ) ;
return 0 ;
}
static struct freq_attr * davinci_cpufreq_attr [ ] = {
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
} ;
static struct cpufreq_driver davinci_driver = {
. flags = CPUFREQ_STICKY ,
. verify = davinci_verify_speed ,
. target = davinci_target ,
. get = davinci_getspeed ,
. init = davinci_cpu_init ,
. exit = davinci_cpu_exit ,
. name = " davinci " ,
. attr = davinci_cpufreq_attr ,
} ;
static int __init davinci_cpufreq_probe ( struct platform_device * pdev )
{
struct davinci_cpufreq_config * pdata = pdev - > dev . platform_data ;
2010-07-20 16:46:50 +05:30
struct clk * asyncclk ;
2009-09-22 21:14:00 +05:30
if ( ! pdata )
return - EINVAL ;
if ( ! pdata - > freq_table )
return - EINVAL ;
cpufreq . dev = & pdev - > dev ;
cpufreq . armclk = clk_get ( NULL , " arm " ) ;
if ( IS_ERR ( cpufreq . armclk ) ) {
dev_err ( cpufreq . dev , " Unable to get ARM clock \n " ) ;
return PTR_ERR ( cpufreq . armclk ) ;
}
2010-07-20 16:46:50 +05:30
asyncclk = clk_get ( cpufreq . dev , " async " ) ;
if ( ! IS_ERR ( asyncclk ) ) {
cpufreq . asyncclk = asyncclk ;
cpufreq . asyncrate = clk_get_rate ( asyncclk ) ;
}
2009-09-22 21:14:00 +05:30
return cpufreq_register_driver ( & davinci_driver ) ;
}
static int __exit davinci_cpufreq_remove ( struct platform_device * pdev )
{
clk_put ( cpufreq . armclk ) ;
2010-07-20 16:46:50 +05:30
if ( cpufreq . asyncclk )
clk_put ( cpufreq . asyncclk ) ;
2009-09-22 21:14:00 +05:30
return cpufreq_unregister_driver ( & davinci_driver ) ;
}
static struct platform_driver davinci_cpufreq_driver = {
. driver = {
. name = " cpufreq-davinci " ,
. owner = THIS_MODULE ,
} ,
. remove = __exit_p ( davinci_cpufreq_remove ) ,
} ;
2012-04-26 09:45:39 +08:00
int __init davinci_cpufreq_init ( void )
2009-09-22 21:14:00 +05:30
{
return platform_driver_probe ( & davinci_cpufreq_driver ,
davinci_cpufreq_probe ) ;
}