2016-06-29 21:05:28 +02:00
/*
* Copyright ( C ) 2016 Maxime Ripard
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*/
# include <linux/clk-provider.h>
# include "ccu_gate.h"
# include "ccu_div.h"
static unsigned long ccu_div_round_rate ( struct ccu_mux_internal * mux ,
2017-05-17 09:40:31 +02:00
struct clk_hw * parent ,
unsigned long * parent_rate ,
2016-06-29 21:05:28 +02:00
unsigned long rate ,
void * data )
{
struct ccu_div * cd = data ;
2017-08-12 20:43:50 +08:00
if ( cd - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate * = cd - > fixed_post_div ;
rate = divider_round_rate_parent ( & cd - > common . hw , parent ,
2017-05-17 09:40:32 +02:00
rate , parent_rate ,
cd - > div . table , cd - > div . width ,
cd - > div . flags ) ;
2017-08-12 20:43:50 +08:00
if ( cd - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate / = cd - > fixed_post_div ;
return rate ;
2016-06-29 21:05:28 +02:00
}
static void ccu_div_disable ( struct clk_hw * hw )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
return ccu_gate_helper_disable ( & cd - > common , cd - > enable ) ;
}
static int ccu_div_enable ( struct clk_hw * hw )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
return ccu_gate_helper_enable ( & cd - > common , cd - > enable ) ;
}
static int ccu_div_is_enabled ( struct clk_hw * hw )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
return ccu_gate_helper_is_enabled ( & cd - > common , cd - > enable ) ;
}
static unsigned long ccu_div_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
unsigned long val ;
u32 reg ;
reg = readl ( cd - > common . base + cd - > common . reg ) ;
val = reg > > cd - > div . shift ;
val & = ( 1 < < cd - > div . width ) - 1 ;
2017-05-17 09:40:35 +02:00
parent_rate = ccu_mux_helper_apply_prediv ( & cd - > common , & cd - > mux , - 1 ,
parent_rate ) ;
2016-06-29 21:05:28 +02:00
2017-08-12 20:43:50 +08:00
val = divider_recalc_rate ( hw , parent_rate , val , cd - > div . table ,
2017-12-21 17:30:54 +01:00
cd - > div . flags , cd - > div . width ) ;
2017-08-12 20:43:50 +08:00
if ( cd - > common . features & CCU_FEATURE_FIXED_POSTDIV )
val / = cd - > fixed_post_div ;
return val ;
2016-06-29 21:05:28 +02:00
}
static int ccu_div_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
return ccu_mux_helper_determine_rate ( & cd - > common , & cd - > mux ,
req , ccu_div_round_rate , cd ) ;
}
static int ccu_div_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
unsigned long flags ;
unsigned long val ;
u32 reg ;
2017-05-17 09:40:35 +02:00
parent_rate = ccu_mux_helper_apply_prediv ( & cd - > common , & cd - > mux , - 1 ,
parent_rate ) ;
2016-06-29 21:05:28 +02:00
2017-08-12 20:43:50 +08:00
if ( cd - > common . features & CCU_FEATURE_FIXED_POSTDIV )
rate * = cd - > fixed_post_div ;
2016-06-29 21:05:28 +02:00
val = divider_get_val ( rate , parent_rate , cd - > div . table , cd - > div . width ,
cd - > div . flags ) ;
spin_lock_irqsave ( cd - > common . lock , flags ) ;
reg = readl ( cd - > common . base + cd - > common . reg ) ;
reg & = ~ GENMASK ( cd - > div . width + cd - > div . shift - 1 , cd - > div . shift ) ;
writel ( reg | ( val < < cd - > div . shift ) ,
cd - > common . base + cd - > common . reg ) ;
spin_unlock_irqrestore ( cd - > common . lock , flags ) ;
return 0 ;
}
static u8 ccu_div_get_parent ( struct clk_hw * hw )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
return ccu_mux_helper_get_parent ( & cd - > common , & cd - > mux ) ;
}
static int ccu_div_set_parent ( struct clk_hw * hw , u8 index )
{
struct ccu_div * cd = hw_to_ccu_div ( hw ) ;
return ccu_mux_helper_set_parent ( & cd - > common , & cd - > mux , index ) ;
}
const struct clk_ops ccu_div_ops = {
. disable = ccu_div_disable ,
. enable = ccu_div_enable ,
. is_enabled = ccu_div_is_enabled ,
. get_parent = ccu_div_get_parent ,
. set_parent = ccu_div_set_parent ,
. determine_rate = ccu_div_determine_rate ,
. recalc_rate = ccu_div_recalc_rate ,
. set_rate = ccu_div_set_rate ,
} ;