2006-01-16 22:14:17 -08:00
/*
* arch / sh / kernel / cpu / sh4 / clock - sh4 - 202. c
*
* Additional SH4 - 202 support for the clock framework
*
* Copyright ( C ) 2005 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/err.h>
2010-05-11 09:36:57 +00:00
# include <linux/io.h>
2010-11-17 10:04:33 +01:00
# include <linux/clkdev.h>
2006-01-16 22:14:17 -08:00
# include <asm/clock.h>
# include <asm/freq.h>
# define CPG2_FRQCR3 0xfe0a0018
static int frqcr3_divisors [ ] = { 1 , 2 , 3 , 4 , 6 , 8 , 16 } ;
static int frqcr3_values [ ] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 } ;
2009-05-12 03:45:08 +09:00
static unsigned long emi_clk_recalc ( struct clk * clk )
2006-01-16 22:14:17 -08:00
{
2010-01-26 12:58:40 +09:00
int idx = __raw_readl ( CPG2_FRQCR3 ) & 0x0007 ;
2009-05-12 03:45:08 +09:00
return clk - > parent - > rate / frqcr3_divisors [ idx ] ;
2006-01-16 22:14:17 -08:00
}
static inline int frqcr3_lookup ( struct clk * clk , unsigned long rate )
{
int divisor = clk - > parent - > rate / rate ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( frqcr3_divisors ) ; i + + )
if ( frqcr3_divisors [ i ] = = divisor )
return frqcr3_values [ i ] ;
/* Safe fallback */
return 5 ;
}
2012-02-29 22:17:47 +09:00
static struct sh_clk_ops sh4202_emi_clk_ops = {
2006-01-16 22:14:17 -08:00
. recalc = emi_clk_recalc ,
} ;
static struct clk sh4202_emi_clk = {
2009-05-12 05:14:53 +09:00
. flags = CLK_ENABLE_ON_INIT ,
2006-01-16 22:14:17 -08:00
. ops = & sh4202_emi_clk_ops ,
} ;
2009-05-12 03:45:08 +09:00
static unsigned long femi_clk_recalc ( struct clk * clk )
2006-01-16 22:14:17 -08:00
{
2010-01-26 12:58:40 +09:00
int idx = ( __raw_readl ( CPG2_FRQCR3 ) > > 3 ) & 0x0007 ;
2009-05-12 03:45:08 +09:00
return clk - > parent - > rate / frqcr3_divisors [ idx ] ;
2006-01-16 22:14:17 -08:00
}
2012-02-29 22:17:47 +09:00
static struct sh_clk_ops sh4202_femi_clk_ops = {
2006-01-16 22:14:17 -08:00
. recalc = femi_clk_recalc ,
} ;
static struct clk sh4202_femi_clk = {
2009-05-12 05:14:53 +09:00
. flags = CLK_ENABLE_ON_INIT ,
2006-01-16 22:14:17 -08:00
. ops = & sh4202_femi_clk_ops ,
} ;
static void shoc_clk_init ( struct clk * clk )
{
int i ;
/*
* For some reason , the shoc_clk seems to be set to some really
* insane value at boot ( values outside of the allowable frequency
* range for instance ) . We deal with this by scaling it back down
* to something sensible just in case .
*
* Start scaling from the high end down until we find something
* that passes rate verification . .
*/
for ( i = 0 ; i < ARRAY_SIZE ( frqcr3_divisors ) ; i + + ) {
int divisor = frqcr3_divisors [ i ] ;
2010-12-24 11:27:29 +09:00
if ( clk - > ops - > set_rate ( clk , clk - > parent - > rate / divisor ) = = 0 )
2006-01-16 22:14:17 -08:00
break ;
}
WARN_ON ( i = = ARRAY_SIZE ( frqcr3_divisors ) ) ; /* Undefined clock */
}
2009-05-12 03:45:08 +09:00
static unsigned long shoc_clk_recalc ( struct clk * clk )
2006-01-16 22:14:17 -08:00
{
2010-01-26 12:58:40 +09:00
int idx = ( __raw_readl ( CPG2_FRQCR3 ) > > 6 ) & 0x0007 ;
2009-05-12 03:45:08 +09:00
return clk - > parent - > rate / frqcr3_divisors [ idx ] ;
2006-01-16 22:14:17 -08:00
}
static int shoc_clk_verify_rate ( struct clk * clk , unsigned long rate )
{
2006-12-01 13:15:14 +09:00
struct clk * bclk = clk_get ( NULL , " bus_clk " ) ;
2006-01-16 22:14:17 -08:00
unsigned long bclk_rate = clk_get_rate ( bclk ) ;
clk_put ( bclk ) ;
if ( rate > bclk_rate )
return 1 ;
if ( rate > 66000000 )
return 1 ;
return 0 ;
}
2010-11-15 18:18:32 +09:00
static int shoc_clk_set_rate ( struct clk * clk , unsigned long rate )
2006-01-16 22:14:17 -08:00
{
unsigned long frqcr3 ;
unsigned int tmp ;
/* Make sure we have something sensible to switch to */
if ( shoc_clk_verify_rate ( clk , rate ) ! = 0 )
return - EINVAL ;
tmp = frqcr3_lookup ( clk , rate ) ;
2010-01-26 12:58:40 +09:00
frqcr3 = __raw_readl ( CPG2_FRQCR3 ) ;
2006-01-16 22:14:17 -08:00
frqcr3 & = ~ ( 0x0007 < < 6 ) ;
frqcr3 | = tmp < < 6 ;
2010-01-26 12:58:40 +09:00
__raw_writel ( frqcr3 , CPG2_FRQCR3 ) ;
2006-01-16 22:14:17 -08:00
2009-05-12 05:59:27 +09:00
clk - > rate = clk - > parent - > rate / frqcr3_divisors [ tmp ] ;
2006-01-16 22:14:17 -08:00
return 0 ;
}
2012-02-29 22:17:47 +09:00
static struct sh_clk_ops sh4202_shoc_clk_ops = {
2006-01-16 22:14:17 -08:00
. init = shoc_clk_init ,
. recalc = shoc_clk_recalc ,
. set_rate = shoc_clk_set_rate ,
} ;
static struct clk sh4202_shoc_clk = {
2009-05-12 05:14:53 +09:00
. flags = CLK_ENABLE_ON_INIT ,
2006-01-16 22:14:17 -08:00
. ops = & sh4202_shoc_clk_ops ,
} ;
static struct clk * sh4202_onchip_clocks [ ] = {
& sh4202_emi_clk ,
& sh4202_femi_clk ,
& sh4202_shoc_clk ,
} ;
2010-05-11 09:36:57 +00:00
static struct clk_lookup lookups [ ] = {
/* main clocks */
CLKDEV_CON_ID ( " emi_clk " , & sh4202_emi_clk ) ,
CLKDEV_CON_ID ( " femi_clk " , & sh4202_femi_clk ) ,
CLKDEV_CON_ID ( " shoc_clk " , & sh4202_shoc_clk ) ,
} ;
2009-05-12 19:29:04 +09:00
int __init arch_clk_init ( void )
2006-01-16 22:14:17 -08:00
{
2009-05-13 17:38:11 +09:00
struct clk * clk ;
2009-05-12 05:59:27 +09:00
int i , ret = 0 ;
2006-01-16 22:14:17 -08:00
2009-05-13 17:38:11 +09:00
cpg_clk_init ( ) ;
clk = clk_get ( NULL , " master_clk " ) ;
2006-01-16 22:14:17 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( sh4202_onchip_clocks ) ; i + + ) {
struct clk * clkp = sh4202_onchip_clocks [ i ] ;
clkp - > parent = clk ;
2009-05-12 05:59:27 +09:00
ret | = clk_register ( clkp ) ;
2006-01-16 22:14:17 -08:00
}
clk_put ( clk ) ;
2010-05-11 09:36:57 +00:00
clkdev_add_table ( lookups , ARRAY_SIZE ( lookups ) ) ;
2009-05-12 05:59:27 +09:00
return ret ;
2006-01-16 22:14:17 -08:00
}