2014-05-15 17:40:25 +04:00
/*
* Copyright ( C ) 2014 Intel Corporation
*
* 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 .
*
* Adjustable fractional divider clock implementation .
* Output rate = ( m / n ) * parent_rate .
2015-09-22 18:54:11 +03:00
* Uses rational best approximation algorithm .
2014-05-15 17:40:25 +04:00
*/
# include <linux/clk-provider.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/slab.h>
2015-09-22 18:54:11 +03:00
# include <linux/rational.h>
2014-05-15 17:40:25 +04:00
# define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
static unsigned long clk_fd_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_fractional_divider * fd = to_clk_fd ( hw ) ;
unsigned long flags = 0 ;
2015-09-22 18:54:11 +03:00
unsigned long m , n ;
u32 val ;
2014-05-15 17:40:25 +04:00
u64 ret ;
if ( fd - > lock )
spin_lock_irqsave ( fd - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__acquire ( fd - > lock ) ;
2014-05-15 17:40:25 +04:00
val = clk_readl ( fd - > reg ) ;
if ( fd - > lock )
spin_unlock_irqrestore ( fd - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__release ( fd - > lock ) ;
2014-05-15 17:40:25 +04:00
m = ( val & fd - > mmask ) > > fd - > mshift ;
n = ( val & fd - > nmask ) > > fd - > nshift ;
2015-02-02 16:37:04 +03:00
if ( ! n | | ! m )
return parent_rate ;
2014-08-28 14:46:10 +04:00
ret = ( u64 ) parent_rate * m ;
2014-05-15 17:40:25 +04:00
do_div ( ret , n ) ;
return ret ;
}
static long clk_fd_round_rate ( struct clk_hw * hw , unsigned long rate ,
2015-09-22 18:54:08 +03:00
unsigned long * parent_rate )
2014-05-15 17:40:25 +04:00
{
struct clk_fractional_divider * fd = to_clk_fd ( hw ) ;
2015-09-22 18:54:11 +03:00
unsigned long scale ;
unsigned long m , n ;
u64 ret ;
2014-05-15 17:40:25 +04:00
2015-09-22 18:54:08 +03:00
if ( ! rate | | rate > = * parent_rate )
return * parent_rate ;
2014-05-15 17:40:25 +04:00
2015-09-22 18:54:11 +03:00
/*
* Get rate closer to * parent_rate to guarantee there is no overflow
* for m and n . In the result it will be the nearest rate left shifted
* by ( scale - fd - > nwidth ) bits .
*/
scale = fls_long ( * parent_rate / rate - 1 ) ;
if ( scale > fd - > nwidth )
rate < < = scale - fd - > nwidth ;
2014-05-15 17:40:25 +04:00
2015-09-22 18:54:11 +03:00
rational_best_approximation ( rate , * parent_rate ,
GENMASK ( fd - > mwidth - 1 , 0 ) , GENMASK ( fd - > nwidth - 1 , 0 ) ,
& m , & n ) ;
2014-05-15 17:40:25 +04:00
2015-09-22 18:54:11 +03:00
ret = ( u64 ) * parent_rate * m ;
do_div ( ret , n ) ;
return ret ;
2014-05-15 17:40:25 +04:00
}
static int clk_fd_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_fractional_divider * fd = to_clk_fd ( hw ) ;
unsigned long flags = 0 ;
2015-09-22 18:54:11 +03:00
unsigned long m , n ;
2014-05-15 17:40:25 +04:00
u32 val ;
2015-09-22 18:54:11 +03:00
rational_best_approximation ( rate , parent_rate ,
GENMASK ( fd - > mwidth - 1 , 0 ) , GENMASK ( fd - > nwidth - 1 , 0 ) ,
& m , & n ) ;
2014-05-15 17:40:25 +04:00
if ( fd - > lock )
spin_lock_irqsave ( fd - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__acquire ( fd - > lock ) ;
2014-05-15 17:40:25 +04:00
val = clk_readl ( fd - > reg ) ;
val & = ~ ( fd - > mmask | fd - > nmask ) ;
val | = ( m < < fd - > mshift ) | ( n < < fd - > nshift ) ;
clk_writel ( val , fd - > reg ) ;
if ( fd - > lock )
spin_unlock_irqrestore ( fd - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__release ( fd - > lock ) ;
2014-05-15 17:40:25 +04:00
return 0 ;
}
const struct clk_ops clk_fractional_divider_ops = {
. recalc_rate = clk_fd_recalc_rate ,
. round_rate = clk_fd_round_rate ,
. set_rate = clk_fd_set_rate ,
} ;
EXPORT_SYMBOL_GPL ( clk_fractional_divider_ops ) ;
struct clk * clk_register_fractional_divider ( struct device * dev ,
const char * name , const char * parent_name , unsigned long flags ,
void __iomem * reg , u8 mshift , u8 mwidth , u8 nshift , u8 nwidth ,
u8 clk_divider_flags , spinlock_t * lock )
{
struct clk_fractional_divider * fd ;
struct clk_init_data init ;
struct clk * clk ;
fd = kzalloc ( sizeof ( * fd ) , GFP_KERNEL ) ;
2015-05-15 02:47:10 +03:00
if ( ! fd )
2014-05-15 17:40:25 +04:00
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & clk_fractional_divider_ops ;
init . flags = flags | CLK_IS_BASIC ;
init . parent_names = parent_name ? & parent_name : NULL ;
init . num_parents = parent_name ? 1 : 0 ;
fd - > reg = reg ;
fd - > mshift = mshift ;
2015-09-22 18:54:09 +03:00
fd - > mwidth = mwidth ;
fd - > mmask = GENMASK ( mwidth - 1 , 0 ) < < mshift ;
2014-05-15 17:40:25 +04:00
fd - > nshift = nshift ;
2015-09-22 18:54:09 +03:00
fd - > nwidth = nwidth ;
fd - > nmask = GENMASK ( nwidth - 1 , 0 ) < < nshift ;
2014-05-15 17:40:25 +04:00
fd - > flags = clk_divider_flags ;
fd - > lock = lock ;
fd - > hw . init = & init ;
clk = clk_register ( dev , & fd - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( fd ) ;
return clk ;
}
EXPORT_SYMBOL_GPL ( clk_register_fractional_divider ) ;