2007-11-29 19:11:23 +03:00
# include <linux/init.h>
# include <linux/module.h>
# include <linux/cpufreq.h>
# include <hwregs/reg_map.h>
# include <hwregs/reg_rdwr.h>
# include <hwregs/clkgen_defs.h>
# include <hwregs/ddr2_defs.h>
static int
cris_sdram_freq_notifier ( struct notifier_block * nb , unsigned long val ,
void * data ) ;
static struct notifier_block cris_sdram_freq_notifier_block = {
. notifier_call = cris_sdram_freq_notifier
} ;
static struct cpufreq_frequency_table cris_freq_table [ ] = {
{ 0x01 , 6000 } ,
{ 0x02 , 200000 } ,
{ 0 , CPUFREQ_TABLE_END } ,
} ;
static unsigned int cris_freq_get_cpu_frequency ( unsigned int cpu )
{
reg_clkgen_rw_clk_ctrl clk_ctrl ;
clk_ctrl = REG_RD ( clkgen , regi_clkgen , rw_clk_ctrl ) ;
return clk_ctrl . pll ? 200000 : 6000 ;
}
2013-03-24 10:26:43 +04:00
static void cris_freq_set_cpu_state ( struct cpufreq_policy * policy ,
unsigned int state )
2007-11-29 19:11:23 +03:00
{
struct cpufreq_freqs freqs ;
reg_clkgen_rw_clk_ctrl clk_ctrl ;
clk_ctrl = REG_RD ( clkgen , regi_clkgen , rw_clk_ctrl ) ;
2013-03-24 10:26:43 +04:00
freqs . old = cris_freq_get_cpu_frequency ( policy - > cpu ) ;
freqs . new = cris_freq_table [ state ] . frequency ;
2007-11-29 19:11:23 +03:00
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_PRECHANGE ) ;
2007-11-29 19:11:23 +03:00
local_irq_disable ( ) ;
/* Even though we may be SMP they will share the same clock
* so all settings are made on CPU0 . */
if ( cris_freq_table [ state ] . frequency = = 200000 )
clk_ctrl . pll = 1 ;
else
clk_ctrl . pll = 0 ;
REG_WR ( clkgen , regi_clkgen , rw_clk_ctrl , clk_ctrl ) ;
local_irq_enable ( ) ;
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_POSTCHANGE ) ;
2007-11-29 19:11:23 +03:00
} ;
static int cris_freq_verify ( struct cpufreq_policy * policy )
{
return cpufreq_frequency_table_verify ( policy , & cris_freq_table [ 0 ] ) ;
}
static int cris_freq_target ( struct cpufreq_policy * policy ,
unsigned int target_freq ,
unsigned int relation )
{
unsigned int newstate = 0 ;
if ( cpufreq_frequency_table_target ( policy , cris_freq_table ,
target_freq , relation , & newstate ) )
return - EINVAL ;
2013-03-24 10:26:43 +04:00
cris_freq_set_cpu_state ( policy , newstate ) ;
2007-11-29 19:11:23 +03:00
return 0 ;
}
static int cris_freq_cpu_init ( struct cpufreq_policy * policy )
{
int result ;
/* cpuinfo and default policy values */
policy - > cpuinfo . transition_latency = 1000000 ; /* 1ms */
policy - > cur = cris_freq_get_cpu_frequency ( 0 ) ;
result = cpufreq_frequency_table_cpuinfo ( policy , cris_freq_table ) ;
if ( result )
return ( result ) ;
cpufreq_frequency_table_get_attr ( cris_freq_table , policy - > cpu ) ;
return 0 ;
}
static int cris_freq_cpu_exit ( struct cpufreq_policy * policy )
{
cpufreq_frequency_table_put_attr ( policy - > cpu ) ;
return 0 ;
}
static struct freq_attr * cris_freq_attr [ ] = {
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
} ;
static struct cpufreq_driver cris_freq_driver = {
. get = cris_freq_get_cpu_frequency ,
. verify = cris_freq_verify ,
. target = cris_freq_target ,
. init = cris_freq_cpu_init ,
. exit = cris_freq_cpu_exit ,
. name = " cris_freq " ,
. attr = cris_freq_attr ,
} ;
static int __init cris_freq_init ( void )
{
int ret ;
ret = cpufreq_register_driver ( & cris_freq_driver ) ;
cpufreq_register_notifier ( & cris_sdram_freq_notifier_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
return ret ;
}
static int
cris_sdram_freq_notifier ( struct notifier_block * nb , unsigned long val ,
void * data )
{
int i ;
struct cpufreq_freqs * freqs = data ;
if ( val = = CPUFREQ_PRECHANGE ) {
reg_ddr2_rw_cfg cfg =
REG_RD ( ddr2 , regi_ddr2_ctrl , rw_cfg ) ;
cfg . ref_interval = ( freqs - > new = = 200000 ? 1560 : 46 ) ;
if ( freqs - > new = = 200000 )
for ( i = 0 ; i < 50000 ; i + + ) ;
REG_WR ( bif_core , regi_bif_core , rw_sdram_timing , timing ) ;
}
return 0 ;
}
module_init ( cris_freq_init ) ;