2022-06-07 16:11:32 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2013-09-09 15:46:45 +03:00
/*
* TI composite clock support
*
* Copyright ( C ) 2013 Texas Instruments , Inc .
*
* Tero Kristo < t - kristo @ ti . com >
*/
# include <linux/clk-provider.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/clk/ti.h>
# include <linux/list.h>
2014-12-16 18:20:52 +02:00
# include "clock.h"
2013-09-09 15:46:45 +03:00
# undef pr_fmt
# define pr_fmt(fmt) "%s: " fmt, __func__
static unsigned long ti_composite_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2013-09-13 12:02:15 +03:00
return ti_clk_divider_ops . recalc_rate ( hw , parent_rate ) ;
2013-09-09 15:46:45 +03:00
}
static long ti_composite_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
return - EINVAL ;
}
static int ti_composite_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
return - EINVAL ;
}
static const struct clk_ops ti_composite_divider_ops = {
. recalc_rate = & ti_composite_recalc_rate ,
. round_rate = & ti_composite_round_rate ,
. set_rate = & ti_composite_set_rate ,
} ;
static const struct clk_ops ti_composite_gate_ops = {
. enable = & omap2_dflt_clk_enable ,
. disable = & omap2_dflt_clk_disable ,
. is_enabled = & omap2_dflt_clk_is_enabled ,
} ;
struct component_clk {
int num_parents ;
const char * * parent_names ;
struct device_node * node ;
int type ;
struct clk_hw * hw ;
struct list_head link ;
} ;
2015-02-18 10:59:45 +01:00
static const char * const component_clk_types [ ] __initconst = {
2013-09-09 15:46:45 +03:00
" gate " , " divider " , " mux "
} ;
static LIST_HEAD ( component_clks ) ;
static struct device_node * _get_component_node ( struct device_node * node , int i )
{
int rc ;
struct of_phandle_args clkspec ;
rc = of_parse_phandle_with_args ( node , " clocks " , " #clock-cells " , i ,
& clkspec ) ;
if ( rc )
return NULL ;
return clkspec . np ;
}
static struct component_clk * _lookup_component ( struct device_node * node )
{
struct component_clk * comp ;
list_for_each_entry ( comp , & component_clks , link ) {
if ( comp - > node = = node )
return comp ;
}
return NULL ;
}
struct clk_hw_omap_comp {
struct clk_hw hw ;
struct device_node * comp_nodes [ CLK_COMPONENT_TYPE_MAX ] ;
struct component_clk * comp_clks [ CLK_COMPONENT_TYPE_MAX ] ;
} ;
static inline struct clk_hw * _get_hw ( struct clk_hw_omap_comp * clk , int idx )
{
if ( ! clk )
return NULL ;
if ( ! clk - > comp_clks [ idx ] )
return NULL ;
return clk - > comp_clks [ idx ] - > hw ;
}
# define to_clk_hw_comp(_hw) container_of(_hw, struct clk_hw_omap_comp, hw)
2017-11-06 09:43:16 +02:00
static void __init _register_composite ( void * user ,
2014-12-16 18:20:52 +02:00
struct device_node * node )
2013-09-09 15:46:45 +03:00
{
2017-11-06 09:43:16 +02:00
struct clk_hw * hw = user ;
2013-09-09 15:46:45 +03:00
struct clk * clk ;
struct clk_hw_omap_comp * cclk = to_clk_hw_comp ( hw ) ;
struct component_clk * comp ;
int num_parents = 0 ;
const char * * parent_names = NULL ;
2022-02-04 09:14:49 +02:00
const char * name ;
2013-09-09 15:46:45 +03:00
int i ;
2016-09-29 12:06:40 +03:00
int ret ;
2013-09-09 15:46:45 +03:00
/* Check for presence of each component clock */
for ( i = 0 ; i < CLK_COMPONENT_TYPE_MAX ; i + + ) {
if ( ! cclk - > comp_nodes [ i ] )
continue ;
comp = _lookup_component ( cclk - > comp_nodes [ i ] ) ;
if ( ! comp ) {
2018-08-28 10:44:29 -05:00
pr_debug ( " component %s not ready for %pOFn, retry \n " ,
cclk - > comp_nodes [ i ] - > name , node ) ;
2013-09-09 15:46:45 +03:00
if ( ! ti_clk_retry_init ( node , hw ,
2014-12-16 18:20:52 +02:00
_register_composite ) )
2013-09-09 15:46:45 +03:00
return ;
goto cleanup ;
}
if ( cclk - > comp_clks [ comp - > type ] ! = NULL ) {
2018-08-28 10:44:29 -05:00
pr_err ( " duplicate component types for %pOFn (%s)! \n " ,
node , component_clk_types [ comp - > type ] ) ;
2013-09-09 15:46:45 +03:00
goto cleanup ;
}
cclk - > comp_clks [ comp - > type ] = comp ;
/* Mark this node as found */
cclk - > comp_nodes [ i ] = NULL ;
}
/* All components exists, proceed with registration */
for ( i = CLK_COMPONENT_TYPE_MAX - 1 ; i > = 0 ; i - - ) {
comp = cclk - > comp_clks [ i ] ;
if ( ! comp )
continue ;
if ( comp - > num_parents ) {
num_parents = comp - > num_parents ;
parent_names = comp - > parent_names ;
break ;
}
}
if ( ! num_parents ) {
2018-08-28 10:44:29 -05:00
pr_err ( " %s: no parents found for %pOFn! \n " , __func__ , node ) ;
2013-09-09 15:46:45 +03:00
goto cleanup ;
}
2022-02-04 09:14:49 +02:00
name = ti_dt_clk_name ( node ) ;
clk = clk_register_composite ( NULL , name ,
2013-09-09 15:46:45 +03:00
parent_names , num_parents ,
_get_hw ( cclk , CLK_COMPONENT_TYPE_MUX ) ,
2013-09-13 20:22:27 +03:00
& ti_clk_mux_ops ,
2013-09-09 15:46:45 +03:00
_get_hw ( cclk , CLK_COMPONENT_TYPE_DIVIDER ) ,
& ti_composite_divider_ops ,
_get_hw ( cclk , CLK_COMPONENT_TYPE_GATE ) ,
& ti_composite_gate_ops , 0 ) ;
2016-09-29 12:06:40 +03:00
if ( ! IS_ERR ( clk ) ) {
2022-11-13 19:11:46 +01:00
ret = ti_clk_add_alias ( clk , name ) ;
2016-09-29 12:06:40 +03:00
if ( ret ) {
clk_unregister ( clk ) ;
goto cleanup ;
}
2013-09-09 15:46:45 +03:00
of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
2016-09-29 12:06:40 +03:00
}
2013-09-09 15:46:45 +03:00
cleanup :
/* Free component clock list entries */
for ( i = 0 ; i < CLK_COMPONENT_TYPE_MAX ; i + + ) {
if ( ! cclk - > comp_clks [ i ] )
continue ;
list_del ( & cclk - > comp_clks [ i ] - > link ) ;
2020-04-29 16:13:39 +03:00
kfree ( cclk - > comp_clks [ i ] - > parent_names ) ;
2013-09-09 15:46:45 +03:00
kfree ( cclk - > comp_clks [ i ] ) ;
}
kfree ( cclk ) ;
}
static void __init of_ti_composite_clk_setup ( struct device_node * node )
{
2016-02-19 17:49:23 -08:00
unsigned int num_clks ;
2013-09-09 15:46:45 +03:00
int i ;
struct clk_hw_omap_comp * cclk ;
/* Number of component clocks to be put inside this clock */
num_clks = of_clk_get_parent_count ( node ) ;
2016-02-19 17:49:23 -08:00
if ( ! num_clks ) {
2018-08-28 10:44:29 -05:00
pr_err ( " composite clk %pOFn must have component(s) \n " , node ) ;
2013-09-09 15:46:45 +03:00
return ;
}
cclk = kzalloc ( sizeof ( * cclk ) , GFP_KERNEL ) ;
if ( ! cclk )
return ;
/* Get device node pointers for each component clock */
for ( i = 0 ; i < num_clks ; i + + )
cclk - > comp_nodes [ i ] = _get_component_node ( node , i ) ;
2014-12-16 18:20:52 +02:00
_register_composite ( & cclk - > hw , node ) ;
2013-09-09 15:46:45 +03:00
}
CLK_OF_DECLARE ( ti_composite_clock , " ti,composite-clock " ,
of_ti_composite_clk_setup ) ;
/**
* ti_clk_add_component - add a component clock to the pool
* @ node : device node of the component clock
* @ hw : hardware clock definition for the component clock
* @ type : type of the component clock
*
* Adds a component clock to the list of available components , so that
* it can be registered by a composite clock .
*/
int __init ti_clk_add_component ( struct device_node * node , struct clk_hw * hw ,
int type )
{
2016-02-19 17:49:23 -08:00
unsigned int num_parents ;
2013-09-09 15:46:45 +03:00
const char * * parent_names ;
struct component_clk * clk ;
num_parents = of_clk_get_parent_count ( node ) ;
2016-02-19 17:49:23 -08:00
if ( ! num_parents ) {
2018-08-28 10:44:29 -05:00
pr_err ( " component-clock %pOFn must have parent(s) \n " , node ) ;
2013-09-09 15:46:45 +03:00
return - EINVAL ;
}
2021-09-04 15:17:14 +02:00
parent_names = kcalloc ( num_parents , sizeof ( char * ) , GFP_KERNEL ) ;
2013-09-09 15:46:45 +03:00
if ( ! parent_names )
return - ENOMEM ;
2015-07-06 22:59:06 -05:00
of_clk_parent_fill ( node , parent_names , num_parents ) ;
2013-09-09 15:46:45 +03:00
clk = kzalloc ( sizeof ( * clk ) , GFP_KERNEL ) ;
if ( ! clk ) {
kfree ( parent_names ) ;
return - ENOMEM ;
}
clk - > num_parents = num_parents ;
clk - > parent_names = parent_names ;
clk - > hw = hw ;
clk - > node = node ;
clk - > type = type ;
list_add ( & clk - > link , & component_clks ) ;
return 0 ;
}