2007-06-04 16:10:57 +02: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 12:55:26 -04:00
# include <linux/export.h>
2013-09-16 18:56:41 +05:30
# include <linux/slab.h>
2007-06-04 16:10:57 +02:00
2013-09-16 18:56:41 +05:30
static struct cpufreq_frequency_table * freq_table ;
2007-06-04 16:10:57 +02:00
2008-10-23 11:23:08 +02:00
static unsigned int ref_freq ;
static unsigned long loops_per_jiffy_ref ;
2013-10-25 19:45:48 +05:30
static int at32_set_target ( struct cpufreq_policy * policy , unsigned int index )
2007-06-04 16:10:57 +02:00
{
2013-08-14 19:38:24 +05:30
unsigned int old_freq , new_freq ;
2007-06-04 16:10:57 +02:00
2014-01-09 20:38:43 +05:30
old_freq = policy - > cur ;
2013-08-14 19:38:24 +05:30
new_freq = freq_table [ index ] . frequency ;
2007-06-04 16:10:57 +02:00
2008-10-23 11:23:08 +02:00
if ( ! ref_freq ) {
2013-08-14 19:38:24 +05:30
ref_freq = old_freq ;
2008-10-23 11:23:08 +02:00
loops_per_jiffy_ref = boot_cpu_data . loops_per_jiffy ;
}
2013-08-14 19:38:24 +05:30
if ( old_freq < new_freq )
2008-10-23 11:23:08 +02:00
boot_cpu_data . loops_per_jiffy = cpufreq_scale (
2013-08-14 19:38:24 +05:30
loops_per_jiffy_ref , ref_freq , new_freq ) ;
2014-01-09 20:38:43 +05:30
clk_set_rate ( policy - > clk , new_freq * 1000 ) ;
2013-08-14 19:38:24 +05:30
if ( new_freq < old_freq )
2008-10-23 11:23:08 +02:00
boot_cpu_data . loops_per_jiffy = cpufreq_scale (
2013-08-14 19:38:24 +05:30
loops_per_jiffy_ref , ref_freq , new_freq ) ;
2007-06-04 16:10:57 +02:00
return 0 ;
}
2013-12-05 10:59:57 +01:00
static int at32_cpufreq_driver_init ( struct cpufreq_policy * policy )
2007-06-04 16:10:57 +02:00
{
2013-10-03 20:28:33 +05:30
unsigned int frequency , rate , min_freq ;
2014-04-03 20:20:36 +05:30
struct clk * cpuclk ;
2013-09-16 18:56:41 +05:30
int retval , steps , i ;
2007-06-04 16:10:57 +02: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 18:56:41 +05:30
retval = PTR_ERR ( cpuclk ) ;
goto out_err ;
2007-06-04 16:10:57 +02:00
}
2013-10-03 20:28:33 +05:30
min_freq = ( clk_round_rate ( cpuclk , 1 ) + 500 ) / 1000 ;
frequency = ( clk_round_rate ( cpuclk , ~ 0UL ) + 500 ) / 1000 ;
2007-06-04 16:10:57 +02:00
policy - > cpuinfo . transition_latency = 0 ;
2013-09-16 18:56:41 +05:30
/*
* 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 20:28:33 +05:30
steps = fls ( frequency / min_freq ) + 1 ;
2013-09-16 18:56:41 +05:30
freq_table = kzalloc ( steps * sizeof ( struct cpufreq_frequency_table ) ,
GFP_KERNEL ) ;
if ( ! freq_table ) {
retval = - ENOMEM ;
goto out_err_put_clk ;
}
2007-06-04 16:10:57 +02:00
2013-09-16 18:56:41 +05:30
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 20:38:43 +05:30
policy - > clk = cpuclk ;
2013-09-16 18:56:41 +05:30
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 16:10:57 +02:00
}
static struct cpufreq_driver at32_driver = {
. name = " at32ap " ,
. init = at32_cpufreq_driver_init ,
2013-10-03 20:27:58 +05:30
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 19:45:48 +05:30
. target_index = at32_set_target ,
2014-01-09 20:38:43 +05:30
. get = cpufreq_generic_get ,
2007-06-04 16:10:57 +02:00
. flags = CPUFREQ_STICKY ,
} ;
static int __init at32_cpufreq_init ( void )
{
return cpufreq_register_driver ( & at32_driver ) ;
}
2008-05-27 09:37:42 +02:00
late_initcall ( at32_cpufreq_init ) ;