2018-12-11 20:57:48 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-03-20 16:00:34 +04:00
/*
* Copyright ( c ) 2013 NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/slab.h>
static u8 clk_composite_get_parent ( struct clk_hw * hw )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * mux_ops = composite - > mux_ops ;
struct clk_hw * mux_hw = composite - > mux_hw ;
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( mux_hw , hw ) ;
2013-03-20 16:00:34 +04:00
return mux_ops - > get_parent ( mux_hw ) ;
}
static int clk_composite_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * mux_ops = composite - > mux_ops ;
struct clk_hw * mux_hw = composite - > mux_hw ;
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( mux_hw , hw ) ;
2013-03-20 16:00:34 +04:00
return mux_ops - > set_parent ( mux_hw , index ) ;
}
static unsigned long clk_composite_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
2013-04-11 22:31:36 +04:00
const struct clk_ops * rate_ops = composite - > rate_ops ;
struct clk_hw * rate_hw = composite - > rate_hw ;
2013-03-20 16:00:34 +04:00
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( rate_hw , hw ) ;
2013-03-20 16:00:34 +04:00
2013-04-11 22:31:36 +04:00
return rate_ops - > recalc_rate ( rate_hw , parent_rate ) ;
2013-03-20 16:00:34 +04:00
}
2015-07-07 21:48:08 +03:00
static int clk_composite_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
2013-09-15 04:37:59 +04:00
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * rate_ops = composite - > rate_ops ;
const struct clk_ops * mux_ops = composite - > mux_ops ;
struct clk_hw * rate_hw = composite - > rate_hw ;
struct clk_hw * mux_hw = composite - > mux_hw ;
2015-07-31 03:20:57 +03:00
struct clk_hw * parent ;
2014-07-03 03:56:45 +04:00
unsigned long parent_rate ;
long tmp_rate , best_rate = 0 ;
unsigned long rate_diff ;
unsigned long best_rate_diff = ULONG_MAX ;
2015-07-07 21:48:08 +03:00
long rate ;
2014-07-03 03:56:45 +04:00
int i ;
2013-09-15 04:37:59 +04:00
if ( rate_hw & & rate_ops & & rate_ops - > determine_rate ) {
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( rate_hw , hw ) ;
2015-07-07 21:48:08 +03:00
return rate_ops - > determine_rate ( rate_hw , req ) ;
2014-07-03 03:56:45 +04:00
} else if ( rate_hw & & rate_ops & & rate_ops - > round_rate & &
mux_hw & & mux_ops & & mux_ops - > set_parent ) {
2015-07-07 21:48:08 +03:00
req - > best_parent_hw = NULL ;
2014-07-03 03:56:45 +04:00
2015-06-30 02:56:30 +03:00
if ( clk_hw_get_flags ( hw ) & CLK_SET_RATE_NO_REPARENT ) {
2015-07-31 03:20:57 +03:00
parent = clk_hw_get_parent ( mux_hw ) ;
req - > best_parent_hw = parent ;
req - > best_parent_rate = clk_hw_get_rate ( parent ) ;
2014-07-03 03:56:45 +04:00
2015-07-07 21:48:08 +03:00
rate = rate_ops - > round_rate ( rate_hw , req - > rate ,
& req - > best_parent_rate ) ;
if ( rate < 0 )
return rate ;
req - > rate = rate ;
return 0 ;
2014-07-03 03:56:45 +04:00
}
2015-06-26 02:53:23 +03:00
for ( i = 0 ; i < clk_hw_get_num_parents ( mux_hw ) ; i + + ) {
2015-07-31 03:20:57 +03:00
parent = clk_hw_get_parent_by_index ( mux_hw , i ) ;
2014-07-03 03:56:45 +04:00
if ( ! parent )
continue ;
2015-07-31 03:20:57 +03:00
parent_rate = clk_hw_get_rate ( parent ) ;
2014-07-03 03:56:45 +04:00
2015-07-07 21:48:08 +03:00
tmp_rate = rate_ops - > round_rate ( rate_hw , req - > rate ,
2014-07-03 03:56:45 +04:00
& parent_rate ) ;
if ( tmp_rate < 0 )
continue ;
2015-07-07 21:48:08 +03:00
rate_diff = abs ( req - > rate - tmp_rate ) ;
2014-07-03 03:56:45 +04:00
2015-07-07 21:48:08 +03:00
if ( ! rate_diff | | ! req - > best_parent_hw
2014-07-03 03:56:45 +04:00
| | best_rate_diff > rate_diff ) {
2015-07-31 03:20:57 +03:00
req - > best_parent_hw = parent ;
2015-07-07 21:48:08 +03:00
req - > best_parent_rate = parent_rate ;
2014-07-03 03:56:45 +04:00
best_rate_diff = rate_diff ;
best_rate = tmp_rate ;
}
if ( ! rate_diff )
2015-07-07 21:48:08 +03:00
return 0 ;
2014-07-03 03:56:45 +04:00
}
2015-07-07 21:48:08 +03:00
req - > rate = best_rate ;
return 0 ;
2013-09-15 04:37:59 +04:00
} else if ( mux_hw & & mux_ops & & mux_ops - > determine_rate ) {
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( mux_hw , hw ) ;
2015-07-07 21:48:08 +03:00
return mux_ops - > determine_rate ( mux_hw , req ) ;
2013-09-15 04:37:59 +04:00
} else {
pr_err ( " clk: clk_composite_determine_rate function called, but no mux or rate callback set! \n " ) ;
2015-07-09 23:39:38 +03:00
return - EINVAL ;
2013-09-15 04:37:59 +04:00
}
}
2013-03-20 16:00:34 +04:00
static long clk_composite_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
2013-04-11 22:31:36 +04:00
const struct clk_ops * rate_ops = composite - > rate_ops ;
struct clk_hw * rate_hw = composite - > rate_hw ;
2013-03-20 16:00:34 +04:00
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( rate_hw , hw ) ;
2013-03-20 16:00:34 +04:00
2013-04-11 22:31:36 +04:00
return rate_ops - > round_rate ( rate_hw , rate , prate ) ;
2013-03-20 16:00:34 +04:00
}
static int clk_composite_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
2013-04-11 22:31:36 +04:00
const struct clk_ops * rate_ops = composite - > rate_ops ;
struct clk_hw * rate_hw = composite - > rate_hw ;
2013-03-20 16:00:34 +04:00
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( rate_hw , hw ) ;
2013-03-20 16:00:34 +04:00
2013-04-11 22:31:36 +04:00
return rate_ops - > set_rate ( rate_hw , rate , parent_rate ) ;
2013-03-20 16:00:34 +04:00
}
2016-04-12 11:43:39 +03:00
static int clk_composite_set_rate_and_parent ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long parent_rate ,
u8 index )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * rate_ops = composite - > rate_ops ;
const struct clk_ops * mux_ops = composite - > mux_ops ;
struct clk_hw * rate_hw = composite - > rate_hw ;
struct clk_hw * mux_hw = composite - > mux_hw ;
unsigned long temp_rate ;
__clk_hw_set_clk ( rate_hw , hw ) ;
__clk_hw_set_clk ( mux_hw , hw ) ;
temp_rate = rate_ops - > recalc_rate ( rate_hw , parent_rate ) ;
if ( temp_rate > rate ) {
rate_ops - > set_rate ( rate_hw , rate , parent_rate ) ;
mux_ops - > set_parent ( mux_hw , index ) ;
} else {
mux_ops - > set_parent ( mux_hw , index ) ;
rate_ops - > set_rate ( rate_hw , rate , parent_rate ) ;
}
return 0 ;
}
2013-03-20 16:00:34 +04:00
static int clk_composite_is_enabled ( struct clk_hw * hw )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * gate_ops = composite - > gate_ops ;
struct clk_hw * gate_hw = composite - > gate_hw ;
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( gate_hw , hw ) ;
2013-03-20 16:00:34 +04:00
return gate_ops - > is_enabled ( gate_hw ) ;
}
static int clk_composite_enable ( struct clk_hw * hw )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * gate_ops = composite - > gate_ops ;
struct clk_hw * gate_hw = composite - > gate_hw ;
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( gate_hw , hw ) ;
2013-03-20 16:00:34 +04:00
return gate_ops - > enable ( gate_hw ) ;
}
static void clk_composite_disable ( struct clk_hw * hw )
{
struct clk_composite * composite = to_clk_composite ( hw ) ;
const struct clk_ops * gate_ops = composite - > gate_ops ;
struct clk_hw * gate_hw = composite - > gate_hw ;
2015-02-12 16:58:30 +03:00
__clk_hw_set_clk ( gate_hw , hw ) ;
2013-03-20 16:00:34 +04:00
gate_ops - > disable ( gate_hw ) ;
}
2016-02-07 11:20:31 +03:00
struct clk_hw * clk_hw_register_composite ( struct device * dev , const char * name ,
2015-03-31 21:16:52 +03:00
const char * const * parent_names , int num_parents ,
2013-03-20 16:00:34 +04:00
struct clk_hw * mux_hw , const struct clk_ops * mux_ops ,
2013-04-11 22:31:36 +04:00
struct clk_hw * rate_hw , const struct clk_ops * rate_ops ,
2013-03-20 16:00:34 +04:00
struct clk_hw * gate_hw , const struct clk_ops * gate_ops ,
unsigned long flags )
{
2016-02-07 11:20:31 +03:00
struct clk_hw * hw ;
2019-11-15 19:28:55 +03:00
struct clk_init_data init = { } ;
2013-03-20 16:00:34 +04:00
struct clk_composite * composite ;
struct clk_ops * clk_composite_ops ;
2016-02-07 11:20:31 +03:00
int ret ;
2013-03-20 16:00:34 +04:00
composite = kzalloc ( sizeof ( * composite ) , GFP_KERNEL ) ;
2015-05-15 02:47:10 +03:00
if ( ! composite )
2013-03-20 16:00:34 +04:00
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
2019-04-25 20:57:37 +03:00
init . flags = flags ;
2013-03-20 16:00:34 +04:00
init . parent_names = parent_names ;
init . num_parents = num_parents ;
2016-02-07 11:20:31 +03:00
hw = & composite - > hw ;
2013-03-20 16:00:34 +04:00
clk_composite_ops = & composite - > ops ;
if ( mux_hw & & mux_ops ) {
2014-07-03 03:57:30 +04:00
if ( ! mux_ops - > get_parent ) {
2016-02-07 11:20:31 +03:00
hw = ERR_PTR ( - EINVAL ) ;
2013-03-20 16:00:34 +04:00
goto err ;
}
composite - > mux_hw = mux_hw ;
composite - > mux_ops = mux_ops ;
clk_composite_ops - > get_parent = clk_composite_get_parent ;
2014-07-03 03:57:30 +04:00
if ( mux_ops - > set_parent )
clk_composite_ops - > set_parent = clk_composite_set_parent ;
2013-09-15 04:37:59 +04:00
if ( mux_ops - > determine_rate )
clk_composite_ops - > determine_rate = clk_composite_determine_rate ;
2013-03-20 16:00:34 +04:00
}
2013-04-11 22:31:36 +04:00
if ( rate_hw & & rate_ops ) {
2013-04-11 22:31:37 +04:00
if ( ! rate_ops - > recalc_rate ) {
2016-02-07 11:20:31 +03:00
hw = ERR_PTR ( - EINVAL ) ;
2013-03-20 16:00:34 +04:00
goto err ;
}
2014-07-03 03:58:14 +04:00
clk_composite_ops - > recalc_rate = clk_composite_recalc_rate ;
2013-03-20 16:00:34 +04:00
2014-07-03 03:58:14 +04:00
if ( rate_ops - > determine_rate )
clk_composite_ops - > determine_rate =
clk_composite_determine_rate ;
else if ( rate_ops - > round_rate )
clk_composite_ops - > round_rate =
clk_composite_round_rate ;
/* .set_rate requires either .round_rate or .determine_rate */
if ( rate_ops - > set_rate ) {
if ( rate_ops - > determine_rate | | rate_ops - > round_rate )
clk_composite_ops - > set_rate =
clk_composite_set_rate ;
else
WARN ( 1 , " %s: missing round_rate op is required \n " ,
__func__ ) ;
2013-04-11 22:31:37 +04:00
}
2013-04-11 22:31:36 +04:00
composite - > rate_hw = rate_hw ;
composite - > rate_ops = rate_ops ;
2013-03-20 16:00:34 +04:00
}
2016-04-12 11:43:39 +03:00
if ( mux_hw & & mux_ops & & rate_hw & & rate_ops ) {
if ( mux_ops - > set_parent & & rate_ops - > set_rate )
clk_composite_ops - > set_rate_and_parent =
clk_composite_set_rate_and_parent ;
}
2013-03-20 16:00:34 +04:00
if ( gate_hw & & gate_ops ) {
if ( ! gate_ops - > is_enabled | | ! gate_ops - > enable | |
! gate_ops - > disable ) {
2016-02-07 11:20:31 +03:00
hw = ERR_PTR ( - EINVAL ) ;
2013-03-20 16:00:34 +04:00
goto err ;
}
composite - > gate_hw = gate_hw ;
composite - > gate_ops = gate_ops ;
clk_composite_ops - > is_enabled = clk_composite_is_enabled ;
clk_composite_ops - > enable = clk_composite_enable ;
clk_composite_ops - > disable = clk_composite_disable ;
}
init . ops = clk_composite_ops ;
composite - > hw . init = & init ;
2016-02-07 11:20:31 +03:00
ret = clk_hw_register ( dev , hw ) ;
if ( ret ) {
hw = ERR_PTR ( ret ) ;
2013-03-20 16:00:34 +04:00
goto err ;
2016-02-07 11:20:31 +03:00
}
2013-03-20 16:00:34 +04:00
if ( composite - > mux_hw )
2016-02-07 11:20:31 +03:00
composite - > mux_hw - > clk = hw - > clk ;
2013-03-20 16:00:34 +04:00
2013-04-11 22:31:36 +04:00
if ( composite - > rate_hw )
2016-02-07 11:20:31 +03:00
composite - > rate_hw - > clk = hw - > clk ;
2013-03-20 16:00:34 +04:00
if ( composite - > gate_hw )
2016-02-07 11:20:31 +03:00
composite - > gate_hw - > clk = hw - > clk ;
2013-03-20 16:00:34 +04:00
2016-02-07 11:20:31 +03:00
return hw ;
2013-03-20 16:00:34 +04:00
err :
kfree ( composite ) ;
2016-02-07 11:20:31 +03:00
return hw ;
}
struct clk * clk_register_composite ( struct device * dev , const char * name ,
const char * const * parent_names , int num_parents ,
struct clk_hw * mux_hw , const struct clk_ops * mux_ops ,
struct clk_hw * rate_hw , const struct clk_ops * rate_ops ,
struct clk_hw * gate_hw , const struct clk_ops * gate_ops ,
unsigned long flags )
{
struct clk_hw * hw ;
hw = clk_hw_register_composite ( dev , name , parent_names , num_parents ,
mux_hw , mux_ops , rate_hw , rate_ops , gate_hw , gate_ops ,
flags ) ;
if ( IS_ERR ( hw ) )
return ERR_CAST ( hw ) ;
return hw - > clk ;
2013-03-20 16:00:34 +04:00
}
2016-03-23 19:38:24 +03:00
void clk_unregister_composite ( struct clk * clk )
{
struct clk_composite * composite ;
struct clk_hw * hw ;
hw = __clk_get_hw ( clk ) ;
if ( ! hw )
return ;
composite = to_clk_composite ( hw ) ;
clk_unregister ( clk ) ;
kfree ( composite ) ;
}