2012-05-03 14:06:14 +04:00
/*
* Copyright ( C ) 2011 Sascha Hauer , Pengutronix < s . hauer @ pengutronix . de >
*
* 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 .
*
* Standard functionality for the common clock API .
*/
# include <linux/module.h>
# include <linux/clk-provider.h>
# include <linux/slab.h>
# include <linux/err.h>
2013-04-12 15:57:44 +04:00
# include <linux/of.h>
2012-05-03 14:06:14 +04:00
/*
* DOC : basic fixed multiplier and divider clock that cannot gate
*
* Traits of this clock :
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is fixed . clk - > rate = parent - > rate / div * mult
* parent - fixed parent . No clk_set_parent support
*/
static unsigned long clk_factor_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_fixed_factor * fix = to_clk_fixed_factor ( hw ) ;
2012-12-03 12:14:37 +04:00
unsigned long long int rate ;
2012-05-03 14:06:14 +04:00
2012-12-03 12:14:37 +04:00
rate = ( unsigned long long int ) parent_rate * fix - > mult ;
do_div ( rate , fix - > div ) ;
return ( unsigned long ) rate ;
2012-05-03 14:06:14 +04:00
}
static long clk_factor_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
struct clk_fixed_factor * fix = to_clk_fixed_factor ( hw ) ;
2015-06-30 02:56:30 +03:00
if ( clk_hw_get_flags ( hw ) & CLK_SET_RATE_PARENT ) {
2012-05-03 14:06:14 +04:00
unsigned long best_parent ;
best_parent = ( rate / fix - > mult ) * fix - > div ;
2015-07-31 03:20:57 +03:00
* prate = clk_hw_round_rate ( clk_hw_get_parent ( hw ) , best_parent ) ;
2012-05-03 14:06:14 +04:00
}
return ( * prate / fix - > div ) * fix - > mult ;
}
static int clk_factor_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
2015-06-10 23:04:54 +03:00
/*
* We must report success but we can do so unconditionally because
* clk_factor_round_rate returns values that ensure this call is a
* nop .
*/
2012-05-03 14:06:14 +04:00
return 0 ;
}
2015-06-10 23:04:54 +03:00
const struct clk_ops clk_fixed_factor_ops = {
2012-05-03 14:06:14 +04:00
. round_rate = clk_factor_round_rate ,
. set_rate = clk_factor_set_rate ,
. recalc_rate = clk_factor_recalc_rate ,
} ;
EXPORT_SYMBOL_GPL ( clk_fixed_factor_ops ) ;
2016-02-07 11:11:06 +03:00
struct clk_hw * clk_hw_register_fixed_factor ( struct device * dev ,
const char * name , const char * parent_name , unsigned long flags ,
2012-05-03 14:06:14 +04:00
unsigned int mult , unsigned int div )
{
struct clk_fixed_factor * fix ;
struct clk_init_data init ;
2016-02-07 11:11:06 +03:00
struct clk_hw * hw ;
int ret ;
2012-05-03 14:06:14 +04:00
fix = kmalloc ( sizeof ( * fix ) , GFP_KERNEL ) ;
2015-05-15 02:47:10 +03:00
if ( ! fix )
2012-05-03 14:06:14 +04:00
return ERR_PTR ( - ENOMEM ) ;
/* struct clk_fixed_factor assignments */
fix - > mult = mult ;
fix - > div = div ;
fix - > hw . init = & init ;
init . name = name ;
init . ops = & clk_fixed_factor_ops ;
2012-06-01 12:32:47 +04:00
init . flags = flags | CLK_IS_BASIC ;
2012-05-03 14:06:14 +04:00
init . parent_names = & parent_name ;
init . num_parents = 1 ;
2016-02-07 11:11:06 +03:00
hw = & fix - > hw ;
ret = clk_hw_register ( dev , hw ) ;
if ( ret ) {
2012-05-03 14:06:14 +04:00
kfree ( fix ) ;
2016-02-07 11:11:06 +03:00
hw = ERR_PTR ( ret ) ;
}
2012-05-03 14:06:14 +04:00
2016-02-07 11:11:06 +03:00
return hw ;
}
EXPORT_SYMBOL_GPL ( clk_hw_register_fixed_factor ) ;
struct clk * clk_register_fixed_factor ( struct device * dev , const char * name ,
const char * parent_name , unsigned long flags ,
unsigned int mult , unsigned int div )
{
struct clk_hw * hw ;
hw = clk_hw_register_fixed_factor ( dev , name , parent_name , flags , mult ,
div ) ;
if ( IS_ERR ( hw ) )
return ERR_CAST ( hw ) ;
return hw - > clk ;
2012-05-03 14:06:14 +04:00
}
2013-08-16 06:06:29 +04:00
EXPORT_SYMBOL_GPL ( clk_register_fixed_factor ) ;
2016-01-06 07:25:09 +03:00
void clk_unregister_fixed_factor ( struct clk * clk )
{
struct clk_hw * hw ;
hw = __clk_get_hw ( clk ) ;
if ( ! hw )
return ;
clk_unregister ( clk ) ;
kfree ( to_clk_fixed_factor ( hw ) ) ;
}
EXPORT_SYMBOL_GPL ( clk_unregister_fixed_factor ) ;
2016-02-07 11:11:06 +03:00
void clk_hw_unregister_fixed_factor ( struct clk_hw * hw )
{
struct clk_fixed_factor * fix ;
fix = to_clk_fixed_factor ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( fix ) ;
}
EXPORT_SYMBOL_GPL ( clk_hw_unregister_fixed_factor ) ;
2013-08-16 06:06:29 +04:00
2013-04-12 15:57:44 +04:00
# ifdef CONFIG_OF
2016-06-22 12:15:54 +03:00
static const struct of_device_id set_rate_parent_matches [ ] = {
{ . compatible = " allwinner,sun4i-a10-pll3-2x-clk " } ,
{ /* Sentinel */ } ,
} ;
2013-04-12 15:57:44 +04:00
/**
* of_fixed_factor_clk_setup ( ) - Setup function for simple fixed factor clock
*/
void __init of_fixed_factor_clk_setup ( struct device_node * node )
{
struct clk * clk ;
const char * clk_name = node - > name ;
const char * parent_name ;
2016-06-22 12:15:54 +03:00
unsigned long flags = 0 ;
2013-04-12 15:57:44 +04:00
u32 div , mult ;
if ( of_property_read_u32 ( node , " clock-div " , & div ) ) {
pr_err ( " %s Fixed factor clock <%s> must have a clock-div property \n " ,
__func__ , node - > name ) ;
return ;
}
if ( of_property_read_u32 ( node , " clock-mult " , & mult ) ) {
2013-09-25 23:08:46 +04:00
pr_err ( " %s Fixed factor clock <%s> must have a clock-mult property \n " ,
2013-04-12 15:57:44 +04:00
__func__ , node - > name ) ;
return ;
}
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
parent_name = of_clk_get_parent_name ( node , 0 ) ;
2016-06-22 12:15:54 +03:00
if ( of_match_node ( set_rate_parent_matches , node ) )
flags | = CLK_SET_RATE_PARENT ;
clk = clk_register_fixed_factor ( NULL , clk_name , parent_name , flags ,
2013-04-12 15:57:44 +04:00
mult , div ) ;
if ( ! IS_ERR ( clk ) )
of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
}
EXPORT_SYMBOL_GPL ( of_fixed_factor_clk_setup ) ;
CLK_OF_DECLARE ( fixed_factor_clk , " fixed-factor-clock " ,
of_fixed_factor_clk_setup ) ;
# endif