2018-12-11 20:57:48 +03:00
// SPDX-License-Identifier: GPL-2.0
2014-05-15 17:40:25 +04:00
/*
* Copyright ( C ) 2014 Intel Corporation
*
* 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
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 ;
}
2017-08-01 19:21:22 +03:00
static void clk_fd_general_approximation ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate ,
unsigned long * m , unsigned long * n )
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 ;
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 ) ,
2017-08-01 19:21:22 +03:00
m , n ) ;
}
static long clk_fd_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
struct clk_fractional_divider * fd = to_clk_fd ( hw ) ;
unsigned long m , n ;
u64 ret ;
if ( ! rate | | rate > = * parent_rate )
return * parent_rate ;
if ( fd - > approximation )
fd - > approximation ( hw , rate , parent_rate , & m , & n ) ;
else
clk_fd_general_approximation ( hw , rate , parent_rate , & 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 ) ;
2016-02-07 11:15:09 +03:00
struct clk_hw * clk_hw_register_fractional_divider ( struct device * dev ,
2014-05-15 17:40:25 +04:00
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 ;
2016-02-07 11:15:09 +03:00
struct clk_hw * hw ;
int ret ;
2014-05-15 17:40:25 +04:00
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 ;
2016-02-07 11:15:09 +03:00
hw = & fd - > hw ;
ret = clk_hw_register ( dev , hw ) ;
if ( ret ) {
2014-05-15 17:40:25 +04:00
kfree ( fd ) ;
2016-02-07 11:15:09 +03:00
hw = ERR_PTR ( ret ) ;
}
return hw ;
}
EXPORT_SYMBOL_GPL ( clk_hw_register_fractional_divider ) ;
2014-05-15 17:40:25 +04:00
2016-02-07 11:15:09 +03:00
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_hw * hw ;
hw = clk_hw_register_fractional_divider ( dev , name , parent_name , flags ,
reg , mshift , mwidth , nshift , nwidth , clk_divider_flags ,
lock ) ;
if ( IS_ERR ( hw ) )
return ERR_CAST ( hw ) ;
return hw - > clk ;
2014-05-15 17:40:25 +04:00
}
EXPORT_SYMBOL_GPL ( clk_register_fractional_divider ) ;
2016-02-07 11:15:09 +03:00
void clk_hw_unregister_fractional_divider ( struct clk_hw * hw )
{
struct clk_fractional_divider * fd ;
fd = to_clk_fd ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( fd ) ;
}