2013-08-21 20:39:15 +04:00
/*
* OMAP clockdomain support
*
* Copyright ( C ) 2013 Texas Instruments , Inc .
*
* Tero Kristo < t - kristo @ ti . com >
*
* 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 .
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2015-06-20 01:00:46 +03:00
# include <linux/clk.h>
2013-08-21 20:39:15 +04: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 20:39:15 +04:00
# undef pr_fmt
# define pr_fmt(fmt) "%s: " fmt, __func__
2015-03-03 17:22:50 +03: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 21:42:23 +03:00
clk_hw_get_name ( hw ) ) ;
2015-03-03 17:22:50 +03: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 21:42:23 +03:00
__func__ , clk_hw_get_name ( hw ) ) ;
2015-03-03 17:22:50 +03: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 21:42:23 +03:00
__func__ , clk_hw_get_name ( hw ) , clk - > clkdm_name , ret ) ;
2015-03-03 17:22:50 +03: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 21:42:23 +03:00
clk_hw_get_name ( hw ) ) ;
2015-03-03 17:22:50 +03: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 21:42:23 +03:00
__func__ , clk_hw_get_name ( hw ) ) ;
2015-03-03 17:22:50 +03: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
* @ clk : OMAP clock struct ptr to use
*
* Convert a clockdomain name stored in a struct clk ' clk ' into a
* clockdomain pointer , and save it into the struct clk . Intended to be
* called during clk_register ( ) . No return value .
*/
void omap2_init_clk_clkdm ( struct clk_hw * hw )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
struct clockdomain * clkdm ;
const char * clk_name ;
if ( ! clk - > clkdm_name )
return ;
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 ) ;
}
}
2013-08-21 20:39:15 +04:00
static void __init of_ti_clockdomain_setup ( struct device_node * node )
{
struct clk * clk ;
struct clk_hw * clk_hw ;
const char * clkdm_name = node - > name ;
int i ;
2016-02-20 04:49:23 +03:00
unsigned int num_clks ;
2013-08-21 20:39:15 +04:00
2015-05-29 12:25:47 +03:00
num_clks = of_clk_get_parent_count ( node ) ;
2013-08-21 20:39:15 +04:00
for ( i = 0 ; i < num_clks ; i + + ) {
clk = of_clk_get ( node , i ) ;
2014-09-18 18:33:27 +04:00
if ( IS_ERR ( clk ) ) {
2017-07-19 00:42:52 +03:00
pr_err ( " %s: Failed get %pOF' clock nr %d (%ld) \n " ,
__func__ , node , i , PTR_ERR ( clk ) ) ;
2014-09-18 18:33:27 +04:00
continue ;
}
2015-06-30 02:56:30 +03:00
clk_hw = __clk_get_hw ( clk ) ;
2019-01-15 12:15:14 +03:00
if ( ! omap2_clk_is_hw_omap ( clk_hw ) ) {
2013-08-21 20:39:15 +04:00
pr_warn ( " can't setup clkdm for basic clk %s \n " ,
__clk_get_name ( clk ) ) ;
continue ;
}
to_clk_hw_omap ( clk_hw ) - > clkdm_name = clkdm_name ;
omap2_init_clk_clkdm ( clk_hw ) ;
}
}
2015-03-31 21:50:42 +03:00
static const struct of_device_id ti_clkdm_match_table [ ] __initconst = {
2013-08-21 20:39:15 +04: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 ) ;
}
}