2008-04-25 00:58:29 +04:00
/*
2009-09-24 18:11:24 +04:00
* Blackfin core clock scaling
2008-04-25 00:58:29 +04:00
*
2011-03-01 00:23:36 +03:00
* Copyright 2008 - 2011 Analog Devices Inc .
2008-04-25 00:58:29 +04:00
*
2009-09-24 18:11:24 +04:00
* Licensed under the GPL - 2 or later .
2008-04-25 00:58:29 +04:00
*/
# include <linux/kernel.h>
2011-08-10 00:54:30 +04:00
# include <linux/module.h>
2008-04-25 00:58:29 +04:00
# include <linux/types.h>
# include <linux/init.h>
2012-05-16 13:49:52 +04:00
# include <linux/clk.h>
2008-04-25 00:58:29 +04:00
# include <linux/cpufreq.h>
# include <linux/fs.h>
2010-03-08 06:01:35 +03:00
# include <linux/delay.h>
2008-04-25 00:58:29 +04:00
# include <asm/blackfin.h>
# include <asm/time.h>
2009-10-15 21:12:05 +04:00
# include <asm/dpmc.h>
2008-04-25 00:58:29 +04:00
2012-05-16 13:49:52 +04:00
2008-04-25 00:58:29 +04:00
/* this is the table of CCLK frequencies, in Hz */
2011-03-01 00:23:36 +03:00
/* .index is the entry in the auxiliary dpm_state_table[] */
2008-04-25 00:58:29 +04:00
static struct cpufreq_frequency_table bfin_freq_table [ ] = {
{
. frequency = CPUFREQ_TABLE_END ,
. index = 0 ,
} ,
{
. frequency = CPUFREQ_TABLE_END ,
. index = 1 ,
} ,
{
. frequency = CPUFREQ_TABLE_END ,
. index = 2 ,
} ,
{
. frequency = CPUFREQ_TABLE_END ,
. index = 0 ,
} ,
} ;
static struct bfin_dpm_state {
unsigned int csel ; /* system clock divider */
unsigned int tscale ; /* change the divider on the core timer interrupt */
} dpm_state_table [ 3 ] ;
2010-01-27 14:16:32 +03:00
# if defined(CONFIG_CYCLES_CLOCKSOURCE)
2008-05-07 07:41:26 +04:00
/*
2011-03-01 00:23:36 +03:00
* normalized to maximum frequency offset for CYCLES ,
2010-01-27 14:16:32 +03:00
* used in time - ts cycles clock source , but could be used
* somewhere also .
2008-05-07 07:41:26 +04:00
*/
unsigned long long __bfin_cycles_off ;
unsigned int __bfin_cycles_mod ;
2010-01-27 14:16:32 +03:00
# endif
2008-05-07 07:41:26 +04:00
2008-04-25 00:58:29 +04:00
/**************************************************************************/
2010-01-27 14:16:32 +03:00
static void __init bfin_init_tables ( unsigned long cclk , unsigned long sclk )
{
2008-04-25 00:58:29 +04:00
2010-01-27 14:16:32 +03:00
unsigned long csel , min_cclk ;
int index ;
/* Anomaly 273 seems to still exist on non-BF54x w/dcache turned on */
# if ANOMALY_05000273 || ANOMALY_05000274 || \
( ! defined ( CONFIG_BF54x ) & & defined ( CONFIG_BFIN_EXTMEM_DCACHEABLE ) )
min_cclk = sclk * 2 ;
# else
min_cclk = sclk ;
# endif
2012-05-16 13:49:52 +04:00
# ifndef CONFIG_BF60x
2010-01-27 14:16:32 +03:00
csel = ( ( bfin_read_PLL_DIV ( ) & CSEL ) > > 4 ) ;
2012-05-16 13:49:52 +04:00
# else
csel = bfin_read32 ( CGU0_DIV ) & 0x1F ;
# endif
2010-01-27 14:16:32 +03:00
for ( index = 0 ; ( cclk > > index ) > = min_cclk & & csel < = 3 ; index + + , csel + + ) {
bfin_freq_table [ index ] . frequency = cclk > > index ;
2012-05-16 13:49:52 +04:00
# ifndef CONFIG_BF60x
2010-01-27 14:16:32 +03:00
dpm_state_table [ index ] . csel = csel < < 4 ; /* Shift now into PLL_DIV bitpos */
dpm_state_table [ index ] . tscale = ( TIME_SCALE / ( 1 < < csel ) ) - 1 ;
2012-05-16 13:49:52 +04:00
# else
dpm_state_table [ index ] . csel = csel ;
dpm_state_table [ index ] . tscale = TIME_SCALE > > index ;
# endif
2010-01-27 14:16:32 +03:00
pr_debug ( " cpufreq: freq:%d csel:0x%x tscale:%d \n " ,
bfin_freq_table [ index ] . frequency ,
dpm_state_table [ index ] . csel ,
dpm_state_table [ index ] . tscale ) ;
}
return ;
}
static void bfin_adjust_core_timer ( void * info )
2008-04-25 00:58:29 +04:00
{
2010-01-27 14:16:32 +03:00
unsigned int tscale ;
unsigned int index = * ( unsigned int * ) info ;
/* we have to adjust the core timer, because it is using cclk */
tscale = dpm_state_table [ index ] . tscale ;
bfin_write_TSCALE ( tscale ) ;
return ;
}
2008-04-25 00:58:29 +04:00
2010-01-27 14:16:32 +03:00
static unsigned int bfin_getfreq_khz ( unsigned int cpu )
{
/* Both CoreA/B have the same core clock */
2008-10-28 09:18:29 +03:00
return get_cclk ( ) / 1000 ;
2008-04-25 00:58:29 +04:00
}
2012-05-16 13:49:52 +04:00
unsigned long cpu_set_cclk ( int cpu , unsigned long new )
{
struct clk * clk ;
int ret ;
clk = clk_get ( NULL , " CCLK " ) ;
if ( IS_ERR ( clk ) )
return - ENODEV ;
ret = clk_set_rate ( clk , new ) ;
clk_put ( clk ) ;
return ret ;
}
2010-01-27 14:16:32 +03:00
static int bfin_target ( struct cpufreq_policy * poli ,
2008-04-25 00:58:29 +04:00
unsigned int target_freq , unsigned int relation )
{
2010-01-27 14:16:32 +03:00
unsigned int index , plldiv , cpu ;
2008-04-25 00:58:29 +04:00
unsigned long flags , cclk_hz ;
struct cpufreq_freqs freqs ;
2010-03-08 06:01:35 +03:00
static unsigned long lpj_ref ;
static unsigned int lpj_ref_freq ;
2012-05-16 13:49:52 +04:00
int ret = 0 ;
2010-03-08 06:01:35 +03:00
2010-01-27 14:16:32 +03:00
# if defined(CONFIG_CYCLES_CLOCKSOURCE)
2008-05-07 07:41:26 +04:00
cycles_t cycles ;
2010-01-27 14:16:32 +03:00
# endif
2008-04-25 00:58:29 +04:00
2010-01-27 14:16:32 +03:00
for_each_online_cpu ( cpu ) {
struct cpufreq_policy * policy = cpufreq_cpu_get ( cpu ) ;
if ( ! policy )
continue ;
if ( cpufreq_frequency_table_target ( policy , bfin_freq_table ,
target_freq , relation , & index ) )
return - EINVAL ;
cclk_hz = bfin_freq_table [ index ] . frequency ;
freqs . old = bfin_getfreq_khz ( 0 ) ;
freqs . new = cclk_hz ;
freqs . cpu = cpu ;
pr_debug ( " cpufreq: changing cclk to %lu; target = %u, oldfreq = %u \n " ,
cclk_hz , target_freq , freqs . old ) ;
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
if ( cpu = = CPUFREQ_CPU ) {
2010-10-07 17:08:52 +04:00
flags = hard_local_irq_save ( ) ;
2012-05-16 13:49:52 +04:00
# ifndef CONFIG_BF60x
2010-01-27 14:16:32 +03:00
plldiv = ( bfin_read_PLL_DIV ( ) & SSEL ) |
dpm_state_table [ index ] . csel ;
bfin_write_PLL_DIV ( plldiv ) ;
2012-05-16 13:49:52 +04:00
# else
ret = cpu_set_cclk ( cpu , freqs . new * 1000 ) ;
if ( ret ! = 0 ) {
pr_debug ( " cpufreq set freq failed %d \n " , ret ) ;
break ;
}
# endif
2010-01-27 14:16:32 +03:00
on_each_cpu ( bfin_adjust_core_timer , & index , 1 ) ;
# if defined(CONFIG_CYCLES_CLOCKSOURCE)
cycles = get_cycles ( ) ;
SSYNC ( ) ;
cycles + = 10 ; /* ~10 cycles we lose after get_cycles() */
__bfin_cycles_off + =
( cycles < < __bfin_cycles_mod ) - ( cycles < < index ) ;
__bfin_cycles_mod = index ;
# endif
2010-03-08 06:01:35 +03:00
if ( ! lpj_ref_freq ) {
lpj_ref = loops_per_jiffy ;
lpj_ref_freq = freqs . old ;
}
if ( freqs . new ! = freqs . old ) {
loops_per_jiffy = cpufreq_scale ( lpj_ref ,
lpj_ref_freq , freqs . new ) ;
}
2010-10-07 17:08:52 +04:00
hard_local_irq_restore ( flags ) ;
2010-01-27 14:16:32 +03:00
}
/* TODO: just test case for cycles clock source, remove later */
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
}
2008-04-25 00:58:29 +04:00
2010-01-27 14:16:32 +03:00
pr_debug ( " cpufreq: done \n " ) ;
2012-05-16 13:49:52 +04:00
return ret ;
2008-04-25 00:58:29 +04:00
}
static int bfin_verify_speed ( struct cpufreq_policy * policy )
{
return cpufreq_frequency_table_verify ( policy , bfin_freq_table ) ;
}
2012-05-16 13:49:52 +04:00
static int __bfin_cpu_init ( struct cpufreq_policy * policy )
2008-04-25 00:58:29 +04:00
{
2010-01-27 14:16:32 +03:00
unsigned long cclk , sclk ;
2008-04-25 00:58:29 +04:00
2008-10-28 09:18:29 +03:00
cclk = get_cclk ( ) / 1000 ;
sclk = get_sclk ( ) / 1000 ;
2008-04-25 00:58:29 +04:00
2010-01-27 14:16:32 +03:00
if ( policy - > cpu = = CPUFREQ_CPU )
bfin_init_tables ( cclk , sclk ) ;
2008-04-25 00:58:29 +04:00
2009-09-25 13:03:21 +04:00
policy - > cpuinfo . transition_latency = 50000 ; /* 50us assumed */
2008-04-25 00:58:29 +04:00
policy - > cur = cclk ;
cpufreq_frequency_table_get_attr ( bfin_freq_table , policy - > cpu ) ;
return cpufreq_frequency_table_cpuinfo ( policy , bfin_freq_table ) ;
}
static struct freq_attr * bfin_freq_attr [ ] = {
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
} ;
static struct cpufreq_driver bfin_driver = {
. verify = bfin_verify_speed ,
. target = bfin_target ,
2008-10-28 09:18:29 +03:00
. get = bfin_getfreq_khz ,
2008-04-25 00:58:29 +04:00
. init = __bfin_cpu_init ,
. name = " bfin cpufreq " ,
. owner = THIS_MODULE ,
. attr = bfin_freq_attr ,
} ;
static int __init bfin_cpu_init ( void )
{
return cpufreq_register_driver ( & bfin_driver ) ;
}
static void __exit bfin_cpu_exit ( void )
{
cpufreq_unregister_driver ( & bfin_driver ) ;
}
MODULE_AUTHOR ( " Michael Hennerich <hennerich@blackfin.uclinux.org> " ) ;
MODULE_DESCRIPTION ( " cpufreq driver for Blackfin " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( bfin_cpu_init ) ;
module_exit ( bfin_cpu_exit ) ;