2011-07-08 13:40:12 +04:00
/*
* Clock tree for CSR SiRFprimaII
*
* Copyright ( c ) 2011 Cambridge Silicon Radio Limited , a CSR plc group company .
*
* Licensed under GPLv2 or later .
*/
# include <linux/module.h>
# include <linux/bitops.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/io.h>
# include <linux/clkdev.h>
# include <linux/clk.h>
# include <linux/spinlock.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <asm/mach/map.h>
# include <mach/map.h>
# define SIRFSOC_CLKC_CLK_EN0 0x0000
# define SIRFSOC_CLKC_CLK_EN1 0x0004
# define SIRFSOC_CLKC_REF_CFG 0x0014
# define SIRFSOC_CLKC_CPU_CFG 0x0018
# define SIRFSOC_CLKC_MEM_CFG 0x001c
# define SIRFSOC_CLKC_SYS_CFG 0x0020
# define SIRFSOC_CLKC_IO_CFG 0x0024
# define SIRFSOC_CLKC_DSP_CFG 0x0028
# define SIRFSOC_CLKC_GFX_CFG 0x002c
# define SIRFSOC_CLKC_MM_CFG 0x0030
# define SIRFSOC_LKC_LCD_CFG 0x0034
# define SIRFSOC_CLKC_MMC_CFG 0x0038
# define SIRFSOC_CLKC_PLL1_CFG0 0x0040
# define SIRFSOC_CLKC_PLL2_CFG0 0x0044
# define SIRFSOC_CLKC_PLL3_CFG0 0x0048
# define SIRFSOC_CLKC_PLL1_CFG1 0x004c
# define SIRFSOC_CLKC_PLL2_CFG1 0x0050
# define SIRFSOC_CLKC_PLL3_CFG1 0x0054
# define SIRFSOC_CLKC_PLL1_CFG2 0x0058
# define SIRFSOC_CLKC_PLL2_CFG2 0x005c
# define SIRFSOC_CLKC_PLL3_CFG2 0x0060
# define SIRFSOC_CLOCK_VA_BASE SIRFSOC_VA(0x005000)
# define KHZ 1000
# define MHZ (KHZ * KHZ)
struct clk_ops {
unsigned long ( * get_rate ) ( struct clk * clk ) ;
long ( * round_rate ) ( struct clk * clk , unsigned long rate ) ;
int ( * set_rate ) ( struct clk * clk , unsigned long rate ) ;
int ( * enable ) ( struct clk * clk ) ;
int ( * disable ) ( struct clk * clk ) ;
struct clk * ( * get_parent ) ( struct clk * clk ) ;
int ( * set_parent ) ( struct clk * clk , struct clk * parent ) ;
} ;
struct clk {
struct clk * parent ; /* parent clk */
unsigned long rate ; /* clock rate in Hz */
signed char usage ; /* clock enable count */
signed char enable_bit ; /* enable bit: 0 ~ 63 */
unsigned short regofs ; /* register offset */
struct clk_ops * ops ; /* clock operation */
} ;
static DEFINE_SPINLOCK ( clocks_lock ) ;
static inline unsigned long clkc_readl ( unsigned reg )
{
return readl ( SIRFSOC_CLOCK_VA_BASE + reg ) ;
}
static inline void clkc_writel ( u32 val , unsigned reg )
{
writel ( val , SIRFSOC_CLOCK_VA_BASE + reg ) ;
}
/*
* osc_rtc - real time oscillator - 32.768 KHz
* osc_sys - high speed oscillator - 26 MHz
*/
static struct clk clk_rtc = {
. rate = 32768 ,
} ;
static struct clk clk_osc = {
. rate = 26 * MHZ ,
} ;
/*
* std pll
*/
static unsigned long std_pll_get_rate ( struct clk * clk )
{
unsigned long fin = clk_get_rate ( clk - > parent ) ;
u32 regcfg2 = clk - > regofs + SIRFSOC_CLKC_PLL1_CFG2 -
SIRFSOC_CLKC_PLL1_CFG0 ;
if ( clkc_readl ( regcfg2 ) & BIT ( 2 ) ) {
/* pll bypass mode */
clk - > rate = fin ;
} else {
/* fout = fin * nf / nr / od */
u32 cfg0 = clkc_readl ( clk - > regofs ) ;
u32 nf = ( cfg0 & ( BIT ( 13 ) - 1 ) ) + 1 ;
u32 nr = ( ( cfg0 > > 13 ) & ( BIT ( 6 ) - 1 ) ) + 1 ;
u32 od = ( ( cfg0 > > 19 ) & ( BIT ( 4 ) - 1 ) ) + 1 ;
WARN_ON ( fin % MHZ ) ;
clk - > rate = fin / MHZ * nf / nr / od * MHZ ;
}
return clk - > rate ;
}
static int std_pll_set_rate ( struct clk * clk , unsigned long rate )
{
unsigned long fin , nf , nr , od , reg ;
/*
* fout = fin * nf / ( nr * od ) ;
* set od = 1 , nr = fin / MHz , so fout = nf * MHz
*/
nf = rate / MHZ ;
if ( unlikely ( ( rate % MHZ ) | | nf > BIT ( 13 ) | | nf < 1 ) )
return - EINVAL ;
fin = clk_get_rate ( clk - > parent ) ;
BUG_ON ( fin < MHZ ) ;
nr = fin / MHZ ;
BUG_ON ( ( fin % MHZ ) | | nr > BIT ( 6 ) ) ;
od = 1 ;
reg = ( nf - 1 ) | ( ( nr - 1 ) < < 13 ) | ( ( od - 1 ) < < 19 ) ;
clkc_writel ( reg , clk - > regofs ) ;
reg = clk - > regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0 ;
clkc_writel ( ( nf > > 1 ) - 1 , reg ) ;
reg = clk - > regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0 ;
while ( ! ( clkc_readl ( reg ) & BIT ( 6 ) ) )
cpu_relax ( ) ;
clk - > rate = 0 ; /* set to zero will force recalculation */
return 0 ;
}
static struct clk_ops std_pll_ops = {
. get_rate = std_pll_get_rate ,
. set_rate = std_pll_set_rate ,
} ;
static struct clk clk_pll1 = {
. parent = & clk_osc ,
. regofs = SIRFSOC_CLKC_PLL1_CFG0 ,
. ops = & std_pll_ops ,
} ;
static struct clk clk_pll2 = {
. parent = & clk_osc ,
. regofs = SIRFSOC_CLKC_PLL2_CFG0 ,
. ops = & std_pll_ops ,
} ;
static struct clk clk_pll3 = {
. parent = & clk_osc ,
. regofs = SIRFSOC_CLKC_PLL3_CFG0 ,
. ops = & std_pll_ops ,
} ;
/*
* clock domains - cpu , mem , sys / io
*/
static struct clk clk_mem ;
static struct clk * dmn_get_parent ( struct clk * clk )
{
struct clk * clks [ ] = {
& clk_osc , & clk_rtc , & clk_pll1 , & clk_pll2 , & clk_pll3
} ;
u32 cfg = clkc_readl ( clk - > regofs ) ;
WARN_ON ( ( cfg & ( BIT ( 3 ) - 1 ) ) > 4 ) ;
return clks [ cfg & ( BIT ( 3 ) - 1 ) ] ;
}
static int dmn_set_parent ( struct clk * clk , struct clk * parent )
{
const struct clk * clks [ ] = {
& clk_osc , & clk_rtc , & clk_pll1 , & clk_pll2 , & clk_pll3
} ;
u32 cfg = clkc_readl ( clk - > regofs ) ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( clks ) ; i + + ) {
if ( clks [ i ] = = parent ) {
cfg & = ~ ( BIT ( 3 ) - 1 ) ;
clkc_writel ( cfg | i , clk - > regofs ) ;
/* BIT(3) - switching status: 1 - busy, 0 - done */
while ( clkc_readl ( clk - > regofs ) & BIT ( 3 ) )
cpu_relax ( ) ;
return 0 ;
}
}
return - EINVAL ;
}
static unsigned long dmn_get_rate ( struct clk * clk )
{
unsigned long fin = clk_get_rate ( clk - > parent ) ;
u32 cfg = clkc_readl ( clk - > regofs ) ;
if ( cfg & BIT ( 24 ) ) {
/* fcd bypass mode */
clk - > rate = fin ;
} else {
/*
* wait count : bit [ 19 : 16 ] , hold count : bit [ 23 : 20 ]
*/
u32 wait = ( cfg > > 16 ) & ( BIT ( 4 ) - 1 ) ;
u32 hold = ( cfg > > 20 ) & ( BIT ( 4 ) - 1 ) ;
clk - > rate = fin / ( wait + hold + 2 ) ;
}
return clk - > rate ;
}
static int dmn_set_rate ( struct clk * clk , unsigned long rate )
{
unsigned long fin ;
unsigned ratio , wait , hold , reg ;
unsigned bits = ( clk = = & clk_mem ) ? 3 : 4 ;
fin = clk_get_rate ( clk - > parent ) ;
ratio = fin / rate ;
if ( unlikely ( ratio < 2 | | ratio > BIT ( bits + 1 ) ) )
return - EINVAL ;
WARN_ON ( fin % rate ) ;
wait = ( ratio > > 1 ) - 1 ;
hold = ratio - wait - 2 ;
reg = clkc_readl ( clk - > regofs ) ;
reg & = ~ ( ( ( BIT ( bits ) - 1 ) < < 16 ) | ( ( BIT ( bits ) - 1 ) < < 20 ) ) ;
reg | = ( wait < < 16 ) | ( hold < < 20 ) | BIT ( 25 ) ;
clkc_writel ( reg , clk - > regofs ) ;
/* waiting FCD been effective */
while ( clkc_readl ( clk - > regofs ) & BIT ( 25 ) )
cpu_relax ( ) ;
clk - > rate = 0 ; /* set to zero will force recalculation */
return 0 ;
}
/*
* cpu clock has no FCD register in Prima2 , can only change pll
*/
static int cpu_set_rate ( struct clk * clk , unsigned long rate )
{
int ret1 , ret2 ;
struct clk * cur_parent , * tmp_parent ;
cur_parent = dmn_get_parent ( clk ) ;
BUG_ON ( cur_parent = = NULL | | cur_parent - > usage > 1 ) ;
/* switch to tmp pll before setting parent clock's rate */
tmp_parent = cur_parent = = & clk_pll1 ? & clk_pll2 : & clk_pll1 ;
ret1 = dmn_set_parent ( clk , tmp_parent ) ;
BUG_ON ( ret1 ) ;
ret2 = clk_set_rate ( cur_parent , rate ) ;
ret1 = dmn_set_parent ( clk , cur_parent ) ;
clk - > rate = 0 ; /* set to zero will force recalculation */
return ret2 ? ret2 : ret1 ;
}
static struct clk_ops cpu_ops = {
. get_parent = dmn_get_parent ,
. set_parent = dmn_set_parent ,
. set_rate = cpu_set_rate ,
} ;
static struct clk clk_cpu = {
. parent = & clk_pll1 ,
. regofs = SIRFSOC_CLKC_CPU_CFG ,
. ops = & cpu_ops ,
} ;
static struct clk_ops msi_ops = {
. set_rate = dmn_set_rate ,
. get_rate = dmn_get_rate ,
. set_parent = dmn_set_parent ,
. get_parent = dmn_get_parent ,
} ;
static struct clk clk_mem = {
. parent = & clk_pll2 ,
. regofs = SIRFSOC_CLKC_MEM_CFG ,
. ops = & msi_ops ,
} ;
static struct clk clk_sys = {
. parent = & clk_pll3 ,
. regofs = SIRFSOC_CLKC_SYS_CFG ,
. ops = & msi_ops ,
} ;
static struct clk clk_io = {
. parent = & clk_pll3 ,
. regofs = SIRFSOC_CLKC_IO_CFG ,
. ops = & msi_ops ,
} ;
/*
* on - chip clock sets
*/
static struct clk_lookup onchip_clks [ ] = {
{
. dev_id = " rtc " ,
. clk = & clk_rtc ,
} , {
. dev_id = " osc " ,
. clk = & clk_osc ,
} , {
. dev_id = " pll1 " ,
. clk = & clk_pll1 ,
} , {
. dev_id = " pll2 " ,
. clk = & clk_pll2 ,
} , {
. dev_id = " pll3 " ,
. clk = & clk_pll3 ,
} , {
. dev_id = " cpu " ,
. clk = & clk_cpu ,
} , {
. dev_id = " mem " ,
. clk = & clk_mem ,
} , {
. dev_id = " sys " ,
2011-09-23 09:51:31 +04:00
. clk = & clk_sys ,
2011-07-08 13:40:12 +04:00
} , {
. dev_id = " io " ,
2011-09-23 09:51:31 +04:00
. clk = & clk_io ,
2011-07-08 13:40:12 +04:00
} ,
} ;
int clk_enable ( struct clk * clk )
{
unsigned long flags ;
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return - EINVAL ;
if ( clk - > parent )
clk_enable ( clk - > parent ) ;
spin_lock_irqsave ( & clocks_lock , flags ) ;
if ( ! clk - > usage + + & & clk - > ops & & clk - > ops - > enable )
clk - > ops - > enable ( clk ) ;
spin_unlock_irqrestore ( & clocks_lock , flags ) ;
return 0 ;
}
EXPORT_SYMBOL ( clk_enable ) ;
void clk_disable ( struct clk * clk )
{
unsigned long flags ;
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return ;
WARN_ON ( ! clk - > usage ) ;
spin_lock_irqsave ( & clocks_lock , flags ) ;
if ( - - clk - > usage = = 0 & & clk - > ops & & clk - > ops - > disable )
clk - > ops - > disable ( clk ) ;
spin_unlock_irqrestore ( & clocks_lock , flags ) ;
if ( clk - > parent )
clk_disable ( clk - > parent ) ;
}
EXPORT_SYMBOL ( clk_disable ) ;
unsigned long clk_get_rate ( struct clk * clk )
{
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return 0 ;
if ( clk - > rate )
return clk - > rate ;
if ( clk - > ops & & clk - > ops - > get_rate )
return clk - > ops - > get_rate ( clk ) ;
return clk_get_rate ( clk - > parent ) ;
}
EXPORT_SYMBOL ( clk_get_rate ) ;
long clk_round_rate ( struct clk * clk , unsigned long rate )
{
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return 0 ;
if ( clk - > ops & & clk - > ops - > round_rate )
return clk - > ops - > round_rate ( clk , rate ) ;
return 0 ;
}
EXPORT_SYMBOL ( clk_round_rate ) ;
int clk_set_rate ( struct clk * clk , unsigned long rate )
{
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return - EINVAL ;
if ( ! clk - > ops | | ! clk - > ops - > set_rate )
return - EINVAL ;
return clk - > ops - > set_rate ( clk , rate ) ;
}
EXPORT_SYMBOL ( clk_set_rate ) ;
int clk_set_parent ( struct clk * clk , struct clk * parent )
{
int ret ;
unsigned long flags ;
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return - EINVAL ;
if ( ! clk - > ops | | ! clk - > ops - > set_parent )
return - EINVAL ;
spin_lock_irqsave ( & clocks_lock , flags ) ;
ret = clk - > ops - > set_parent ( clk , parent ) ;
if ( ! ret ) {
parent - > usage + = clk - > usage ;
clk - > parent - > usage - = clk - > usage ;
BUG_ON ( clk - > parent - > usage < 0 ) ;
clk - > parent = parent ;
}
spin_unlock_irqrestore ( & clocks_lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_set_parent ) ;
struct clk * clk_get_parent ( struct clk * clk )
{
unsigned long flags ;
if ( unlikely ( IS_ERR_OR_NULL ( clk ) ) )
return NULL ;
if ( ! clk - > ops | | ! clk - > ops - > get_parent )
return clk - > parent ;
spin_lock_irqsave ( & clocks_lock , flags ) ;
clk - > parent = clk - > ops - > get_parent ( clk ) ;
spin_unlock_irqrestore ( & clocks_lock , flags ) ;
return clk - > parent ;
}
EXPORT_SYMBOL ( clk_get_parent ) ;
static void __init sirfsoc_clk_init ( void )
{
clkdev_add_table ( onchip_clks , ARRAY_SIZE ( onchip_clks ) ) ;
}
static struct of_device_id clkc_ids [ ] = {
{ . compatible = " sirf,prima2-clkc " } ,
2011-08-02 00:09:36 +04:00
{ } ,
2011-07-08 13:40:12 +04:00
} ;
void __init sirfsoc_of_clk_init ( void )
{
struct device_node * np ;
struct resource res ;
struct map_desc sirfsoc_clkc_iodesc = {
. virtual = SIRFSOC_CLOCK_VA_BASE ,
. type = MT_DEVICE ,
} ;
np = of_find_matching_node ( NULL , clkc_ids ) ;
if ( ! np )
panic ( " unable to find compatible clkc node in dtb \n " ) ;
if ( of_address_to_resource ( np , 0 , & res ) )
panic ( " unable to find clkc range in dtb " ) ;
of_node_put ( np ) ;
sirfsoc_clkc_iodesc . pfn = __phys_to_pfn ( res . start ) ;
sirfsoc_clkc_iodesc . length = 1 + res . end - res . start ;
iotable_init ( & sirfsoc_clkc_iodesc , 1 ) ;
sirfsoc_clk_init ( ) ;
}