2012-08-20 06:55:11 +04:00
/*
* mmp factor clock operation source file
*
* Copyright ( C ) 2012 Marvell
* Chao Xie < xiechao . mail @ gmail . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk-provider.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/err.h>
# include "clk.h"
/*
* It is M / N clock
*
* Fout from synthesizer can be given from two equations :
* numerator / denominator = Fin / ( Fout * factor )
*/
2014-10-31 05:13:41 +03:00
# define to_clk_factor(hw) container_of(hw, struct mmp_clk_factor, hw)
2012-08-20 06:55:11 +04:00
static long clk_factor_round_rate ( struct clk_hw * hw , unsigned long drate ,
unsigned long * prate )
{
2014-10-31 05:13:41 +03:00
struct mmp_clk_factor * factor = to_clk_factor ( hw ) ;
2020-05-20 01:41:39 +03:00
u64 rate = 0 , prev_rate ;
2012-08-20 06:55:11 +04:00
int i ;
for ( i = 0 ; i < factor - > ftbl_cnt ; i + + ) {
prev_rate = rate ;
2020-05-20 01:41:39 +03:00
rate = * prate ;
rate * = factor - > ftbl [ i ] . den ;
do_div ( rate , factor - > ftbl [ i ] . num * factor - > masks - > factor ) ;
2012-08-20 06:55:11 +04:00
if ( rate > drate )
break ;
}
2014-01-23 06:47:42 +04:00
if ( ( i = = 0 ) | | ( i = = factor - > ftbl_cnt ) ) {
2012-08-20 06:55:11 +04:00
return rate ;
2014-01-23 06:47:42 +04:00
} else {
if ( ( drate - prev_rate ) > ( rate - drate ) )
return rate ;
else
return prev_rate ;
}
2012-08-20 06:55:11 +04:00
}
static unsigned long clk_factor_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2014-10-31 05:13:41 +03:00
struct mmp_clk_factor * factor = to_clk_factor ( hw ) ;
struct mmp_clk_factor_masks * masks = factor - > masks ;
2012-08-20 06:55:11 +04:00
unsigned int val , num , den ;
2020-05-20 01:41:39 +03:00
u64 rate ;
2012-08-20 06:55:11 +04:00
val = readl_relaxed ( factor - > base ) ;
/* calculate numerator */
num = ( val > > masks - > num_shift ) & masks - > num_mask ;
/* calculate denominator */
2014-01-23 06:47:40 +04:00
den = ( val > > masks - > den_shift ) & masks - > den_mask ;
2012-08-20 06:55:11 +04:00
if ( ! den )
return 0 ;
2020-05-20 01:41:39 +03:00
rate = parent_rate ;
rate * = den ;
do_div ( rate , num * factor - > masks - > factor ) ;
return rate ;
2012-08-20 06:55:11 +04:00
}
/* Configures new clock rate*/
static int clk_factor_set_rate ( struct clk_hw * hw , unsigned long drate ,
unsigned long prate )
{
2014-10-31 05:13:41 +03:00
struct mmp_clk_factor * factor = to_clk_factor ( hw ) ;
struct mmp_clk_factor_masks * masks = factor - > masks ;
2012-08-20 06:55:11 +04:00
int i ;
unsigned long val ;
2014-10-31 05:13:42 +03:00
unsigned long flags = 0 ;
2020-05-20 01:41:39 +03:00
u64 rate = 0 ;
2012-08-20 06:55:11 +04:00
for ( i = 0 ; i < factor - > ftbl_cnt ; i + + ) {
2020-05-20 01:41:39 +03:00
rate = prate ;
rate * = factor - > ftbl [ i ] . den ;
do_div ( rate , factor - > ftbl [ i ] . num * factor - > masks - > factor ) ;
2012-08-20 06:55:11 +04:00
if ( rate > drate )
break ;
}
if ( i > 0 )
i - - ;
2014-10-31 05:13:42 +03:00
if ( factor - > lock )
spin_lock_irqsave ( factor - > lock , flags ) ;
2012-08-20 06:55:11 +04:00
val = readl_relaxed ( factor - > base ) ;
val & = ~ ( masks - > num_mask < < masks - > num_shift ) ;
val | = ( factor - > ftbl [ i ] . num & masks - > num_mask ) < < masks - > num_shift ;
val & = ~ ( masks - > den_mask < < masks - > den_shift ) ;
val | = ( factor - > ftbl [ i ] . den & masks - > den_mask ) < < masks - > den_shift ;
writel_relaxed ( val , factor - > base ) ;
2014-10-31 05:13:42 +03:00
if ( factor - > lock )
spin_unlock_irqrestore ( factor - > lock , flags ) ;
2012-08-20 06:55:11 +04:00
return 0 ;
}
2019-09-24 15:39:53 +03:00
static int clk_factor_init ( struct clk_hw * hw )
2014-10-31 05:13:43 +03:00
{
struct mmp_clk_factor * factor = to_clk_factor ( hw ) ;
struct mmp_clk_factor_masks * masks = factor - > masks ;
u32 val , num , den ;
int i ;
unsigned long flags = 0 ;
if ( factor - > lock )
spin_lock_irqsave ( factor - > lock , flags ) ;
val = readl ( factor - > base ) ;
/* calculate numerator */
num = ( val > > masks - > num_shift ) & masks - > num_mask ;
/* calculate denominator */
den = ( val > > masks - > den_shift ) & masks - > den_mask ;
for ( i = 0 ; i < factor - > ftbl_cnt ; i + + )
if ( den = = factor - > ftbl [ i ] . den & & num = = factor - > ftbl [ i ] . num )
break ;
if ( i > = factor - > ftbl_cnt ) {
val & = ~ ( masks - > num_mask < < masks - > num_shift ) ;
val | = ( factor - > ftbl [ 0 ] . num & masks - > num_mask ) < <
masks - > num_shift ;
val & = ~ ( masks - > den_mask < < masks - > den_shift ) ;
val | = ( factor - > ftbl [ 0 ] . den & masks - > den_mask ) < <
masks - > den_shift ;
2020-05-20 01:41:40 +03:00
}
2014-10-31 05:13:43 +03:00
2020-05-20 01:41:40 +03:00
if ( ! ( val & masks - > enable_mask ) | | i > = factor - > ftbl_cnt ) {
val | = masks - > enable_mask ;
2014-10-31 05:13:43 +03:00
writel ( val , factor - > base ) ;
}
if ( factor - > lock )
spin_unlock_irqrestore ( factor - > lock , flags ) ;
2019-09-24 15:39:53 +03:00
return 0 ;
2014-10-31 05:13:43 +03:00
}
2017-08-22 16:35:55 +03:00
static const struct clk_ops clk_factor_ops = {
2012-08-20 06:55:11 +04:00
. recalc_rate = clk_factor_recalc_rate ,
. round_rate = clk_factor_round_rate ,
. set_rate = clk_factor_set_rate ,
2014-10-31 05:13:43 +03:00
. init = clk_factor_init ,
2012-08-20 06:55:11 +04:00
} ;
struct clk * mmp_clk_register_factor ( const char * name , const char * parent_name ,
unsigned long flags , void __iomem * base ,
2014-10-31 05:13:41 +03:00
struct mmp_clk_factor_masks * masks ,
struct mmp_clk_factor_tbl * ftbl ,
2014-10-31 05:13:42 +03:00
unsigned int ftbl_cnt , spinlock_t * lock )
2012-08-20 06:55:11 +04:00
{
2014-10-31 05:13:41 +03:00
struct mmp_clk_factor * factor ;
2012-08-20 06:55:11 +04:00
struct clk_init_data init ;
struct clk * clk ;
if ( ! masks ) {
pr_err ( " %s: must pass a clk_factor_mask \n " , __func__ ) ;
return ERR_PTR ( - EINVAL ) ;
}
factor = kzalloc ( sizeof ( * factor ) , GFP_KERNEL ) ;
2017-09-26 23:25:38 +03:00
if ( ! factor )
2012-08-20 06:55:11 +04:00
return ERR_PTR ( - ENOMEM ) ;
/* struct clk_aux assignments */
factor - > base = base ;
factor - > masks = masks ;
factor - > ftbl = ftbl ;
factor - > ftbl_cnt = ftbl_cnt ;
factor - > hw . init = & init ;
2014-10-31 05:13:42 +03:00
factor - > lock = lock ;
2012-08-20 06:55:11 +04:00
init . name = name ;
init . ops = & clk_factor_ops ;
init . flags = flags ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
clk = clk_register ( NULL , & factor - > hw ) ;
if ( IS_ERR_OR_NULL ( clk ) )
kfree ( factor ) ;
return clk ;
}