2011-05-10 15:42:08 +02:00
/*
2009-06-15 11:23:20 +01:00
* Copyright 2009 Wolfson Microelectronics plc
*
* S3C64xx CPUfreq Support
*
* 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 .
*/
2011-12-05 18:22:01 +00:00
# define pr_fmt(fmt) "cpufreq: " fmt
2009-06-15 11:23:20 +01:00
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/cpufreq.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/regulator/consumer.h>
2011-07-29 16:19:26 +01:00
# include <linux/module.h>
2009-06-15 11:23:20 +01:00
static struct regulator * vddarm ;
2009-11-03 14:42:11 +00:00
static unsigned long regulator_latency ;
2009-06-15 11:23:20 +01:00
# ifdef CONFIG_CPU_S3C6410
struct s3c64xx_dvfs {
unsigned int vddarm_min ;
unsigned int vddarm_max ;
} ;
static struct s3c64xx_dvfs s3c64xx_dvfs_table [ ] = {
2009-11-03 14:42:12 +00:00
[ 0 ] = { 1000000 , 1150000 } ,
[ 1 ] = { 1050000 , 1150000 } ,
[ 2 ] = { 1100000 , 1150000 } ,
[ 3 ] = { 1200000 , 1350000 } ,
2011-06-08 14:49:15 +01:00
[ 4 ] = { 1300000 , 1350000 } ,
2009-06-15 11:23:20 +01:00
} ;
static struct cpufreq_frequency_table s3c64xx_freq_table [ ] = {
2014-03-28 19:11:47 +05:30
{ 0 , 0 , 66000 } ,
{ 0 , 0 , 100000 } ,
{ 0 , 0 , 133000 } ,
{ 0 , 1 , 200000 } ,
{ 0 , 1 , 222000 } ,
{ 0 , 1 , 266000 } ,
{ 0 , 2 , 333000 } ,
{ 0 , 2 , 400000 } ,
{ 0 , 2 , 532000 } ,
{ 0 , 2 , 533000 } ,
{ 0 , 3 , 667000 } ,
{ 0 , 4 , 800000 } ,
{ 0 , 0 , CPUFREQ_TABLE_END } ,
2009-06-15 11:23:20 +01:00
} ;
# endif
static int s3c64xx_cpufreq_set_target ( struct cpufreq_policy * policy ,
2013-10-25 19:45:48 +05:30
unsigned int index )
2009-06-15 11:23:20 +01:00
{
struct s3c64xx_dvfs * dvfs ;
2013-08-14 19:38:24 +05:30
unsigned int old_freq , new_freq ;
int ret ;
2009-06-15 11:23:20 +01:00
2014-01-09 20:38:43 +05:30
old_freq = clk_get_rate ( policy - > clk ) / 1000 ;
2013-08-14 19:38:24 +05:30
new_freq = s3c64xx_freq_table [ index ] . frequency ;
2013-10-25 19:45:48 +05:30
dvfs = & s3c64xx_dvfs_table [ s3c64xx_freq_table [ index ] . driver_data ] ;
2009-06-15 11:23:20 +01:00
# ifdef CONFIG_REGULATOR
2013-08-14 19:38:24 +05:30
if ( vddarm & & new_freq > old_freq ) {
2009-06-15 11:23:20 +01:00
ret = regulator_set_voltage ( vddarm ,
dvfs - > vddarm_min ,
dvfs - > vddarm_max ) ;
if ( ret ! = 0 ) {
2011-12-05 18:22:01 +00:00
pr_err ( " Failed to set VDDARM for %dkHz: %d \n " ,
2013-08-14 19:38:24 +05:30
new_freq , ret ) ;
return ret ;
2009-06-15 11:23:20 +01:00
}
}
# endif
2014-01-09 20:38:43 +05:30
ret = clk_set_rate ( policy - > clk , new_freq * 1000 ) ;
2009-06-15 11:23:20 +01:00
if ( ret < 0 ) {
2011-12-05 18:22:01 +00:00
pr_err ( " Failed to set rate %dkHz: %d \n " ,
2013-08-14 19:38:24 +05:30
new_freq , ret ) ;
return ret ;
2009-06-15 11:23:20 +01:00
}
# ifdef CONFIG_REGULATOR
2013-08-14 19:38:24 +05:30
if ( vddarm & & new_freq < old_freq ) {
2009-06-15 11:23:20 +01:00
ret = regulator_set_voltage ( vddarm ,
dvfs - > vddarm_min ,
dvfs - > vddarm_max ) ;
if ( ret ! = 0 ) {
2011-12-05 18:22:01 +00:00
pr_err ( " Failed to set VDDARM for %dkHz: %d \n " ,
2013-08-14 19:38:24 +05:30
new_freq , ret ) ;
2014-01-09 20:38:43 +05:30
if ( clk_set_rate ( policy - > clk , old_freq * 1000 ) < 0 )
2013-08-14 19:38:24 +05:30
pr_err ( " Failed to restore original clock rate \n " ) ;
return ret ;
2009-06-15 11:23:20 +01:00
}
}
# endif
2011-12-05 18:22:01 +00:00
pr_debug ( " Set actual frequency %lukHz \n " ,
2014-01-09 20:38:43 +05:30
clk_get_rate ( policy - > clk ) / 1000 ) ;
2009-06-15 11:23:20 +01:00
return 0 ;
}
# ifdef CONFIG_REGULATOR
2009-11-03 14:42:11 +00:00
static void __init s3c64xx_cpufreq_config_regulator ( void )
2009-06-15 11:23:20 +01:00
{
int count , v , i , found ;
struct cpufreq_frequency_table * freq ;
struct s3c64xx_dvfs * dvfs ;
count = regulator_count_voltages ( vddarm ) ;
if ( count < 0 ) {
2011-12-05 18:22:01 +00:00
pr_err ( " Unable to check supported voltages \n " ) ;
2009-06-15 11:23:20 +01:00
}
2014-04-25 23:15:38 +03:00
if ( ! count )
goto out ;
2009-06-15 11:23:20 +01:00
2014-04-25 23:15:38 +03:00
cpufreq_for_each_valid_entry ( freq , s3c64xx_freq_table ) {
2013-10-14 19:36:47 +01:00
dvfs = & s3c64xx_dvfs_table [ freq - > driver_data ] ;
2009-06-15 11:23:20 +01:00
found = 0 ;
for ( i = 0 ; i < count ; i + + ) {
v = regulator_list_voltage ( vddarm , i ) ;
if ( v > = dvfs - > vddarm_min & & v < = dvfs - > vddarm_max )
found = 1 ;
}
if ( ! found ) {
2011-12-05 18:22:01 +00:00
pr_debug ( " %dkHz unsupported by regulator \n " ,
2009-06-15 11:23:20 +01:00
freq - > frequency ) ;
freq - > frequency = CPUFREQ_ENTRY_INVALID ;
}
}
2009-11-03 14:42:11 +00:00
2014-04-25 23:15:38 +03:00
out :
2009-11-03 14:42:11 +00:00
/* Guess based on having to do an I2C/SPI write; in future we
* will be able to query the regulator performance here . */
regulator_latency = 1 * 1000 * 1000 ;
2009-06-15 11:23:20 +01:00
}
# endif
2011-03-11 16:10:03 +09:00
static int s3c64xx_cpufreq_driver_init ( struct cpufreq_policy * policy )
2009-06-15 11:23:20 +01:00
{
int ret ;
struct cpufreq_frequency_table * freq ;
if ( policy - > cpu ! = 0 )
return - EINVAL ;
if ( s3c64xx_freq_table = = NULL ) {
2011-12-05 18:22:01 +00:00
pr_err ( " No frequency information for this CPU \n " ) ;
2009-06-15 11:23:20 +01:00
return - ENODEV ;
}
2014-01-09 20:38:43 +05:30
policy - > clk = clk_get ( NULL , " armclk " ) ;
if ( IS_ERR ( policy - > clk ) ) {
2011-12-05 18:22:01 +00:00
pr_err ( " Unable to obtain ARMCLK: %ld \n " ,
2014-01-09 20:38:43 +05:30
PTR_ERR ( policy - > clk ) ) ;
return PTR_ERR ( policy - > clk ) ;
2009-06-15 11:23:20 +01:00
}
# ifdef CONFIG_REGULATOR
vddarm = regulator_get ( NULL , " vddarm " ) ;
if ( IS_ERR ( vddarm ) ) {
ret = PTR_ERR ( vddarm ) ;
2011-12-05 18:22:01 +00:00
pr_err ( " Failed to obtain VDDARM: %d \n " , ret ) ;
pr_err ( " Only frequency scaling available \n " ) ;
2009-06-15 11:23:20 +01:00
vddarm = NULL ;
} else {
2009-11-03 14:42:11 +00:00
s3c64xx_cpufreq_config_regulator ( ) ;
2009-06-15 11:23:20 +01:00
}
# endif
2014-04-25 23:15:38 +03:00
cpufreq_for_each_entry ( freq , s3c64xx_freq_table ) {
2009-06-15 11:23:20 +01:00
unsigned long r ;
/* Check for frequencies we can generate */
2014-01-09 20:38:43 +05:30
r = clk_round_rate ( policy - > clk , freq - > frequency * 1000 ) ;
2009-06-15 11:23:20 +01:00
r / = 1000 ;
2009-11-03 14:42:07 +00:00
if ( r ! = freq - > frequency ) {
2011-12-05 18:22:01 +00:00
pr_debug ( " %dkHz unsupported by clock \n " ,
2009-11-03 14:42:07 +00:00
freq - > frequency ) ;
2009-06-15 11:23:20 +01:00
freq - > frequency = CPUFREQ_ENTRY_INVALID ;
2009-11-03 14:42:07 +00:00
}
2009-06-15 11:23:20 +01:00
/* If we have no regulator then assume startup
* frequency is the maximum we can support . */
2014-01-09 20:38:43 +05:30
if ( ! vddarm & & freq - > frequency > clk_get_rate ( policy - > clk ) / 1000 )
2009-06-15 11:23:20 +01:00
freq - > frequency = CPUFREQ_ENTRY_INVALID ;
}
2009-11-03 14:42:11 +00:00
/* Datasheet says PLL stabalisation time (if we were to use
* the PLLs , which we don ' t currently ) is ~ 300u s worst case ,
* but add some fudge .
*/
2013-10-03 20:29:22 +05:30
ret = cpufreq_generic_init ( policy , s3c64xx_freq_table ,
( 500 * 1000 ) + regulator_latency ) ;
2009-06-15 11:23:20 +01:00
if ( ret ! = 0 ) {
2011-12-05 18:22:01 +00:00
pr_err ( " Failed to configure frequency table: %d \n " ,
2009-06-15 11:23:20 +01:00
ret ) ;
regulator_put ( vddarm ) ;
2014-01-09 20:38:43 +05:30
clk_put ( policy - > clk ) ;
2009-06-15 11:23:20 +01:00
}
return ret ;
}
static struct cpufreq_driver s3c64xx_cpufreq_driver = {
2013-12-03 11:20:45 +05:30
. flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK ,
2013-10-03 20:28:21 +05:30
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 19:45:48 +05:30
. target_index = s3c64xx_cpufreq_set_target ,
2014-01-09 20:38:43 +05:30
. get = cpufreq_generic_get ,
2009-06-15 11:23:20 +01:00
. init = s3c64xx_cpufreq_driver_init ,
. name = " s3c " ,
} ;
static int __init s3c64xx_cpufreq_init ( void )
{
return cpufreq_register_driver ( & s3c64xx_cpufreq_driver ) ;
}
module_init ( s3c64xx_cpufreq_init ) ;