2011-06-02 01:18:22 +04:00
/*
2011-02-14 10:33:10 +03:00
* Copyright ( c ) 2010 - 2011 Samsung Electronics Co . , Ltd .
2010-09-16 12:59:21 +04:00
* http : //www.samsung.com
*
2012-01-07 15:18:35 +04:00
* EXYNOS4210 - CPU frequency scaling support
2010-09-16 12:59:21 +04:00
*
* 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 .
*/
2012-01-07 15:18:39 +04:00
# include <linux/module.h>
2010-09-16 12:59:21 +04:00
# include <linux/kernel.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/cpufreq.h>
2014-05-26 01:26:03 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
2010-09-16 12:59:21 +04:00
2012-12-29 04:29:10 +04:00
# include "exynos-cpufreq.h"
2010-09-16 12:59:21 +04:00
static struct clk * cpu_clk ;
static struct clk * moutcore ;
static struct clk * mout_mpll ;
static struct clk * mout_apll ;
2014-05-26 01:26:03 +04:00
static struct exynos_dvfs_info * cpufreq ;
2010-09-16 12:59:21 +04:00
2012-12-24 03:57:42 +04:00
static unsigned int exynos4210_volt_table [ ] = {
2012-01-07 15:18:35 +04:00
1250000 , 1150000 , 1050000 , 975000 , 950000 ,
2010-09-16 12:59:21 +04:00
} ;
2012-01-07 15:18:35 +04:00
static struct cpufreq_frequency_table exynos4210_freq_table [ ] = {
2014-03-28 17:41:47 +04:00
{ 0 , L0 , 1200 * 1000 } ,
{ 0 , L1 , 1000 * 1000 } ,
{ 0 , L2 , 800 * 1000 } ,
{ 0 , L3 , 500 * 1000 } ,
{ 0 , L4 , 200 * 1000 } ,
{ 0 , 0 , CPUFREQ_TABLE_END } ,
2010-09-16 12:59:21 +04:00
} ;
2012-12-24 03:57:42 +04:00
static struct apll_freq apll_freq_4210 [ ] = {
2010-09-16 12:59:21 +04:00
/*
2012-12-24 03:57:42 +04:00
* values :
* freq
* clock divider for CORE , COREM0 , COREM1 , PERIPH , ATB , PCLK_DBG , APLL , RESERVED
* clock divider for COPY , HPM , RESERVED
* PLL M , P , S
2010-09-16 12:59:21 +04:00
*/
2012-12-24 03:57:42 +04:00
APLL_FREQ ( 1200 , 0 , 3 , 7 , 3 , 4 , 1 , 7 , 0 , 5 , 0 , 0 , 150 , 3 , 1 ) ,
APLL_FREQ ( 1000 , 0 , 3 , 7 , 3 , 4 , 1 , 7 , 0 , 4 , 0 , 0 , 250 , 6 , 1 ) ,
APLL_FREQ ( 800 , 0 , 3 , 7 , 3 , 3 , 1 , 7 , 0 , 3 , 0 , 0 , 200 , 6 , 1 ) ,
APLL_FREQ ( 500 , 0 , 3 , 7 , 3 , 3 , 1 , 7 , 0 , 3 , 0 , 0 , 250 , 6 , 2 ) ,
APLL_FREQ ( 200 , 0 , 1 , 3 , 1 , 3 , 1 , 0 , 0 , 3 , 0 , 0 , 200 , 6 , 3 ) ,
2010-12-22 10:49:32 +03:00
} ;
2012-01-07 15:18:35 +04:00
static void exynos4210_set_clkdiv ( unsigned int div_index )
2010-09-16 12:59:21 +04:00
{
unsigned int tmp ;
/* Change Divider - CPU0 */
2012-12-24 03:57:42 +04:00
tmp = apll_freq_4210 [ div_index ] . clk_div_cpu0 ;
2010-09-16 12:59:21 +04:00
2014-05-26 01:26:03 +04:00
__raw_writel ( tmp , cpufreq - > cmu_regs + EXYNOS4_CLKDIV_CPU ) ;
2010-09-16 12:59:21 +04:00
do {
2014-05-26 01:26:03 +04:00
tmp = __raw_readl ( cpufreq - > cmu_regs + EXYNOS4_CLKDIV_STATCPU ) ;
2010-09-16 12:59:21 +04:00
} while ( tmp & 0x1111111 ) ;
2010-12-22 10:49:32 +03:00
/* Change Divider - CPU1 */
2012-12-24 03:57:42 +04:00
tmp = apll_freq_4210 [ div_index ] . clk_div_cpu1 ;
2010-12-22 10:49:32 +03:00
2014-05-26 01:26:03 +04:00
__raw_writel ( tmp , cpufreq - > cmu_regs + EXYNOS4_CLKDIV_CPU1 ) ;
2010-12-22 10:49:32 +03:00
do {
2014-05-26 01:26:03 +04:00
tmp = __raw_readl ( cpufreq - > cmu_regs + EXYNOS4_CLKDIV_STATCPU1 ) ;
2010-12-22 10:49:32 +03:00
} while ( tmp & 0x11 ) ;
2010-09-16 12:59:21 +04:00
}
2012-01-07 15:18:35 +04:00
static void exynos4210_set_apll ( unsigned int index )
2010-12-22 10:49:32 +03:00
{
2013-10-09 16:08:43 +04:00
unsigned int tmp , freq = apll_freq_4210 [ index ] . freq ;
2010-12-22 10:49:32 +03:00
2013-10-09 16:08:43 +04:00
/* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
2010-12-22 10:49:32 +03:00
clk_set_parent ( moutcore , mout_mpll ) ;
do {
2014-05-26 01:26:03 +04:00
tmp = ( __raw_readl ( cpufreq - > cmu_regs + EXYNOS4_CLKMUX_STATCPU )
2012-01-31 08:49:24 +04:00
> > EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT ) ;
2010-12-22 10:49:32 +03:00
tmp & = 0x7 ;
} while ( tmp ! = 0x2 ) ;
2013-10-09 16:08:43 +04:00
clk_set_rate ( mout_apll , freq * 1000 ) ;
2010-12-22 10:49:32 +03:00
2013-10-09 16:08:43 +04:00
/* MUX_CORE_SEL = APLL */
2010-12-22 10:49:32 +03:00
clk_set_parent ( moutcore , mout_apll ) ;
do {
2014-05-26 01:26:03 +04:00
tmp = __raw_readl ( cpufreq - > cmu_regs + EXYNOS4_CLKMUX_STATCPU ) ;
2012-01-31 08:49:24 +04:00
tmp & = EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK ;
} while ( tmp ! = ( 0x1 < < EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT ) ) ;
2010-12-22 10:49:32 +03:00
}
2012-01-07 15:18:35 +04:00
static void exynos4210_set_frequency ( unsigned int old_index ,
unsigned int new_index )
2010-12-22 10:49:32 +03:00
{
if ( old_index > new_index ) {
2013-10-09 16:08:43 +04:00
exynos4210_set_clkdiv ( new_index ) ;
exynos4210_set_apll ( new_index ) ;
2011-12-07 06:44:09 +04:00
} else if ( old_index < new_index ) {
2013-10-09 16:08:43 +04:00
exynos4210_set_apll ( new_index ) ;
exynos4210_set_clkdiv ( new_index ) ;
2010-12-22 10:49:32 +03:00
}
}
2012-01-07 15:18:35 +04:00
int exynos4210_cpufreq_init ( struct exynos_dvfs_info * info )
2010-09-16 12:59:21 +04:00
{
2014-05-26 01:26:03 +04:00
struct device_node * np ;
2012-01-07 15:18:35 +04:00
unsigned long rate ;
2011-12-07 06:44:09 +04:00
2014-05-26 01:26:03 +04:00
/*
* HACK : This is a temporary workaround to get access to clock
* controller registers directly and remove static mappings and
* dependencies on platform headers . It is necessary to enable
* Exynos multi - platform support and will be removed together with
* this whole driver as soon as Exynos gets migrated to use
* cpufreq - cpu0 driver .
*/
np = of_find_compatible_node ( NULL , NULL , " samsung,exynos4210-clock " ) ;
if ( ! np ) {
pr_err ( " %s: failed to find clock controller DT node \n " ,
__func__ ) ;
return - ENODEV ;
}
info - > cmu_regs = of_iomap ( np , 0 ) ;
if ( ! info - > cmu_regs ) {
pr_err ( " %s: failed to map CMU registers \n " , __func__ ) ;
return - EFAULT ;
}
2010-09-16 12:59:21 +04:00
cpu_clk = clk_get ( NULL , " armclk " ) ;
if ( IS_ERR ( cpu_clk ) )
return PTR_ERR ( cpu_clk ) ;
moutcore = clk_get ( NULL , " moutcore " ) ;
if ( IS_ERR ( moutcore ) )
2012-01-07 15:18:35 +04:00
goto err_moutcore ;
2010-09-16 12:59:21 +04:00
mout_mpll = clk_get ( NULL , " mout_mpll " ) ;
if ( IS_ERR ( mout_mpll ) )
2012-01-07 15:18:35 +04:00
goto err_mout_mpll ;
rate = clk_get_rate ( mout_mpll ) / 1000 ;
2010-09-16 12:59:21 +04:00
mout_apll = clk_get ( NULL , " mout_apll " ) ;
if ( IS_ERR ( mout_apll ) )
2012-01-07 15:18:35 +04:00
goto err_mout_apll ;
2011-08-18 14:45:16 +04:00
2012-01-07 15:18:35 +04:00
info - > mpll_freq_khz = rate ;
2012-12-24 03:57:42 +04:00
/* 800Mhz */
2012-01-07 15:18:35 +04:00
info - > pll_safe_idx = L2 ;
info - > cpu_clk = cpu_clk ;
info - > volt_table = exynos4210_volt_table ;
info - > freq_table = exynos4210_freq_table ;
info - > set_freq = exynos4210_set_frequency ;
2010-09-16 12:59:21 +04:00
2014-05-26 01:26:03 +04:00
cpufreq = info ;
2012-01-07 15:18:35 +04:00
return 0 ;
2010-09-16 12:59:21 +04:00
2012-01-07 15:18:35 +04:00
err_mout_apll :
2012-12-24 03:51:40 +04:00
clk_put ( mout_mpll ) ;
2012-01-07 15:18:35 +04:00
err_mout_mpll :
2012-12-24 03:51:40 +04:00
clk_put ( moutcore ) ;
2012-01-07 15:18:35 +04:00
err_moutcore :
2012-12-24 03:51:40 +04:00
clk_put ( cpu_clk ) ;
2010-09-16 12:59:21 +04:00
2012-01-07 15:18:35 +04:00
pr_debug ( " %s: failed initialization \n " , __func__ ) ;
2010-09-16 12:59:21 +04:00
return - EINVAL ;
}