2018-12-11 20:57:48 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-05-19 23:19:33 +03:00
/*
* Copyright ( C ) 2015 Maxime Ripard < maxime . ripard @ free - electrons . com >
*/
# include <linux/bitops.h>
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/export.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2015-05-19 23:19:33 +03:00
# include <linux/kernel.h>
# include <linux/of.h>
# include <linux/slab.h>
2019-04-18 14:12:07 +03:00
static inline u32 clk_mult_readl ( struct clk_multiplier * mult )
{
if ( mult - > flags & CLK_MULTIPLIER_BIG_ENDIAN )
return ioread32be ( mult - > reg ) ;
2019-04-18 14:12:11 +03:00
return readl ( mult - > reg ) ;
2019-04-18 14:12:07 +03:00
}
static inline void clk_mult_writel ( struct clk_multiplier * mult , u32 val )
{
if ( mult - > flags & CLK_MULTIPLIER_BIG_ENDIAN )
iowrite32be ( val , mult - > reg ) ;
else
2019-04-18 14:12:11 +03:00
writel ( val , mult - > reg ) ;
2019-04-18 14:12:07 +03:00
}
2015-05-19 23:19:33 +03:00
static unsigned long __get_mult ( struct clk_multiplier * mult ,
unsigned long rate ,
unsigned long parent_rate )
{
if ( mult - > flags & CLK_MULTIPLIER_ROUND_CLOSEST )
return DIV_ROUND_CLOSEST ( rate , parent_rate ) ;
return rate / parent_rate ;
}
static unsigned long clk_multiplier_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_multiplier * mult = to_clk_multiplier ( hw ) ;
unsigned long val ;
2019-04-18 14:12:07 +03:00
val = clk_mult_readl ( mult ) > > mult - > shift ;
2015-05-19 23:19:33 +03:00
val & = GENMASK ( mult - > width - 1 , 0 ) ;
if ( ! val & & mult - > flags & CLK_MULTIPLIER_ZERO_BYPASS )
val = 1 ;
return parent_rate * val ;
}
static bool __is_best_rate ( unsigned long rate , unsigned long new ,
unsigned long best , unsigned long flags )
{
if ( flags & CLK_MULTIPLIER_ROUND_CLOSEST )
return abs ( rate - new ) < abs ( rate - best ) ;
return new > = rate & & new < best ;
}
static unsigned long __bestmult ( struct clk_hw * hw , unsigned long rate ,
unsigned long * best_parent_rate ,
u8 width , unsigned long flags )
{
2016-05-16 15:47:02 +03:00
struct clk_multiplier * mult = to_clk_multiplier ( hw ) ;
2015-05-19 23:19:33 +03:00
unsigned long orig_parent_rate = * best_parent_rate ;
unsigned long parent_rate , current_rate , best_rate = ~ 0 ;
unsigned int i , bestmult = 0 ;
2016-05-16 15:47:02 +03:00
unsigned int maxmult = ( 1 < < width ) - 1 ;
if ( ! ( clk_hw_get_flags ( hw ) & CLK_SET_RATE_PARENT ) ) {
bestmult = rate / orig_parent_rate ;
/* Make sure we don't end up with a 0 multiplier */
if ( ( bestmult = = 0 ) & &
! ( mult - > flags & CLK_MULTIPLIER_ZERO_BYPASS ) )
bestmult = 1 ;
2015-05-19 23:19:33 +03:00
2016-05-16 15:47:02 +03:00
/* Make sure we don't overflow the multiplier */
if ( bestmult > maxmult )
bestmult = maxmult ;
return bestmult ;
}
2015-05-19 23:19:33 +03:00
2016-05-16 15:47:02 +03:00
for ( i = 1 ; i < maxmult ; i + + ) {
2015-05-19 23:19:33 +03:00
if ( rate = = orig_parent_rate * i ) {
/*
* This is the best case for us if we have a
* perfect match without changing the parent
* rate .
*/
* best_parent_rate = orig_parent_rate ;
return i ;
}
parent_rate = clk_hw_round_rate ( clk_hw_get_parent ( hw ) ,
rate / i ) ;
current_rate = parent_rate * i ;
if ( __is_best_rate ( rate , current_rate , best_rate , flags ) ) {
bestmult = i ;
best_rate = current_rate ;
* best_parent_rate = parent_rate ;
}
}
return bestmult ;
}
static long clk_multiplier_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
struct clk_multiplier * mult = to_clk_multiplier ( hw ) ;
unsigned long factor = __bestmult ( hw , rate , parent_rate ,
mult - > width , mult - > flags ) ;
return * parent_rate * factor ;
}
static int clk_multiplier_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_multiplier * mult = to_clk_multiplier ( hw ) ;
unsigned long factor = __get_mult ( mult , rate , parent_rate ) ;
unsigned long flags = 0 ;
unsigned long val ;
if ( mult - > lock )
spin_lock_irqsave ( mult - > lock , flags ) ;
else
__acquire ( mult - > lock ) ;
2019-04-18 14:12:07 +03:00
val = clk_mult_readl ( mult ) ;
2015-05-19 23:19:33 +03:00
val & = ~ GENMASK ( mult - > width + mult - > shift - 1 , mult - > shift ) ;
val | = factor < < mult - > shift ;
2019-04-18 14:12:07 +03:00
clk_mult_writel ( mult , val ) ;
2015-05-19 23:19:33 +03:00
if ( mult - > lock )
spin_unlock_irqrestore ( mult - > lock , flags ) ;
else
__release ( mult - > lock ) ;
return 0 ;
}
const struct clk_ops clk_multiplier_ops = {
. recalc_rate = clk_multiplier_recalc_rate ,
. round_rate = clk_multiplier_round_rate ,
. set_rate = clk_multiplier_set_rate ,
} ;
EXPORT_SYMBOL_GPL ( clk_multiplier_ops ) ;