2013-06-18 19:55:59 +04:00
/*
* OMAP gate clock 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/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/clk/ti.h>
2014-12-16 19:20:48 +03:00
# include "clock.h"
2013-06-18 19:55:59 +04:00
# undef pr_fmt
# define pr_fmt(fmt) "%s: " fmt, __func__
static int omap36xx_gate_clk_enable_with_hsdiv_restore ( struct clk_hw * clk ) ;
static const struct clk_ops omap_gate_clkdm_clk_ops = {
. init = & omap2_init_clk_clkdm ,
. enable = & omap2_clkops_enable_clkdm ,
. disable = & omap2_clkops_disable_clkdm ,
2018-09-04 09:49:37 +03:00
. restore_context = clk_gate_restore_context ,
2013-06-18 19:55:59 +04:00
} ;
2017-02-09 12:10:19 +03:00
const struct clk_ops omap_gate_clk_ops = {
2013-06-18 19:55:59 +04:00
. init = & omap2_init_clk_clkdm ,
. enable = & omap2_dflt_clk_enable ,
. disable = & omap2_dflt_clk_disable ,
. is_enabled = & omap2_dflt_clk_is_enabled ,
2018-09-04 09:49:37 +03:00
. restore_context = clk_gate_restore_context ,
2013-06-18 19:55:59 +04:00
} ;
static const struct clk_ops omap_gate_clk_hsdiv_restore_ops = {
. init = & omap2_init_clk_clkdm ,
. enable = & omap36xx_gate_clk_enable_with_hsdiv_restore ,
. disable = & omap2_dflt_clk_disable ,
. is_enabled = & omap2_dflt_clk_is_enabled ,
2018-09-04 09:49:37 +03:00
. restore_context = clk_gate_restore_context ,
2013-06-18 19:55:59 +04:00
} ;
/**
* omap36xx_gate_clk_enable_with_hsdiv_restore - enable clocks suffering
* from HSDivider PWRDN problem Implements Errata ID : i556 .
2021-01-20 12:30:36 +03:00
* @ hw : DPLL output struct clk_hw
2013-06-18 19:55:59 +04:00
*
* 3630 only : dpll3_m3_ck , dpll4_m2_ck , dpll4_m3_ck , dpll4_m4_ck ,
* dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset
* valueafter their respective PWRDN bits are set . Any dummy write
* ( Any other value different from the Read value ) to the
* corresponding CM_CLKSEL register will refresh the dividers .
*/
2015-07-31 03:20:57 +03:00
static int omap36xx_gate_clk_enable_with_hsdiv_restore ( struct clk_hw * hw )
2013-06-18 19:55:59 +04:00
{
2017-02-09 15:45:45 +03:00
struct clk_omap_divider * parent ;
2013-06-18 19:55:59 +04:00
struct clk_hw * parent_hw ;
u32 dummy_v , orig_v ;
int ret ;
/* Clear PWRDN bit of HSDIVIDER */
2015-07-31 03:20:57 +03:00
ret = omap2_dflt_clk_enable ( hw ) ;
2013-06-18 19:55:59 +04:00
/* Parent is the x2 node, get parent of parent for the m2 div */
2015-07-31 03:20:57 +03:00
parent_hw = clk_hw_get_parent ( clk_hw_get_parent ( hw ) ) ;
2017-02-09 15:45:45 +03:00
parent = to_clk_omap_divider ( parent_hw ) ;
2013-06-18 19:55:59 +04:00
/* Restore the dividers */
if ( ! ret ) {
2017-02-09 12:24:37 +03:00
orig_v = ti_clk_ll_ops - > clk_readl ( & parent - > reg ) ;
2013-06-18 19:55:59 +04:00
dummy_v = orig_v ;
/* Write any other value different from the Read value */
dummy_v ^ = ( 1 < < parent - > shift ) ;
2017-02-09 12:24:37 +03:00
ti_clk_ll_ops - > clk_writel ( dummy_v , & parent - > reg ) ;
2013-06-18 19:55:59 +04:00
/* Write the original divider */
2017-02-09 12:24:37 +03:00
ti_clk_ll_ops - > clk_writel ( orig_v , & parent - > reg ) ;
2013-06-18 19:55:59 +04:00
}
return ret ;
}
2014-12-16 19:20:48 +03:00
static struct clk * _register_gate ( struct device * dev , const char * name ,
const char * parent_name , unsigned long flags ,
2017-02-09 12:24:37 +03:00
struct clk_omap_reg * reg , u8 bit_idx ,
2014-12-16 19:20:48 +03:00
u8 clk_gate_flags , const struct clk_ops * ops ,
const struct clk_hw_omap_ops * hw_ops )
2013-06-18 19:55:59 +04:00
{
struct clk_init_data init = { NULL } ;
struct clk_hw_omap * clk_hw ;
2014-12-16 19:20:48 +03:00
struct clk * clk ;
2013-06-18 19:55:59 +04:00
clk_hw = kzalloc ( sizeof ( * clk_hw ) , GFP_KERNEL ) ;
if ( ! clk_hw )
2014-12-16 19:20:48 +03:00
return ERR_PTR ( - ENOMEM ) ;
2013-06-18 19:55:59 +04:00
clk_hw - > hw . init = & init ;
2014-12-16 19:20:48 +03:00
init . name = name ;
2013-06-18 19:55:59 +04:00
init . ops = ops ;
2017-02-09 12:24:37 +03:00
memcpy ( & clk_hw - > enable_reg , reg , sizeof ( * reg ) ) ;
2014-12-16 19:20:48 +03:00
clk_hw - > enable_bit = bit_idx ;
clk_hw - > ops = hw_ops ;
2013-06-18 19:55:59 +04:00
2017-01-30 17:01:36 +03:00
clk_hw - > flags = clk_gate_flags ;
2014-12-16 19:20:48 +03:00
init . parent_names = & parent_name ;
init . num_parents = 1 ;
init . flags = flags ;
2019-01-15 12:15:15 +03:00
clk = ti_clk_register_omap_hw ( NULL , & clk_hw - > hw , name ) ;
2014-12-16 19:20:48 +03:00
if ( IS_ERR ( clk ) )
kfree ( clk_hw ) ;
return clk ;
}
static void __init _of_ti_gate_clk_setup ( struct device_node * node ,
const struct clk_ops * ops ,
const struct clk_hw_omap_ops * hw_ops )
{
struct clk * clk ;
const char * parent_name ;
2017-02-09 12:24:37 +03:00
struct clk_omap_reg reg ;
2014-12-16 19:20:48 +03:00
u8 enable_bit = 0 ;
u32 val ;
u32 flags = 0 ;
u8 clk_gate_flags = 0 ;
if ( ops ! = & omap_gate_clkdm_clk_ops ) {
2017-02-09 12:24:37 +03:00
if ( ti_clk_get_reg_addr ( node , 0 , & reg ) )
2014-12-16 19:20:48 +03:00
return ;
if ( ! of_property_read_u32 ( node , " ti,bit-shift " , & val ) )
enable_bit = val ;
}
2013-06-18 19:55:59 +04:00
if ( of_clk_get_parent_count ( node ) ! = 1 ) {
2018-08-28 18:44:29 +03:00
pr_err ( " %pOFn must have 1 parent \n " , node ) ;
2014-12-16 19:20:48 +03:00
return ;
2013-06-18 19:55:59 +04:00
}
parent_name = of_clk_get_parent_name ( node , 0 ) ;
if ( of_property_read_bool ( node , " ti,set-rate-parent " ) )
2014-12-16 19:20:48 +03:00
flags | = CLK_SET_RATE_PARENT ;
2013-06-18 19:55:59 +04:00
if ( of_property_read_bool ( node , " ti,set-bit-to-disable " ) )
2014-12-16 19:20:48 +03:00
clk_gate_flags | = INVERT_ENABLE ;
2013-06-18 19:55:59 +04:00
2017-02-09 12:24:37 +03:00
clk = _register_gate ( NULL , node - > name , parent_name , flags , & reg ,
2014-12-16 19:20:48 +03:00
enable_bit , clk_gate_flags , ops , hw_ops ) ;
2013-06-18 19:55:59 +04:00
2014-12-16 19:20:48 +03:00
if ( ! IS_ERR ( clk ) )
2013-06-18 19:55:59 +04:00
of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
}
static void __init
_of_ti_composite_gate_clk_setup ( struct device_node * node ,
const struct clk_hw_omap_ops * hw_ops )
{
struct clk_hw_omap * gate ;
u32 val = 0 ;
gate = kzalloc ( sizeof ( * gate ) , GFP_KERNEL ) ;
if ( ! gate )
return ;
2017-02-09 12:24:37 +03:00
if ( ti_clk_get_reg_addr ( node , 0 , & gate - > enable_reg ) )
2013-06-18 19:55:59 +04:00
goto cleanup ;
of_property_read_u32 ( node , " ti,bit-shift " , & val ) ;
gate - > enable_bit = val ;
gate - > ops = hw_ops ;
if ( ! ti_clk_add_component ( node , & gate - > hw , CLK_COMPONENT_TYPE_GATE ) )
return ;
cleanup :
kfree ( gate ) ;
}
static void __init
of_ti_composite_no_wait_gate_clk_setup ( struct device_node * node )
{
_of_ti_composite_gate_clk_setup ( node , NULL ) ;
}
CLK_OF_DECLARE ( ti_composite_no_wait_gate_clk , " ti,composite-no-wait-gate-clock " ,
of_ti_composite_no_wait_gate_clk_setup ) ;
2014-03-05 12:03:38 +04:00
# if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
2013-06-18 19:55:59 +04:00
static void __init of_ti_composite_interface_clk_setup ( struct device_node * node )
{
_of_ti_composite_gate_clk_setup ( node , & clkhwops_iclk_wait ) ;
}
CLK_OF_DECLARE ( ti_composite_interface_clk , " ti,composite-interface-clock " ,
of_ti_composite_interface_clk_setup ) ;
# endif
static void __init of_ti_composite_gate_clk_setup ( struct device_node * node )
{
_of_ti_composite_gate_clk_setup ( node , & clkhwops_wait ) ;
}
CLK_OF_DECLARE ( ti_composite_gate_clk , " ti,composite-gate-clock " ,
of_ti_composite_gate_clk_setup ) ;
static void __init of_ti_clkdm_gate_clk_setup ( struct device_node * node )
{
_of_ti_gate_clk_setup ( node , & omap_gate_clkdm_clk_ops , NULL ) ;
}
CLK_OF_DECLARE ( ti_clkdm_gate_clk , " ti,clkdm-gate-clock " ,
of_ti_clkdm_gate_clk_setup ) ;
static void __init of_ti_hsdiv_gate_clk_setup ( struct device_node * node )
{
_of_ti_gate_clk_setup ( node , & omap_gate_clk_hsdiv_restore_ops ,
& clkhwops_wait ) ;
}
CLK_OF_DECLARE ( ti_hsdiv_gate_clk , " ti,hsdiv-gate-clock " ,
of_ti_hsdiv_gate_clk_setup ) ;
static void __init of_ti_gate_clk_setup ( struct device_node * node )
{
_of_ti_gate_clk_setup ( node , & omap_gate_clk_ops , NULL ) ;
}
2014-05-12 20:41:19 +04:00
CLK_OF_DECLARE ( ti_gate_clk , " ti,gate-clock " , of_ti_gate_clk_setup ) ;
2013-06-18 19:55:59 +04:00
static void __init of_ti_wait_gate_clk_setup ( struct device_node * node )
{
_of_ti_gate_clk_setup ( node , & omap_gate_clk_ops , & clkhwops_wait ) ;
}
CLK_OF_DECLARE ( ti_wait_gate_clk , " ti,wait-gate-clock " ,
of_ti_wait_gate_clk_setup ) ;
# ifdef CONFIG_ARCH_OMAP3
static void __init of_ti_am35xx_gate_clk_setup ( struct device_node * node )
{
_of_ti_gate_clk_setup ( node , & omap_gate_clk_ops ,
& clkhwops_am35xx_ipss_module_wait ) ;
}
CLK_OF_DECLARE ( ti_am35xx_gate_clk , " ti,am35xx-gate-clock " ,
of_ti_am35xx_gate_clk_setup ) ;
static void __init of_ti_dss_gate_clk_setup ( struct device_node * node )
{
_of_ti_gate_clk_setup ( node , & omap_gate_clk_ops ,
& clkhwops_omap3430es2_dss_usbhost_wait ) ;
}
CLK_OF_DECLARE ( ti_dss_gate_clk , " ti,dss-gate-clock " ,
of_ti_dss_gate_clk_setup ) ;
# endif