2005-07-10 22:58:20 +04:00
/*
* CPU frequency scaling for OMAP
*
* Copyright ( C ) 2005 Nokia Corporation
* Written by Tony Lindgren < tony @ atomide . com >
*
* Based on cpu - sa1110 . c , Copyright ( C ) 2001 Russell King
*
2010-08-12 04:02:43 +04:00
* Copyright ( C ) 2007 - 2011 Texas Instruments , Inc .
* - OMAP3 / 4 support by Rajendra Nayak , Santosh Shilimkar
*
2005-07-10 22:58:20 +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 .
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/cpufreq.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/err.h>
2006-01-07 19:15:52 +03:00
# include <linux/clk.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2010-08-12 04:02:43 +04:00
# include <linux/opp.h>
2005-07-10 22:58:20 +04:00
# include <asm/system.h>
2010-08-12 04:02:43 +04:00
# include <asm/smp_plat.h>
2005-07-10 22:58:20 +04:00
2010-08-12 04:02:43 +04:00
# include <plat/clock.h>
# include <plat/omap-pm.h>
# include <plat/common.h>
2006-06-27 03:16:17 +04:00
2010-08-12 04:02:43 +04:00
# include <mach/hardware.h>
2009-01-28 05:13:38 +03:00
2010-08-12 04:02:43 +04:00
# define VERY_HI_RATE 900000000
2006-06-27 03:16:17 +04:00
2010-08-12 04:02:43 +04:00
static struct cpufreq_frequency_table * freq_table ;
2007-08-30 13:46:39 +04:00
static struct clk * mpu_clk ;
2010-10-08 21:00:19 +04:00
static int omap_verify_speed ( struct cpufreq_policy * policy )
2005-07-10 22:58:20 +04:00
{
2009-01-28 05:13:38 +03:00
if ( freq_table )
return cpufreq_frequency_table_verify ( policy , freq_table ) ;
2005-07-10 22:58:20 +04:00
if ( policy - > cpu )
return - EINVAL ;
cpufreq_verify_within_limits ( policy , policy - > cpuinfo . min_freq ,
policy - > cpuinfo . max_freq ) ;
2007-08-30 13:46:39 +04:00
2005-07-10 22:58:20 +04:00
policy - > min = clk_round_rate ( mpu_clk , policy - > min * 1000 ) / 1000 ;
policy - > max = clk_round_rate ( mpu_clk , policy - > max * 1000 ) / 1000 ;
cpufreq_verify_within_limits ( policy , policy - > cpuinfo . min_freq ,
policy - > cpuinfo . max_freq ) ;
return 0 ;
}
2010-10-08 21:00:19 +04:00
static unsigned int omap_getspeed ( unsigned int cpu )
2005-07-10 22:58:20 +04:00
{
unsigned long rate ;
if ( cpu )
return 0 ;
rate = clk_get_rate ( mpu_clk ) / 1000 ;
return rate ;
}
static int omap_target ( struct cpufreq_policy * policy ,
unsigned int target_freq ,
unsigned int relation )
{
int ret = 0 ;
2010-08-12 04:02:43 +04:00
struct cpufreq_freqs freqs ;
2005-07-10 22:58:20 +04:00
2009-01-28 05:13:38 +03:00
/* Ensure desired rate is within allowed range. Some govenors
* ( ondemand ) will just pass target_freq = 0 to get the minimum . */
2009-06-23 13:53:29 +04:00
if ( target_freq < policy - > min )
target_freq = policy - > min ;
if ( target_freq > policy - > max )
target_freq = policy - > max ;
2009-01-28 05:13:38 +03:00
2005-07-10 22:58:20 +04:00
freqs . old = omap_getspeed ( 0 ) ;
freqs . new = clk_round_rate ( mpu_clk , target_freq * 1000 ) / 1000 ;
freqs . cpu = 0 ;
2009-01-28 05:13:38 +03:00
if ( freqs . old = = freqs . new )
return ret ;
2005-07-10 22:58:20 +04:00
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
2010-08-12 04:02:43 +04:00
2009-01-28 05:13:38 +03:00
# ifdef CONFIG_CPU_FREQ_DEBUG
2010-08-12 04:02:43 +04:00
pr_info ( " cpufreq-omap: transition: %u --> %u \n " , freqs . old , freqs . new ) ;
2009-01-28 05:13:38 +03:00
# endif
2010-08-12 04:02:43 +04:00
2009-01-28 05:13:38 +03:00
ret = clk_set_rate ( mpu_clk , freqs . new * 1000 ) ;
2010-08-12 04:02:43 +04:00
2005-07-10 22:58:20 +04:00
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
return ret ;
}
2011-03-03 01:41:46 +03:00
static int __cpuinit omap_cpu_init ( struct cpufreq_policy * policy )
2005-07-10 22:58:20 +04:00
{
2009-01-28 05:13:38 +03:00
int result = 0 ;
2010-08-12 04:02:43 +04:00
struct device * mpu_dev ;
if ( cpu_is_omap24xx ( ) )
mpu_clk = clk_get ( NULL , " virt_prcm_set " ) ;
else if ( cpu_is_omap34xx ( ) )
mpu_clk = clk_get ( NULL , " dpll1_ck " ) ;
else if ( cpu_is_omap44xx ( ) )
mpu_clk = clk_get ( NULL , " dpll_mpu_ck " ) ;
2009-01-28 05:13:38 +03:00
2005-07-10 22:58:20 +04:00
if ( IS_ERR ( mpu_clk ) )
return PTR_ERR ( mpu_clk ) ;
if ( policy - > cpu ! = 0 )
return - EINVAL ;
2009-01-28 05:13:38 +03:00
2005-07-10 22:58:20 +04:00
policy - > cur = policy - > min = policy - > max = omap_getspeed ( 0 ) ;
2009-01-28 05:13:38 +03:00
2010-08-12 04:02:43 +04:00
mpu_dev = omap2_get_mpuss_device ( ) ;
if ( ! mpu_dev ) {
pr_warning ( " %s: unable to get the mpu device \n " , __func__ ) ;
return - EINVAL ;
}
opp_init_cpufreq_table ( mpu_dev , & freq_table ) ;
2009-01-28 05:13:38 +03:00
if ( freq_table ) {
result = cpufreq_frequency_table_cpuinfo ( policy , freq_table ) ;
if ( ! result )
cpufreq_frequency_table_get_attr ( freq_table ,
policy - > cpu ) ;
} else {
policy - > cpuinfo . min_freq = clk_round_rate ( mpu_clk , 0 ) / 1000 ;
policy - > cpuinfo . max_freq = clk_round_rate ( mpu_clk ,
VERY_HI_RATE ) / 1000 ;
}
2010-08-12 04:02:43 +04:00
policy - > min = policy - > cpuinfo . min_freq ;
policy - > max = policy - > cpuinfo . max_freq ;
policy - > cur = omap_getspeed ( 0 ) ;
2009-01-28 05:13:38 +03:00
/* FIXME: what's the actual transition time? */
2009-11-11 22:00:38 +03:00
policy - > cpuinfo . transition_latency = 300 * 1000 ;
2005-07-10 22:58:20 +04:00
return 0 ;
}
2007-08-30 13:46:39 +04:00
static int omap_cpu_exit ( struct cpufreq_policy * policy )
{
2010-01-09 01:23:16 +03:00
clk_exit_cpufreq_table ( & freq_table ) ;
2007-08-30 13:46:39 +04:00
clk_put ( mpu_clk ) ;
return 0 ;
}
2009-01-28 05:13:38 +03:00
static struct freq_attr * omap_cpufreq_attr [ ] = {
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
} ;
2005-07-10 22:58:20 +04:00
static struct cpufreq_driver omap_driver = {
. flags = CPUFREQ_STICKY ,
. verify = omap_verify_speed ,
. target = omap_target ,
. get = omap_getspeed ,
. init = omap_cpu_init ,
2007-08-30 13:46:39 +04:00
. exit = omap_cpu_exit ,
2005-07-10 22:58:20 +04:00
. name = " omap " ,
2009-01-28 05:13:38 +03:00
. attr = omap_cpufreq_attr ,
2005-07-10 22:58:20 +04:00
} ;
static int __init omap_cpufreq_init ( void )
{
return cpufreq_register_driver ( & omap_driver ) ;
}
2010-08-12 04:02:43 +04:00
static void __exit omap_cpufreq_exit ( void )
{
cpufreq_unregister_driver ( & omap_driver ) ;
}
2009-01-28 05:13:38 +03:00
2010-08-12 04:02:43 +04:00
MODULE_DESCRIPTION ( " cpufreq driver for OMAP SoCs " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( omap_cpufreq_init ) ;
module_exit ( omap_cpufreq_exit ) ;