2007-06-04 18:10:57 +04:00
/*
* Copyright ( C ) 2004 - 2007 Atmel Corporation
*
* Based on MIPS implementation arch / mips / kernel / time . c
* Copyright 2001 MontaVista Software Inc .
*
* 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 .
*/
/*#define DEBUG*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/cpufreq.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/err.h>
2011-08-01 20:55:26 +04:00
# include <linux/export.h>
2013-09-16 17:26:41 +04:00
# include <linux/slab.h>
2007-06-04 18:10:57 +04:00
2013-09-16 17:26:41 +04:00
static struct cpufreq_frequency_table * freq_table ;
2007-06-04 18:10:57 +04:00
2008-10-23 13:23:08 +04:00
static unsigned int ref_freq ;
static unsigned long loops_per_jiffy_ref ;
2013-10-25 18:15:48 +04:00
static int at32_set_target ( struct cpufreq_policy * policy , unsigned int index )
2007-06-04 18:10:57 +04:00
{
2013-08-14 18:08:24 +04:00
unsigned int old_freq , new_freq ;
2007-06-04 18:10:57 +04:00
2014-01-09 19:08:43 +04:00
old_freq = policy - > cur ;
2013-08-14 18:08:24 +04:00
new_freq = freq_table [ index ] . frequency ;
2007-06-04 18:10:57 +04:00
2008-10-23 13:23:08 +04:00
if ( ! ref_freq ) {
2013-08-14 18:08:24 +04:00
ref_freq = old_freq ;
2008-10-23 13:23:08 +04:00
loops_per_jiffy_ref = boot_cpu_data . loops_per_jiffy ;
}
2013-08-14 18:08:24 +04:00
if ( old_freq < new_freq )
2008-10-23 13:23:08 +04:00
boot_cpu_data . loops_per_jiffy = cpufreq_scale (
2013-08-14 18:08:24 +04:00
loops_per_jiffy_ref , ref_freq , new_freq ) ;
2014-01-09 19:08:43 +04:00
clk_set_rate ( policy - > clk , new_freq * 1000 ) ;
2013-08-14 18:08:24 +04:00
if ( new_freq < old_freq )
2008-10-23 13:23:08 +04:00
boot_cpu_data . loops_per_jiffy = cpufreq_scale (
2013-08-14 18:08:24 +04:00
loops_per_jiffy_ref , ref_freq , new_freq ) ;
2007-06-04 18:10:57 +04:00
return 0 ;
}
2013-12-05 13:59:57 +04:00
static int at32_cpufreq_driver_init ( struct cpufreq_policy * policy )
2007-06-04 18:10:57 +04:00
{
2013-10-03 18:58:33 +04:00
unsigned int frequency , rate , min_freq ;
2014-04-03 18:50:36 +04:00
struct clk * cpuclk ;
2013-09-16 17:26:41 +04:00
int retval , steps , i ;
2007-06-04 18:10:57 +04:00
if ( policy - > cpu ! = 0 )
return - EINVAL ;
cpuclk = clk_get ( NULL , " cpu " ) ;
if ( IS_ERR ( cpuclk ) ) {
pr_debug ( " cpufreq: could not get CPU clk \n " ) ;
2013-09-16 17:26:41 +04:00
retval = PTR_ERR ( cpuclk ) ;
goto out_err ;
2007-06-04 18:10:57 +04:00
}
2013-10-03 18:58:33 +04:00
min_freq = ( clk_round_rate ( cpuclk , 1 ) + 500 ) / 1000 ;
frequency = ( clk_round_rate ( cpuclk , ~ 0UL ) + 500 ) / 1000 ;
2007-06-04 18:10:57 +04:00
policy - > cpuinfo . transition_latency = 0 ;
2013-09-16 17:26:41 +04:00
/*
* AVR32 CPU frequency rate scales in power of two between maximum and
* minimum , also add space for the table end marker .
*
* Further validate that the frequency is usable , and append it to the
* frequency table .
*/
2013-10-03 18:58:33 +04:00
steps = fls ( frequency / min_freq ) + 1 ;
2013-09-16 17:26:41 +04:00
freq_table = kzalloc ( steps * sizeof ( struct cpufreq_frequency_table ) ,
GFP_KERNEL ) ;
if ( ! freq_table ) {
retval = - ENOMEM ;
goto out_err_put_clk ;
}
2007-06-04 18:10:57 +04:00
2013-09-16 17:26:41 +04:00
for ( i = 0 ; i < ( steps - 1 ) ; i + + ) {
rate = clk_round_rate ( cpuclk , frequency * 1000 ) / 1000 ;
if ( rate ! = frequency )
freq_table [ i ] . frequency = CPUFREQ_ENTRY_INVALID ;
else
freq_table [ i ] . frequency = frequency ;
frequency / = 2 ;
}
2014-01-09 19:08:43 +04:00
policy - > clk = cpuclk ;
2013-09-16 17:26:41 +04:00
freq_table [ steps - 1 ] . frequency = CPUFREQ_TABLE_END ;
retval = cpufreq_table_validate_and_show ( policy , freq_table ) ;
if ( ! retval ) {
printk ( " cpufreq: AT32AP CPU frequency driver \n " ) ;
return 0 ;
}
kfree ( freq_table ) ;
out_err_put_clk :
clk_put ( cpuclk ) ;
out_err :
return retval ;
2007-06-04 18:10:57 +04:00
}
static struct cpufreq_driver at32_driver = {
. name = " at32ap " ,
. init = at32_cpufreq_driver_init ,
2013-10-03 18:57:58 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = at32_set_target ,
2014-01-09 19:08:43 +04:00
. get = cpufreq_generic_get ,
2007-06-04 18:10:57 +04:00
. flags = CPUFREQ_STICKY ,
} ;
static int __init at32_cpufreq_init ( void )
{
return cpufreq_register_driver ( & at32_driver ) ;
}
2008-05-27 11:37:42 +04:00
late_initcall ( at32_cpufreq_init ) ;