2012-06-11 17:29:54 +02:00
/*
* Driver for the ICST307 VCO clock found in the ARM Reference designs .
* We wrap the custom interface from < asm / hardware / icst . h > into the generic
* clock framework .
*
2012-11-20 22:39:31 +01:00
* Copyright ( C ) 2012 Linus Walleij
*
* 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-06-11 17:29:54 +02:00
* TODO : when all ARM reference designs are migrated to generic clocks , the
* ICST clock code from the ARM tree should probably be merged into this
* file .
*/
# include <linux/clk.h>
# include <linux/clkdev.h>
# include <linux/err.h>
# include <linux/clk-provider.h>
2012-11-20 23:01:04 +01:00
# include <linux/io.h>
2012-06-11 17:29:54 +02:00
# include "clk-icst.h"
/**
* struct clk_icst - ICST VCO clock wrapper
* @ hw : corresponding clock hardware entry
2012-11-20 23:01:04 +01:00
* @ vcoreg : VCO register address
* @ lockreg : VCO lock register address
2012-06-11 17:29:54 +02:00
* @ params : parameters for this ICST instance
* @ rate : current rate
*/
struct clk_icst {
struct clk_hw hw ;
2012-11-20 23:01:04 +01:00
void __iomem * vcoreg ;
void __iomem * lockreg ;
2014-01-20 21:46:46 +01:00
struct icst_params * params ;
2012-06-11 17:29:54 +02:00
unsigned long rate ;
} ;
# define to_icst(_hw) container_of(_hw, struct clk_icst, hw)
2012-11-20 23:01:04 +01:00
/**
* vco_get ( ) - get ICST VCO settings from a certain register
* @ vcoreg : register containing the VCO settings
*/
static struct icst_vco vco_get ( void __iomem * vcoreg )
{
u32 val ;
struct icst_vco vco ;
val = readl ( vcoreg ) ;
vco . v = val & 0x1ff ;
vco . r = ( val > > 9 ) & 0x7f ;
vco . s = ( val > > 16 ) & 03 ;
return vco ;
}
/**
* vco_set ( ) - commit changes to an ICST VCO
* @ locreg : register to poke to unlock the VCO for writing
* @ vcoreg : register containing the VCO settings
* @ vco : ICST VCO parameters to commit
*/
static void vco_set ( void __iomem * lockreg ,
void __iomem * vcoreg ,
struct icst_vco vco )
{
u32 val ;
val = readl ( vcoreg ) & ~ 0x7ffff ;
val | = vco . v | ( vco . r < < 9 ) | ( vco . s < < 16 ) ;
/* This magic unlocks the VCO so it can be controlled */
writel ( 0xa05f , lockreg ) ;
writel ( val , vcoreg ) ;
/* This locks the VCO again */
writel ( 0 , lockreg ) ;
}
2012-06-11 17:29:54 +02:00
static unsigned long icst_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_icst * icst = to_icst ( hw ) ;
struct icst_vco vco ;
2014-01-20 21:46:46 +01:00
if ( parent_rate )
icst - > params - > ref = parent_rate ;
2012-11-20 23:01:04 +01:00
vco = vco_get ( icst - > vcoreg ) ;
2012-06-11 17:29:54 +02:00
icst - > rate = icst_hz ( icst - > params , vco ) ;
return icst - > rate ;
}
static long icst_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
struct clk_icst * icst = to_icst ( hw ) ;
struct icst_vco vco ;
vco = icst_hz_to_vco ( icst - > params , rate ) ;
return icst_hz ( icst - > params , vco ) ;
}
static int icst_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_icst * icst = to_icst ( hw ) ;
struct icst_vco vco ;
2014-01-20 21:46:46 +01:00
if ( parent_rate )
icst - > params - > ref = parent_rate ;
2012-06-11 17:29:54 +02:00
vco = icst_hz_to_vco ( icst - > params , rate ) ;
icst - > rate = icst_hz ( icst - > params , vco ) ;
2013-07-23 16:42:18 +01:00
vco_set ( icst - > lockreg , icst - > vcoreg , vco ) ;
2012-06-11 17:29:54 +02:00
return 0 ;
}
static const struct clk_ops icst_ops = {
. recalc_rate = icst_recalc_rate ,
. round_rate = icst_round_rate ,
. set_rate = icst_set_rate ,
} ;
2012-11-20 23:01:04 +01:00
struct clk * icst_clk_register ( struct device * dev ,
const struct clk_icst_desc * desc ,
2013-11-22 11:30:05 +01:00
const char * name ,
2014-01-20 21:31:41 +01:00
const char * parent_name ,
2012-11-20 23:01:04 +01:00
void __iomem * base )
2012-06-11 17:29:54 +02:00
{
struct clk * clk ;
struct clk_icst * icst ;
struct clk_init_data init ;
2014-01-20 21:46:46 +01:00
struct icst_params * pclone ;
2012-06-11 17:29:54 +02:00
icst = kzalloc ( sizeof ( struct clk_icst ) , GFP_KERNEL ) ;
if ( ! icst ) {
pr_err ( " could not allocate ICST clock! \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2014-01-20 21:46:46 +01:00
pclone = kmemdup ( desc - > params , sizeof ( * pclone ) , GFP_KERNEL ) ;
if ( ! pclone ) {
pr_err ( " could not clone ICST params \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2013-11-22 11:30:05 +01:00
init . name = name ;
2012-06-11 17:29:54 +02:00
init . ops = & icst_ops ;
init . flags = CLK_IS_ROOT ;
2014-01-20 21:46:46 +01:00
init . parent_names = ( parent_name ? & parent_name : NULL ) ;
init . num_parents = ( parent_name ? 1 : 0 ) ;
2012-06-11 17:29:54 +02:00
icst - > hw . init = & init ;
2014-01-20 21:46:46 +01:00
icst - > params = pclone ;
2012-11-20 23:01:04 +01:00
icst - > vcoreg = base + desc - > vco_offset ;
icst - > lockreg = base + desc - > lock_offset ;
2012-06-11 17:29:54 +02:00
clk = clk_register ( dev , & icst - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( icst ) ;
return clk ;
}