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
static struct clk * cpuclk ;
2013-09-16 17:26:41 +04:00
static struct cpufreq_frequency_table * freq_table ;
2007-06-04 18:10:57 +04:00
static unsigned int at32_get_speed ( unsigned int cpu )
{
/* No SMP support */
if ( cpu )
return 0 ;
return ( unsigned int ) ( ( clk_get_rate ( cpuclk ) + 500 ) / 1000 ) ;
}
2008-10-23 13:23:08 +04:00
static unsigned int ref_freq ;
static unsigned long loops_per_jiffy_ref ;
2007-06-04 18:10:57 +04:00
static int at32_set_target ( struct cpufreq_policy * policy ,
unsigned int target_freq ,
unsigned int relation )
{
struct cpufreq_freqs freqs ;
long freq ;
/* Convert target_freq from kHz to Hz */
freq = clk_round_rate ( cpuclk , target_freq * 1000 ) ;
/* Check if policy->min <= new_freq <= policy->max */
if ( freq < ( policy - > min * 1000 ) | | freq > ( policy - > max * 1000 ) )
return - EINVAL ;
pr_debug ( " cpufreq: requested frequency %u Hz \n " , target_freq * 1000 ) ;
freqs . old = at32_get_speed ( 0 ) ;
freqs . new = ( freq + 500 ) / 1000 ;
freqs . flags = 0 ;
2008-10-23 13:23:08 +04:00
if ( ! ref_freq ) {
ref_freq = freqs . old ;
loops_per_jiffy_ref = boot_cpu_data . loops_per_jiffy ;
}
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_PRECHANGE ) ;
2008-10-23 13:23:08 +04:00
if ( freqs . old < freqs . new )
boot_cpu_data . loops_per_jiffy = cpufreq_scale (
loops_per_jiffy_ref , ref_freq , freqs . new ) ;
2007-06-04 18:10:57 +04:00
clk_set_rate ( cpuclk , freq ) ;
2008-10-23 13:23:08 +04:00
if ( freqs . new < freqs . old )
boot_cpu_data . loops_per_jiffy = cpufreq_scale (
loops_per_jiffy_ref , ref_freq , freqs . new ) ;
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_POSTCHANGE ) ;
2007-06-04 18:10:57 +04:00
pr_debug ( " cpufreq: set frequency %lu Hz \n " , freq ) ;
return 0 ;
}
static int __init at32_cpufreq_driver_init ( struct cpufreq_policy * policy )
{
2013-09-16 17:26:41 +04:00
unsigned int frequency , rate ;
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
}
policy - > cpuinfo . min_freq = ( clk_round_rate ( cpuclk , 1 ) + 500 ) / 1000 ;
policy - > cpuinfo . max_freq = ( clk_round_rate ( cpuclk , ~ 0UL ) + 500 ) / 1000 ;
policy - > cpuinfo . transition_latency = 0 ;
policy - > cur = at32_get_speed ( 0 ) ;
policy - > min = policy - > cpuinfo . min_freq ;
policy - > max = policy - > cpuinfo . max_freq ;
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 .
*/
steps = fls ( policy - > cpuinfo . max_freq / policy - > cpuinfo . min_freq ) + 1 ;
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
frequency = policy - > cpuinfo . max_freq ;
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 ;
}
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 ,
2007-06-04 18:10:57 +04:00
. target = at32_set_target ,
. get = at32_get_speed ,
. 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 ) ;