2013-02-25 18:44:26 +04:00
/*
* Copyright ( C ) 2013 Emilio López < emilio @ elopez . com . ar >
*
* 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 factor - based clock implementation
*/
# include <linux/clk-provider.h>
2014-07-05 00:24:52 +04:00
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of_address.h>
2013-02-25 18:44:26 +04:00
# include <linux/slab.h>
# include <linux/string.h>
# include "clk-factors.h"
/*
2014-07-05 00:24:52 +04:00
* DOC : basic adjustable factor - based clock
2013-02-25 18:44:26 +04:00
*
* 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 adjustable .
* clk - > rate = ( parent - > rate * N * ( K + 1 ) > > P ) / ( M + 1 )
* parent - fixed parent . No clk_set_parent support
*/
# define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
2014-07-05 00:24:52 +04:00
# define FACTORS_MAX_PARENTS 5
2013-09-21 05:03:10 +04:00
# define SETMASK(len, pos) (((1U << (len)) - 1) << (pos))
2013-02-25 18:44:26 +04:00
# define CLRMASK(len, pos) (~(SETMASK(len, pos)))
# define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
# define FACTOR_SET(bit, len, reg, val) \
( ( ( reg ) & CLRMASK ( len , bit ) ) | ( val < < ( bit ) ) )
static unsigned long clk_factors_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
u8 n = 1 , k = 0 , p = 0 , m = 0 ;
u32 reg ;
unsigned long rate ;
struct clk_factors * factors = to_clk_factors ( hw ) ;
2016-01-25 16:15:38 +03:00
const struct clk_factors_config * config = factors - > config ;
2013-02-25 18:44:26 +04:00
/* Fetch the register value */
reg = readl ( factors - > reg ) ;
/* Get each individual factor if applicable */
if ( config - > nwidth ! = SUNXI_FACTORS_NOT_APPLICABLE )
n = FACTOR_GET ( config - > nshift , config - > nwidth , reg ) ;
if ( config - > kwidth ! = SUNXI_FACTORS_NOT_APPLICABLE )
k = FACTOR_GET ( config - > kshift , config - > kwidth , reg ) ;
if ( config - > mwidth ! = SUNXI_FACTORS_NOT_APPLICABLE )
m = FACTOR_GET ( config - > mshift , config - > mwidth , reg ) ;
if ( config - > pwidth ! = SUNXI_FACTORS_NOT_APPLICABLE )
p = FACTOR_GET ( config - > pshift , config - > pwidth , reg ) ;
2016-01-25 16:15:43 +03:00
if ( factors - > recalc ) {
struct factors_request factors_req = {
. parent_rate = parent_rate ,
. n = n ,
. k = k ,
. m = m ,
. p = p ,
} ;
/* get mux details from mux clk structure */
if ( factors - > mux )
factors_req . parent_index =
( reg > > factors - > mux - > shift ) &
factors - > mux - > mask ;
factors - > recalc ( & factors_req ) ;
return factors_req . rate ;
}
2013-02-25 18:44:26 +04:00
/* Calculate the rate */
2014-06-26 19:55:41 +04:00
rate = ( parent_rate * ( n + config - > n_start ) * ( k + 1 ) > > p ) / ( m + 1 ) ;
2013-02-25 18:44:26 +04:00
return rate ;
}
2015-07-07 21:48:08 +03:00
static int clk_factors_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
2014-05-02 19:57:15 +04:00
{
2016-01-25 16:15:43 +03:00
struct clk_factors * factors = to_clk_factors ( hw ) ;
2015-07-31 03:20:57 +03:00
struct clk_hw * parent , * best_parent = NULL ;
2014-05-02 19:57:15 +04:00
int i , num_parents ;
unsigned long parent_rate , best = 0 , child_rate , best_child_rate = 0 ;
/* find the parent that can help provide the fastest rate <= rate */
2015-06-26 02:53:23 +03:00
num_parents = clk_hw_get_num_parents ( hw ) ;
2014-05-02 19:57:15 +04:00
for ( i = 0 ; i < num_parents ; i + + ) {
2016-01-25 16:15:43 +03:00
struct factors_request factors_req = {
. rate = req - > rate ,
. parent_index = i ,
} ;
2015-07-31 03:20:57 +03:00
parent = clk_hw_get_parent_by_index ( hw , i ) ;
2014-05-02 19:57:15 +04:00
if ( ! parent )
continue ;
2015-06-30 02:56:30 +03:00
if ( clk_hw_get_flags ( hw ) & CLK_SET_RATE_PARENT )
2015-07-31 03:20:57 +03:00
parent_rate = clk_hw_round_rate ( parent , req - > rate ) ;
2014-05-02 19:57:15 +04:00
else
2015-07-31 03:20:57 +03:00
parent_rate = clk_hw_get_rate ( parent ) ;
2014-05-02 19:57:15 +04:00
2016-01-25 16:15:43 +03:00
factors_req . parent_rate = parent_rate ;
factors - > get_factors ( & factors_req ) ;
child_rate = factors_req . rate ;
2014-05-02 19:57:15 +04:00
2015-07-07 21:48:08 +03:00
if ( child_rate < = req - > rate & & child_rate > best_child_rate ) {
2014-05-02 19:57:15 +04:00
best_parent = parent ;
best = parent_rate ;
best_child_rate = child_rate ;
}
}
2015-07-09 23:39:38 +03:00
if ( ! best_parent )
return - EINVAL ;
2015-07-31 03:20:57 +03:00
req - > best_parent_hw = best_parent ;
2015-07-07 21:48:08 +03:00
req - > best_parent_rate = best ;
req - > rate = best_child_rate ;
2014-05-02 19:57:15 +04:00
2015-07-07 21:48:08 +03:00
return 0 ;
2014-05-02 19:57:15 +04:00
}
2013-02-25 18:44:26 +04:00
static int clk_factors_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
2016-01-25 16:15:42 +03:00
struct factors_request req = {
. rate = rate ,
. parent_rate = parent_rate ,
} ;
2013-02-25 18:44:26 +04:00
u32 reg ;
struct clk_factors * factors = to_clk_factors ( hw ) ;
2016-01-25 16:15:38 +03:00
const struct clk_factors_config * config = factors - > config ;
2013-02-25 18:44:26 +04:00
unsigned long flags = 0 ;
2016-01-25 16:15:42 +03:00
factors - > get_factors ( & req ) ;
2013-02-25 18:44:26 +04:00
if ( factors - > lock )
spin_lock_irqsave ( factors - > lock , flags ) ;
/* Fetch the register value */
reg = readl ( factors - > reg ) ;
/* Set up the new factors - macros do not do anything if width is 0 */
2016-01-25 16:15:42 +03:00
reg = FACTOR_SET ( config - > nshift , config - > nwidth , reg , req . n ) ;
reg = FACTOR_SET ( config - > kshift , config - > kwidth , reg , req . k ) ;
reg = FACTOR_SET ( config - > mshift , config - > mwidth , reg , req . m ) ;
reg = FACTOR_SET ( config - > pshift , config - > pwidth , reg , req . p ) ;
2013-02-25 18:44:26 +04:00
/* Apply them now */
writel ( reg , factors - > reg ) ;
/* delay 500us so pll stabilizes */
__delay ( ( rate > > 20 ) * 500 / 2 ) ;
if ( factors - > lock )
spin_unlock_irqrestore ( factors - > lock , flags ) ;
return 0 ;
}
2014-07-05 00:24:52 +04:00
static const struct clk_ops clk_factors_ops = {
2014-05-02 19:57:15 +04:00
. determine_rate = clk_factors_determine_rate ,
2013-02-25 18:44:26 +04:00
. recalc_rate = clk_factors_recalc_rate ,
. set_rate = clk_factors_set_rate ,
} ;
2014-07-05 00:24:52 +04:00
2014-11-23 16:38:07 +03:00
struct clk * sunxi_factors_register ( struct device_node * node ,
const struct factors_data * data ,
spinlock_t * lock ,
void __iomem * reg )
2014-07-05 00:24:52 +04:00
{
struct clk * clk ;
struct clk_factors * factors ;
struct clk_gate * gate = NULL ;
struct clk_mux * mux = NULL ;
struct clk_hw * gate_hw = NULL ;
struct clk_hw * mux_hw = NULL ;
const char * clk_name = node - > name ;
const char * parents [ FACTORS_MAX_PARENTS ] ;
2016-01-25 16:15:39 +03:00
int ret , i = 0 ;
2014-07-05 00:24:52 +04:00
/* if we have a mux, we will have >1 parents */
2015-07-07 06:59:05 +03:00
i = of_clk_parent_fill ( node , parents , FACTORS_MAX_PARENTS ) ;
2014-07-05 00:24:52 +04:00
/*
* some factor clocks , such as pll5 and pll6 , may have multiple
* outputs , and have their name designated in factors_data
*/
if ( data - > name )
clk_name = data - > name ;
else
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
factors = kzalloc ( sizeof ( struct clk_factors ) , GFP_KERNEL ) ;
if ( ! factors )
2016-01-25 16:15:39 +03:00
goto err_factors ;
2014-07-05 00:24:52 +04:00
/* set up factors properties */
factors - > reg = reg ;
factors - > config = data - > table ;
factors - > get_factors = data - > getter ;
2016-01-25 16:15:43 +03:00
factors - > recalc = data - > recalc ;
2014-07-05 00:24:52 +04:00
factors - > lock = lock ;
/* Add a gate if this factor clock can be gated */
if ( data - > enable ) {
gate = kzalloc ( sizeof ( struct clk_gate ) , GFP_KERNEL ) ;
2016-01-25 16:15:39 +03:00
if ( ! gate )
goto err_gate ;
2014-07-05 00:24:52 +04:00
2016-01-25 16:15:40 +03:00
factors - > gate = gate ;
2014-07-05 00:24:52 +04:00
/* set up gate properties */
gate - > reg = reg ;
gate - > bit_idx = data - > enable ;
gate - > lock = factors - > lock ;
gate_hw = & gate - > hw ;
}
/* Add a mux if this factor clock can be muxed */
if ( data - > mux ) {
mux = kzalloc ( sizeof ( struct clk_mux ) , GFP_KERNEL ) ;
2016-01-25 16:15:39 +03:00
if ( ! mux )
goto err_mux ;
2014-07-05 00:24:52 +04:00
2016-01-25 16:15:40 +03:00
factors - > mux = mux ;
2014-07-05 00:24:52 +04:00
/* set up gate properties */
mux - > reg = reg ;
mux - > shift = data - > mux ;
2014-10-20 18:10:26 +04:00
mux - > mask = data - > muxmask ;
2014-07-05 00:24:52 +04:00
mux - > lock = factors - > lock ;
mux_hw = & mux - > hw ;
}
clk = clk_register_composite ( NULL , clk_name ,
parents , i ,
mux_hw , & clk_mux_ops ,
& factors - > hw , & clk_factors_ops ,
gate_hw , & clk_gate_ops , 0 ) ;
2016-01-25 16:15:39 +03:00
if ( IS_ERR ( clk ) )
goto err_register ;
2014-07-05 00:24:52 +04:00
2016-01-25 16:15:39 +03:00
ret = of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
if ( ret )
goto err_provider ;
2014-07-05 00:24:52 +04:00
return clk ;
2016-01-25 16:15:39 +03:00
err_provider :
/* TODO: The composite clock stuff will leak a bit here. */
clk_unregister ( clk ) ;
err_register :
kfree ( mux ) ;
err_mux :
kfree ( gate ) ;
err_gate :
kfree ( factors ) ;
err_factors :
return NULL ;
2014-07-05 00:24:52 +04:00
}
2016-01-25 16:15:40 +03:00
void sunxi_factors_unregister ( struct device_node * node , struct clk * clk )
{
struct clk_hw * hw = __clk_get_hw ( clk ) ;
struct clk_factors * factors ;
const char * name ;
if ( ! hw )
return ;
factors = to_clk_factors ( hw ) ;
name = clk_hw_get_name ( hw ) ;
of_clk_del_provider ( node ) ;
/* TODO: The composite clock stuff will leak a bit here. */
clk_unregister ( clk ) ;
kfree ( factors - > mux ) ;
kfree ( factors - > gate ) ;
kfree ( factors ) ;
}