2012-03-19 15:36:10 +04:00
# include <linux/kernel.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <asm/div64.h>
# include "clk.h"
# define to_clk_pllv2(clk) (container_of(clk, struct clk_pllv2, clk))
/* PLL Register Offsets */
# define MXC_PLL_DP_CTL 0x00
# define MXC_PLL_DP_CONFIG 0x04
# define MXC_PLL_DP_OP 0x08
# define MXC_PLL_DP_MFD 0x0C
# define MXC_PLL_DP_MFN 0x10
# define MXC_PLL_DP_MFNMINUS 0x14
# define MXC_PLL_DP_MFNPLUS 0x18
# define MXC_PLL_DP_HFS_OP 0x1C
# define MXC_PLL_DP_HFS_MFD 0x20
# define MXC_PLL_DP_HFS_MFN 0x24
# define MXC_PLL_DP_MFN_TOGC 0x28
# define MXC_PLL_DP_DESTAT 0x2c
/* PLL Register Bit definitions */
# define MXC_PLL_DP_CTL_MUL_CTRL 0x2000
# define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000
# define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET 12
# define MXC_PLL_DP_CTL_ADE 0x800
# define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400
# define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK (3 << 8)
# define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET 8
# define MXC_PLL_DP_CTL_HFSM 0x80
# define MXC_PLL_DP_CTL_PRE 0x40
# define MXC_PLL_DP_CTL_UPEN 0x20
# define MXC_PLL_DP_CTL_RST 0x10
# define MXC_PLL_DP_CTL_RCP 0x8
# define MXC_PLL_DP_CTL_PLM 0x4
# define MXC_PLL_DP_CTL_BRM0 0x2
# define MXC_PLL_DP_CTL_LRF 0x1
# define MXC_PLL_DP_CONFIG_BIST 0x8
# define MXC_PLL_DP_CONFIG_SJC_CE 0x4
# define MXC_PLL_DP_CONFIG_AREN 0x2
# define MXC_PLL_DP_CONFIG_LDREQ 0x1
# define MXC_PLL_DP_OP_MFI_OFFSET 4
# define MXC_PLL_DP_OP_MFI_MASK (0xF << 4)
# define MXC_PLL_DP_OP_PDF_OFFSET 0
# define MXC_PLL_DP_OP_PDF_MASK 0xF
# define MXC_PLL_DP_MFD_OFFSET 0
# define MXC_PLL_DP_MFD_MASK 0x07FFFFFF
# define MXC_PLL_DP_MFN_OFFSET 0x0
# define MXC_PLL_DP_MFN_MASK 0x07FFFFFF
# define MXC_PLL_DP_MFN_TOGC_TOG_DIS (1 << 17)
# define MXC_PLL_DP_MFN_TOGC_TOG_EN (1 << 16)
# define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET 0x0
# define MXC_PLL_DP_MFN_TOGC_CNT_MASK 0xFFFF
# define MXC_PLL_DP_DESTAT_TOG_SEL (1 << 31)
# define MXC_PLL_DP_DESTAT_MFN 0x07FFFFFF
# define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */
struct clk_pllv2 {
struct clk_hw hw ;
void __iomem * base ;
} ;
2012-06-04 16:51:32 +04:00
static unsigned long __clk_pllv2_recalc_rate ( unsigned long parent_rate ,
u32 dp_ctl , u32 dp_op , u32 dp_mfd , u32 dp_mfn )
2012-03-19 15:36:10 +04:00
{
long mfi , mfn , mfd , pdf , ref_clk , mfn_abs ;
2012-06-04 16:51:32 +04:00
unsigned long dbl ;
2012-03-19 15:36:10 +04:00
s64 temp ;
dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN ;
pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK ;
mfi = ( dp_op & MXC_PLL_DP_OP_MFI_MASK ) > > MXC_PLL_DP_OP_MFI_OFFSET ;
mfi = ( mfi < = 5 ) ? 5 : mfi ;
mfd = dp_mfd & MXC_PLL_DP_MFD_MASK ;
mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK ;
/* Sign extend to 32-bits */
if ( mfn > = 0x04000000 ) {
mfn | = 0xFC000000 ;
mfn_abs = - mfn ;
}
ref_clk = 2 * parent_rate ;
if ( dbl ! = 0 )
ref_clk * = 2 ;
ref_clk / = ( pdf + 1 ) ;
temp = ( u64 ) ref_clk * mfn_abs ;
do_div ( temp , mfd + 1 ) ;
if ( mfn < 0 )
temp = - temp ;
temp = ( ref_clk * mfi ) + temp ;
return temp ;
}
2012-06-04 16:51:32 +04:00
static unsigned long clk_pllv2_recalc_rate ( struct clk_hw * hw ,
2012-03-19 15:36:10 +04:00
unsigned long parent_rate )
{
2012-06-04 16:51:32 +04:00
u32 dp_op , dp_mfd , dp_mfn , dp_ctl ;
void __iomem * pllbase ;
2012-03-19 15:36:10 +04:00
struct clk_pllv2 * pll = to_clk_pllv2 ( hw ) ;
2012-06-04 16:51:32 +04:00
pllbase = pll - > base ;
dp_ctl = __raw_readl ( pllbase + MXC_PLL_DP_CTL ) ;
dp_op = __raw_readl ( pllbase + MXC_PLL_DP_OP ) ;
dp_mfd = __raw_readl ( pllbase + MXC_PLL_DP_MFD ) ;
dp_mfn = __raw_readl ( pllbase + MXC_PLL_DP_MFN ) ;
return __clk_pllv2_recalc_rate ( parent_rate , dp_ctl , dp_op , dp_mfd , dp_mfn ) ;
}
static int __clk_pllv2_set_rate ( unsigned long rate , unsigned long parent_rate ,
u32 * dp_op , u32 * dp_mfd , u32 * dp_mfn )
{
2012-03-19 15:36:10 +04:00
u32 reg ;
long mfi , pdf , mfn , mfd = 999999 ;
s64 temp64 ;
unsigned long quad_parent_rate ;
quad_parent_rate = 4 * parent_rate ;
pdf = mfi = - 1 ;
while ( + + pdf < 16 & & mfi < 5 )
mfi = rate * ( pdf + 1 ) / quad_parent_rate ;
if ( mfi > 15 )
return - EINVAL ;
pdf - - ;
2012-06-04 16:51:32 +04:00
temp64 = rate * ( pdf + 1 ) - quad_parent_rate * mfi ;
do_div ( temp64 , quad_parent_rate / 1000000 ) ;
2012-03-19 15:36:10 +04:00
mfn = ( long ) temp64 ;
2012-06-04 16:51:32 +04:00
reg = mfi < < 4 | pdf ;
* dp_op = reg ;
* dp_mfd = mfd ;
* dp_mfn = mfn ;
return 0 ;
}
static int clk_pllv2_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_pllv2 * pll = to_clk_pllv2 ( hw ) ;
void __iomem * pllbase ;
u32 dp_ctl , dp_op , dp_mfd , dp_mfn ;
int ret ;
pllbase = pll - > base ;
ret = __clk_pllv2_set_rate ( rate , parent_rate , & dp_op , & dp_mfd , & dp_mfn ) ;
if ( ret )
return ret ;
2012-03-19 15:36:10 +04:00
dp_ctl = __raw_readl ( pllbase + MXC_PLL_DP_CTL ) ;
/* use dpdck0_2 */
__raw_writel ( dp_ctl | 0x1000L , pllbase + MXC_PLL_DP_CTL ) ;
2012-06-04 14:21:21 +04:00
2012-06-04 16:51:32 +04:00
__raw_writel ( dp_op , pllbase + MXC_PLL_DP_OP ) ;
__raw_writel ( dp_mfd , pllbase + MXC_PLL_DP_MFD ) ;
__raw_writel ( dp_mfn , pllbase + MXC_PLL_DP_MFN ) ;
2012-03-19 15:36:10 +04:00
return 0 ;
}
static long clk_pllv2_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
2012-06-04 16:51:32 +04:00
u32 dp_op , dp_mfd , dp_mfn ;
__clk_pllv2_set_rate ( rate , * prate , & dp_op , & dp_mfd , & dp_mfn ) ;
return __clk_pllv2_recalc_rate ( * prate , MXC_PLL_DP_CTL_DPDCK0_2_EN ,
dp_op , dp_mfd , dp_mfn ) ;
2012-03-19 15:36:10 +04:00
}
static int clk_pllv2_prepare ( struct clk_hw * hw )
{
struct clk_pllv2 * pll = to_clk_pllv2 ( hw ) ;
u32 reg ;
void __iomem * pllbase ;
int i = 0 ;
pllbase = pll - > base ;
reg = __raw_readl ( pllbase + MXC_PLL_DP_CTL ) | MXC_PLL_DP_CTL_UPEN ;
__raw_writel ( reg , pllbase + MXC_PLL_DP_CTL ) ;
/* Wait for lock */
do {
reg = __raw_readl ( pllbase + MXC_PLL_DP_CTL ) ;
if ( reg & MXC_PLL_DP_CTL_LRF )
break ;
udelay ( 1 ) ;
} while ( + + i < MAX_DPLL_WAIT_TRIES ) ;
if ( i = = MAX_DPLL_WAIT_TRIES ) {
pr_err ( " MX5: pll locking failed \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static void clk_pllv2_unprepare ( struct clk_hw * hw )
{
struct clk_pllv2 * pll = to_clk_pllv2 ( hw ) ;
u32 reg ;
void __iomem * pllbase ;
pllbase = pll - > base ;
reg = __raw_readl ( pllbase + MXC_PLL_DP_CTL ) & ~ MXC_PLL_DP_CTL_UPEN ;
__raw_writel ( reg , pllbase + MXC_PLL_DP_CTL ) ;
}
2013-03-25 16:20:34 +04:00
static struct clk_ops clk_pllv2_ops = {
2012-03-19 15:36:10 +04:00
. prepare = clk_pllv2_prepare ,
. unprepare = clk_pllv2_unprepare ,
. recalc_rate = clk_pllv2_recalc_rate ,
. round_rate = clk_pllv2_round_rate ,
. set_rate = clk_pllv2_set_rate ,
} ;
struct clk * imx_clk_pllv2 ( const char * name , const char * parent ,
void __iomem * base )
{
struct clk_pllv2 * pll ;
struct clk * clk ;
struct clk_init_data init ;
pll = kzalloc ( sizeof ( * pll ) , GFP_KERNEL ) ;
if ( ! pll )
return ERR_PTR ( - ENOMEM ) ;
pll - > base = base ;
init . name = name ;
init . ops = & clk_pllv2_ops ;
init . flags = 0 ;
init . parent_names = & parent ;
init . num_parents = 1 ;
pll - > hw . init = & init ;
clk = clk_register ( NULL , & pll - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( pll ) ;
return clk ;
}