2022-06-07 16:11:32 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2013-08-21 19:39:15 +03:00
/*
* OMAP clockdomain support
*
* Copyright ( C ) 2013 Texas Instruments , Inc .
*
* Tero Kristo < t - kristo @ ti . com >
*/
2015-06-19 15:00:46 -07:00
# include <linux/clk.h>
2013-08-21 19:39:15 +03:00
# include <linux/clk-provider.h>
# include <linux/slab.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/clk/ti.h>
2015-04-27 21:55:42 +03:00
# include "clock.h"
2013-08-21 19:39:15 +03:00
# undef pr_fmt
# define pr_fmt(fmt) "%s: " fmt, __func__
2015-03-03 16:22:50 +02:00
/**
* omap2_clkops_enable_clkdm - increment usecount on clkdm of @ hw
* @ hw : struct clk_hw * of the clock being enabled
*
* Increment the usecount of the clockdomain of the clock pointed to
* by @ hw ; if the usecount is 1 , the clockdomain will be " enabled. "
* Only needed for clocks that don ' t use omap2_dflt_clk_enable ( ) as
* their enable function pointer . Passes along the return value of
* clkdm_clk_enable ( ) , - EINVAL if @ hw is not associated with a
* clockdomain , or 0 if clock framework - based clockdomain control is
* not implemented .
*/
int omap2_clkops_enable_clkdm ( struct clk_hw * hw )
{
struct clk_hw_omap * clk ;
int ret = 0 ;
clk = to_clk_hw_omap ( hw ) ;
if ( unlikely ( ! clk - > clkdm ) ) {
pr_err ( " %s: %s: no clkdm set ?! \n " , __func__ ,
2015-08-12 11:42:23 -07:00
clk_hw_get_name ( hw ) ) ;
2015-03-03 16:22:50 +02:00
return - EINVAL ;
}
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DISABLE_CLKDM_CONTROL ) {
pr_err ( " %s: %s: clkfw-based clockdomain control disabled ?! \n " ,
2015-08-12 11:42:23 -07:00
__func__ , clk_hw_get_name ( hw ) ) ;
2015-03-03 16:22:50 +02:00
return 0 ;
}
ret = ti_clk_ll_ops - > clkdm_clk_enable ( clk - > clkdm , hw - > clk ) ;
WARN ( ret , " %s: could not enable %s's clockdomain %s: %d \n " ,
2015-08-12 11:42:23 -07:00
__func__ , clk_hw_get_name ( hw ) , clk - > clkdm_name , ret ) ;
2015-03-03 16:22:50 +02:00
return ret ;
}
/**
* omap2_clkops_disable_clkdm - decrement usecount on clkdm of @ hw
* @ hw : struct clk_hw * of the clock being disabled
*
* Decrement the usecount of the clockdomain of the clock pointed to
* by @ hw ; if the usecount is 0 , the clockdomain will be " disabled. "
* Only needed for clocks that don ' t use omap2_dflt_clk_disable ( ) as their
* disable function pointer . No return value .
*/
void omap2_clkops_disable_clkdm ( struct clk_hw * hw )
{
struct clk_hw_omap * clk ;
clk = to_clk_hw_omap ( hw ) ;
if ( unlikely ( ! clk - > clkdm ) ) {
pr_err ( " %s: %s: no clkdm set ?! \n " , __func__ ,
2015-08-12 11:42:23 -07:00
clk_hw_get_name ( hw ) ) ;
2015-03-03 16:22:50 +02:00
return ;
}
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DISABLE_CLKDM_CONTROL ) {
pr_err ( " %s: %s: clkfw-based clockdomain control disabled ?! \n " ,
2015-08-12 11:42:23 -07:00
__func__ , clk_hw_get_name ( hw ) ) ;
2015-03-03 16:22:50 +02:00
return ;
}
ti_clk_ll_ops - > clkdm_clk_disable ( clk - > clkdm , hw - > clk ) ;
}
2016-09-30 14:13:38 +03:00
/**
* omap2_init_clk_clkdm - look up a clockdomain name , store pointer in clk
2021-01-20 09:30:33 +00:00
* @ hw : Pointer to clk_hw_omap used to obtain OMAP clock struct ptr to use
2016-09-30 14:13:38 +03:00
*
* Convert a clockdomain name stored in a struct clk ' clk ' into a
* clockdomain pointer , and save it into the struct clk . Intended to be
2019-09-24 14:39:53 +02:00
* called during clk_register ( ) . Returns 0 on success , - EERROR otherwise .
2016-09-30 14:13:38 +03:00
*/
2019-09-24 14:39:53 +02:00
int omap2_init_clk_clkdm ( struct clk_hw * hw )
2016-09-30 14:13:38 +03:00
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
struct clockdomain * clkdm ;
const char * clk_name ;
if ( ! clk - > clkdm_name )
2019-09-24 14:39:53 +02:00
return 0 ;
2016-09-30 14:13:38 +03:00
clk_name = __clk_get_name ( hw - > clk ) ;
clkdm = ti_clk_ll_ops - > clkdm_lookup ( clk - > clkdm_name ) ;
if ( clkdm ) {
pr_debug ( " clock: associated clk %s to clkdm %s \n " ,
clk_name , clk - > clkdm_name ) ;
clk - > clkdm = clkdm ;
} else {
pr_debug ( " clock: could not associate clk %s to clkdm %s \n " ,
clk_name , clk - > clkdm_name ) ;
}
2019-09-24 14:39:53 +02:00
return 0 ;
2016-09-30 14:13:38 +03:00
}
2013-08-21 19:39:15 +03:00
static void __init of_ti_clockdomain_setup ( struct device_node * node )
{
struct clk * clk ;
struct clk_hw * clk_hw ;
2022-02-04 09:14:48 +02:00
const char * clkdm_name = ti_dt_clk_name ( node ) ;
2013-08-21 19:39:15 +03:00
int i ;
2016-02-19 17:49:23 -08:00
unsigned int num_clks ;
2013-08-21 19:39:15 +03:00
2015-05-29 11:25:47 +02:00
num_clks = of_clk_get_parent_count ( node ) ;
2013-08-21 19:39:15 +03:00
for ( i = 0 ; i < num_clks ; i + + ) {
clk = of_clk_get ( node , i ) ;
2014-09-18 16:33:27 +02:00
if ( IS_ERR ( clk ) ) {
2017-07-18 16:42:52 -05:00
pr_err ( " %s: Failed get %pOF' clock nr %d (%ld) \n " ,
__func__ , node , i , PTR_ERR ( clk ) ) ;
2014-09-18 16:33:27 +02:00
continue ;
}
2015-06-29 16:56:30 -07:00
clk_hw = __clk_get_hw ( clk ) ;
2019-01-15 11:15:14 +02:00
if ( ! omap2_clk_is_hw_omap ( clk_hw ) ) {
2013-08-21 19:39:15 +03:00
pr_warn ( " can't setup clkdm for basic clk %s \n " ,
__clk_get_name ( clk ) ) ;
2020-09-07 11:25:59 +03:00
clk_put ( clk ) ;
2013-08-21 19:39:15 +03:00
continue ;
}
to_clk_hw_omap ( clk_hw ) - > clkdm_name = clkdm_name ;
omap2_init_clk_clkdm ( clk_hw ) ;
2020-09-07 11:25:59 +03:00
clk_put ( clk ) ;
2013-08-21 19:39:15 +03:00
}
}
2015-03-31 20:50:42 +02:00
static const struct of_device_id ti_clkdm_match_table [ ] __initconst = {
2013-08-21 19:39:15 +03:00
{ . compatible = " ti,clockdomain " } ,
{ }
} ;
/**
* ti_dt_clockdomains_setup - setup device tree clockdomains
*
* Initializes clockdomain nodes for a SoC . This parses through all the
* nodes with compatible = " ti,clockdomain " , and add the clockdomain
* info for all the clocks listed under these . This function shall be
* called after rest of the DT clock init has completed and all
* clock nodes have been registered .
*/
void __init ti_dt_clockdomains_setup ( void )
{
struct device_node * np ;
for_each_matching_node ( np , ti_clkdm_match_table ) {
of_ti_clockdomain_setup ( np ) ;
}
}