2013-08-21 19:39:15 +03: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 .
*/
# 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__ ,
__clk_get_name ( hw - > clk ) ) ;
return - EINVAL ;
}
if ( unlikely ( clk - > enable_reg ) )
pr_err ( " %s: %s: should use dflt_clk_enable ?! \n " , __func__ ,
__clk_get_name ( hw - > clk ) ) ;
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DISABLE_CLKDM_CONTROL ) {
pr_err ( " %s: %s: clkfw-based clockdomain control disabled ?! \n " ,
__func__ , __clk_get_name ( hw - > clk ) ) ;
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 " ,
__func__ , __clk_get_name ( hw - > clk ) , clk - > clkdm_name , ret ) ;
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__ ,
__clk_get_name ( hw - > clk ) ) ;
return ;
}
if ( unlikely ( clk - > enable_reg ) )
pr_err ( " %s: %s: should use dflt_clk_disable ?! \n " , __func__ ,
__clk_get_name ( hw - > clk ) ) ;
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DISABLE_CLKDM_CONTROL ) {
pr_err ( " %s: %s: clkfw-based clockdomain control disabled ?! \n " ,
__func__ , __clk_get_name ( hw - > clk ) ) ;
return ;
}
ti_clk_ll_ops - > clkdm_clk_disable ( clk - > clkdm , hw - > clk ) ;
}
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 ;
const char * clkdm_name = node - > name ;
int i ;
int num_clks ;
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 ) ) {
pr_err ( " %s: Failed get %s' clock nr %d (%ld) \n " ,
__func__ , node - > full_name , i , PTR_ERR ( clk ) ) ;
continue ;
}
2013-08-21 19:39:15 +03:00
if ( __clk_get_flags ( clk ) & CLK_IS_BASIC ) {
pr_warn ( " can't setup clkdm for basic clk %s \n " ,
__clk_get_name ( clk ) ) ;
continue ;
}
clk_hw = __clk_get_hw ( clk ) ;
to_clk_hw_omap ( clk_hw ) - > clkdm_name = clkdm_name ;
omap2_init_clk_clkdm ( clk_hw ) ;
}
}
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 ) ;
}
}