2017-02-09 11:27:11 +02:00
/*
* OMAP clkctrl clock support
*
* Copyright ( C ) 2017 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>
# include <linux/delay.h>
2017-08-15 11:42:17 +03:00
# include <linux/timekeeping.h>
2017-02-09 11:27:11 +02:00
# include "clock.h"
2019-09-12 16:26:05 +03:00
# define NO_IDLEST 0
2017-02-09 11:27:11 +02:00
# define OMAP4_MODULEMODE_MASK 0x3
# define MODULEMODE_HWCTRL 0x1
# define MODULEMODE_SWCTRL 0x2
# define OMAP4_IDLEST_MASK (0x3 << 16)
# define OMAP4_IDLEST_SHIFT 16
2019-09-12 16:26:06 +03:00
# define OMAP4_STBYST_MASK BIT(18)
# define OMAP4_STBYST_SHIFT 18
2017-02-09 11:27:11 +02:00
# define CLKCTRL_IDLEST_FUNCTIONAL 0x0
# define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2
# define CLKCTRL_IDLEST_DISABLED 0x3
/* These timeouts are in us */
# define OMAP4_MAX_MODULE_READY_TIME 2000
# define OMAP4_MAX_MODULE_DISABLE_TIME 5000
static bool _early_timeout = true ;
struct omap_clkctrl_provider {
void __iomem * base ;
struct list_head clocks ;
2017-09-18 21:02:24 +03:00
char * clkdm_name ;
2017-02-09 11:27:11 +02:00
} ;
struct omap_clkctrl_clk {
struct clk_hw * clk ;
u16 reg_offset ;
int bit_offset ;
struct list_head node ;
} ;
union omap4_timeout {
u32 cycles ;
ktime_t start ;
} ;
static const struct omap_clkctrl_data default_clkctrl_data [ ] __initconst = {
{ 0 } ,
} ;
static u32 _omap4_idlest ( u32 val )
{
val & = OMAP4_IDLEST_MASK ;
val > > = OMAP4_IDLEST_SHIFT ;
return val ;
}
static bool _omap4_is_idle ( u32 val )
{
val = _omap4_idlest ( val ) ;
return val = = CLKCTRL_IDLEST_DISABLED ;
}
static bool _omap4_is_ready ( u32 val )
{
val = _omap4_idlest ( val ) ;
return val = = CLKCTRL_IDLEST_FUNCTIONAL | |
val = = CLKCTRL_IDLEST_INTERFACE_IDLE ;
}
static bool _omap4_is_timeout ( union omap4_timeout * time , u32 timeout )
{
2017-08-15 11:42:17 +03:00
/*
* There are two special cases where ktime_to_ns ( ) can ' t be
* used to track the timeouts . First one is during early boot
* when the timers haven ' t been initialized yet . The second
* one is during suspend - resume cycle while timekeeping is
* being suspended / resumed . Clocksource for the system
* can be from a timer that requires pm_runtime access , which
* will eventually bring us here with timekeeping_suspended ,
* during both suspend entry and resume paths . This happens
2019-09-30 08:40:01 -07:00
* at least on am43xx platform . Account for flakeyness
* with udelay ( ) by multiplying the timeout value by 2.
2017-08-15 11:42:17 +03:00
*/
if ( unlikely ( _early_timeout | | timekeeping_suspended ) ) {
2017-02-09 11:27:11 +02:00
if ( time - > cycles + + < timeout ) {
2019-09-30 08:40:01 -07:00
udelay ( 1 * 2 ) ;
2017-02-09 11:27:11 +02:00
return false ;
}
} else {
if ( ! ktime_to_ns ( time - > start ) ) {
time - > start = ktime_get ( ) ;
return false ;
}
if ( ktime_us_delta ( ktime_get ( ) , time - > start ) < timeout ) {
cpu_relax ( ) ;
return false ;
}
}
return true ;
}
static int __init _omap4_disable_early_timeout ( void )
{
_early_timeout = false ;
return 0 ;
}
arch_initcall ( _omap4_disable_early_timeout ) ;
static int _omap4_clkctrl_clk_enable ( struct clk_hw * hw )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
u32 val ;
int ret ;
union omap4_timeout timeout = { 0 } ;
if ( clk - > clkdm ) {
ret = ti_clk_ll_ops - > clkdm_clk_enable ( clk - > clkdm , hw - > clk ) ;
if ( ret ) {
WARN ( 1 ,
" %s: could not enable %s's clockdomain %s: %d \n " ,
__func__ , clk_hw_get_name ( hw ) ,
clk - > clkdm_name , ret ) ;
return ret ;
}
}
2019-05-06 14:08:54 -07:00
if ( ! clk - > enable_bit )
return 0 ;
2017-02-09 11:27:11 +02:00
val = ti_clk_ll_ops - > clk_readl ( & clk - > enable_reg ) ;
val & = ~ OMAP4_MODULEMODE_MASK ;
val | = clk - > enable_bit ;
ti_clk_ll_ops - > clk_writel ( val , & clk - > enable_reg ) ;
2019-09-12 16:26:05 +03:00
if ( test_bit ( NO_IDLEST , & clk - > flags ) )
2017-02-09 11:27:11 +02:00
return 0 ;
/* Wait until module is enabled */
while ( ! _omap4_is_ready ( ti_clk_ll_ops - > clk_readl ( & clk - > enable_reg ) ) ) {
if ( _omap4_is_timeout ( & timeout , OMAP4_MAX_MODULE_READY_TIME ) ) {
pr_err ( " %s: failed to enable \n " , clk_hw_get_name ( hw ) ) ;
return - EBUSY ;
}
}
return 0 ;
}
static void _omap4_clkctrl_clk_disable ( struct clk_hw * hw )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
u32 val ;
union omap4_timeout timeout = { 0 } ;
if ( ! clk - > enable_bit )
2019-05-06 14:08:54 -07:00
goto exit ;
2017-02-09 11:27:11 +02:00
val = ti_clk_ll_ops - > clk_readl ( & clk - > enable_reg ) ;
val & = ~ OMAP4_MODULEMODE_MASK ;
ti_clk_ll_ops - > clk_writel ( val , & clk - > enable_reg ) ;
2019-09-12 16:26:05 +03:00
if ( test_bit ( NO_IDLEST , & clk - > flags ) )
2017-02-09 11:27:11 +02:00
goto exit ;
/* Wait until module is disabled */
while ( ! _omap4_is_idle ( ti_clk_ll_ops - > clk_readl ( & clk - > enable_reg ) ) ) {
if ( _omap4_is_timeout ( & timeout ,
OMAP4_MAX_MODULE_DISABLE_TIME ) ) {
pr_err ( " %s: failed to disable \n " , clk_hw_get_name ( hw ) ) ;
break ;
}
}
exit :
if ( clk - > clkdm )
ti_clk_ll_ops - > clkdm_clk_disable ( clk - > clkdm , hw - > clk ) ;
}
static int _omap4_clkctrl_clk_is_enabled ( struct clk_hw * hw )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
u32 val ;
val = ti_clk_ll_ops - > clk_readl ( & clk - > enable_reg ) ;
if ( val & clk - > enable_bit )
return 1 ;
return 0 ;
}
static const struct clk_ops omap4_clkctrl_clk_ops = {
. enable = _omap4_clkctrl_clk_enable ,
. disable = _omap4_clkctrl_clk_disable ,
. is_enabled = _omap4_clkctrl_clk_is_enabled ,
2017-09-18 21:02:24 +03:00
. init = omap2_init_clk_clkdm ,
2017-02-09 11:27:11 +02:00
} ;
static struct clk_hw * _ti_omap4_clkctrl_xlate ( struct of_phandle_args * clkspec ,
void * data )
{
struct omap_clkctrl_provider * provider = data ;
struct omap_clkctrl_clk * entry ;
2019-05-29 23:55:57 -07:00
bool found = false ;
2017-02-09 11:27:11 +02:00
if ( clkspec - > args_count ! = 2 )
return ERR_PTR ( - EINVAL ) ;
pr_debug ( " %s: looking for %x:%x \n " , __func__ ,
clkspec - > args [ 0 ] , clkspec - > args [ 1 ] ) ;
list_for_each_entry ( entry , & provider - > clocks , node ) {
if ( entry - > reg_offset = = clkspec - > args [ 0 ] & &
2019-05-29 23:55:57 -07:00
entry - > bit_offset = = clkspec - > args [ 1 ] ) {
found = true ;
2017-02-09 11:27:11 +02:00
break ;
2019-05-29 23:55:57 -07:00
}
2017-02-09 11:27:11 +02:00
}
2019-05-29 23:55:57 -07:00
if ( ! found )
2017-02-09 11:27:11 +02:00
return ERR_PTR ( - EINVAL ) ;
return entry - > clk ;
}
2020-04-30 11:34:51 +03:00
/* Get clkctrl clock base name based on clkctrl_name or dts node */
static const char * __init clkctrl_get_clock_name ( struct device_node * np ,
const char * clkctrl_name ,
int offset , int index ,
bool legacy_naming )
{
char * clock_name ;
/* l4per-clkctrl:1234:0 style naming based on clkctrl_name */
if ( clkctrl_name & & ! legacy_naming ) {
clock_name = kasprintf ( GFP_KERNEL , " %s-clkctrl:%04x:%d " ,
clkctrl_name , offset , index ) ;
strreplace ( clock_name , ' _ ' , ' - ' ) ;
return clock_name ;
}
/* l4per:1234:0 old style naming based on clkctrl_name */
if ( clkctrl_name )
return kasprintf ( GFP_KERNEL , " %s_cm:clk:%04x:%d " ,
clkctrl_name , offset , index ) ;
/* l4per_cm:1234:0 old style naming based on parent node name */
if ( legacy_naming )
return kasprintf ( GFP_KERNEL , " %pOFn:clk:%04x:%d " ,
np - > parent , offset , index ) ;
/* l4per-clkctrl:1234:0 style naming based on node name */
return kasprintf ( GFP_KERNEL , " %pOFn:%04x:%d " , np , offset , index ) ;
}
2017-02-09 11:27:11 +02:00
static int __init
_ti_clkctrl_clk_register ( struct omap_clkctrl_provider * provider ,
struct device_node * node , struct clk_hw * clk_hw ,
u16 offset , u8 bit , const char * const * parents ,
2020-04-30 11:34:51 +03:00
int num_parents , const struct clk_ops * ops ,
const char * clkctrl_name )
2017-02-09 11:27:11 +02:00
{
struct clk_init_data init = { NULL } ;
struct clk * clk ;
struct omap_clkctrl_clk * clkctrl_clk ;
int ret = 0 ;
2020-04-30 11:34:51 +03:00
init . name = clkctrl_get_clock_name ( node , clkctrl_name , offset , bit ,
ti_clk_get_features ( ) - > flags &
TI_CLK_CLKCTRL_COMPAT ) ;
2017-02-09 11:27:11 +02:00
clkctrl_clk = kzalloc ( sizeof ( * clkctrl_clk ) , GFP_KERNEL ) ;
if ( ! init . name | | ! clkctrl_clk ) {
ret = - ENOMEM ;
goto cleanup ;
}
clk_hw - > init = & init ;
init . parent_names = parents ;
init . num_parents = num_parents ;
init . ops = ops ;
2019-01-15 11:15:14 +02:00
init . flags = 0 ;
2017-02-09 11:27:11 +02:00
clk = ti_clk_register ( NULL , clk_hw , init . name ) ;
if ( IS_ERR_OR_NULL ( clk ) ) {
ret = - EINVAL ;
goto cleanup ;
}
clkctrl_clk - > reg_offset = offset ;
clkctrl_clk - > bit_offset = bit ;
clkctrl_clk - > clk = clk_hw ;
list_add ( & clkctrl_clk - > node , & provider - > clocks ) ;
return 0 ;
cleanup :
kfree ( init . name ) ;
kfree ( clkctrl_clk ) ;
return ret ;
}
static void __init
_ti_clkctrl_setup_gate ( struct omap_clkctrl_provider * provider ,
struct device_node * node , u16 offset ,
const struct omap_clkctrl_bit_data * data ,
2020-04-30 11:34:51 +03:00
void __iomem * reg , const char * clkctrl_name )
2017-02-09 11:27:11 +02:00
{
struct clk_hw_omap * clk_hw ;
clk_hw = kzalloc ( sizeof ( * clk_hw ) , GFP_KERNEL ) ;
if ( ! clk_hw )
return ;
clk_hw - > enable_bit = data - > bit ;
clk_hw - > enable_reg . ptr = reg ;
if ( _ti_clkctrl_clk_register ( provider , node , & clk_hw - > hw , offset ,
data - > bit , data - > parents , 1 ,
2020-04-30 11:34:51 +03:00
& omap_gate_clk_ops , clkctrl_name ) )
2017-02-09 11:27:11 +02:00
kfree ( clk_hw ) ;
}
static void __init
_ti_clkctrl_setup_mux ( struct omap_clkctrl_provider * provider ,
struct device_node * node , u16 offset ,
const struct omap_clkctrl_bit_data * data ,
2020-04-30 11:34:51 +03:00
void __iomem * reg , const char * clkctrl_name )
2017-02-09 11:27:11 +02:00
{
struct clk_omap_mux * mux ;
int num_parents = 0 ;
const char * const * pname ;
mux = kzalloc ( sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux )
return ;
pname = data - > parents ;
while ( * pname ) {
num_parents + + ;
pname + + ;
}
mux - > mask = num_parents ;
2017-10-11 17:56:10 +03:00
if ( ! ( mux - > flags & CLK_MUX_INDEX_ONE ) )
mux - > mask - - ;
2017-02-09 11:27:11 +02:00
mux - > mask = ( 1 < < fls ( mux - > mask ) ) - 1 ;
mux - > shift = data - > bit ;
mux - > reg . ptr = reg ;
if ( _ti_clkctrl_clk_register ( provider , node , & mux - > hw , offset ,
data - > bit , data - > parents , num_parents ,
2020-04-30 11:34:51 +03:00
& ti_clk_mux_ops , clkctrl_name ) )
2017-02-09 11:27:11 +02:00
kfree ( mux ) ;
}
static void __init
_ti_clkctrl_setup_div ( struct omap_clkctrl_provider * provider ,
struct device_node * node , u16 offset ,
const struct omap_clkctrl_bit_data * data ,
2020-04-30 11:34:51 +03:00
void __iomem * reg , const char * clkctrl_name )
2017-02-09 11:27:11 +02:00
{
struct clk_omap_divider * div ;
const struct omap_clkctrl_div_data * div_data = data - > data ;
2017-10-11 17:56:10 +03:00
u8 div_flags = 0 ;
2017-02-09 11:27:11 +02:00
div = kzalloc ( sizeof ( * div ) , GFP_KERNEL ) ;
if ( ! div )
return ;
div - > reg . ptr = reg ;
div - > shift = data - > bit ;
2017-10-11 17:56:10 +03:00
div - > flags = div_data - > flags ;
if ( div - > flags & CLK_DIVIDER_POWER_OF_TWO )
div_flags | = CLKF_INDEX_POWER_OF_TWO ;
2017-02-09 11:27:11 +02:00
2017-10-11 17:56:10 +03:00
if ( ti_clk_parse_divider_data ( ( int * ) div_data - > dividers , 0 ,
div_data - > max_div , div_flags ,
2019-10-02 15:06:09 +03:00
div ) ) {
2017-09-18 21:01:26 +03:00
pr_err ( " %s: Data parsing for %pOF:%04x:%d failed \n " , __func__ ,
node , offset , data - > bit ) ;
2017-02-09 11:27:11 +02:00
kfree ( div ) ;
return ;
}
if ( _ti_clkctrl_clk_register ( provider , node , & div - > hw , offset ,
data - > bit , data - > parents , 1 ,
2020-04-30 11:34:51 +03:00
& ti_clk_divider_ops , clkctrl_name ) )
2017-02-09 11:27:11 +02:00
kfree ( div ) ;
}
static void __init
_ti_clkctrl_setup_subclks ( struct omap_clkctrl_provider * provider ,
struct device_node * node ,
const struct omap_clkctrl_reg_data * data ,
2020-04-30 11:34:51 +03:00
void __iomem * reg , const char * clkctrl_name )
2017-02-09 11:27:11 +02:00
{
const struct omap_clkctrl_bit_data * bits = data - > bit_data ;
if ( ! bits )
return ;
while ( bits - > bit ) {
switch ( bits - > type ) {
case TI_CLK_GATE :
_ti_clkctrl_setup_gate ( provider , node , data - > offset ,
2020-04-30 11:34:51 +03:00
bits , reg , clkctrl_name ) ;
2017-02-09 11:27:11 +02:00
break ;
case TI_CLK_DIVIDER :
_ti_clkctrl_setup_div ( provider , node , data - > offset ,
2020-04-30 11:34:51 +03:00
bits , reg , clkctrl_name ) ;
2017-02-09 11:27:11 +02:00
break ;
case TI_CLK_MUX :
_ti_clkctrl_setup_mux ( provider , node , data - > offset ,
2020-04-30 11:34:51 +03:00
bits , reg , clkctrl_name ) ;
2017-02-09 11:27:11 +02:00
break ;
default :
pr_err ( " %s: bad subclk type: %d \n " , __func__ ,
bits - > type ) ;
return ;
}
bits + + ;
}
}
2017-10-12 10:55:29 +03:00
static void __init _clkctrl_add_provider ( void * data ,
struct device_node * np )
{
of_clk_add_hw_provider ( np , _ti_omap4_clkctrl_xlate , data ) ;
}
2019-12-10 09:21:04 -08:00
/* Get clock name based on compatible string for clkctrl */
static char * __init clkctrl_get_name ( struct device_node * np )
{
struct property * prop ;
const int prefix_len = 11 ;
const char * compat ;
char * name ;
of_property_for_each_string ( np , " compatible " , prop , compat ) {
if ( ! strncmp ( " ti,clkctrl- " , compat , prefix_len ) ) {
/* Two letter minimum name length for l3, l4 etc */
if ( strnlen ( compat + prefix_len , 16 ) < 2 )
continue ;
name = kasprintf ( GFP_KERNEL , " %s " , compat + prefix_len ) ;
if ( ! name )
continue ;
strreplace ( name , ' - ' , ' _ ' ) ;
return name ;
}
}
return NULL ;
}
2017-02-09 11:27:11 +02:00
static void __init _ti_omap4_clkctrl_setup ( struct device_node * node )
{
struct omap_clkctrl_provider * provider ;
const struct omap_clkctrl_data * data = default_clkctrl_data ;
const struct omap_clkctrl_reg_data * reg_data ;
struct clk_init_data init = { NULL } ;
struct clk_hw_omap * hw ;
struct clk * clk ;
2019-12-10 09:21:04 -08:00
struct omap_clkctrl_clk * clkctrl_clk = NULL ;
2017-02-09 11:27:11 +02:00
const __be32 * addrp ;
2019-12-10 09:21:04 -08:00
bool legacy_naming ;
char * clkctrl_name ;
2017-02-09 11:27:11 +02:00
u32 addr ;
2017-10-12 10:55:29 +03:00
int ret ;
2018-08-30 09:58:31 +03:00
char * c ;
2019-04-04 11:11:04 +03:00
u16 soc_mask = 0 ;
2017-02-09 11:27:11 +02:00
2018-08-10 11:29:09 +03:00
if ( ! ( ti_clk_get_features ( ) - > flags & TI_CLK_CLKCTRL_COMPAT ) & &
2018-12-05 13:50:21 -06:00
of_node_name_eq ( node , " clk " ) )
2018-08-10 11:29:09 +03:00
ti_clk_features . flags | = TI_CLK_CLKCTRL_COMPAT ;
2017-02-09 11:27:11 +02:00
addrp = of_get_address ( node , 0 , NULL , NULL ) ;
addr = ( u32 ) of_translate_address ( node , addrp ) ;
2017-02-09 11:34:23 +02:00
# ifdef CONFIG_ARCH_OMAP4
if ( of_machine_is_compatible ( " ti,omap4 " ) )
data = omap4_clkctrl_data ;
# endif
2017-07-31 12:16:24 +03:00
# ifdef CONFIG_SOC_OMAP5
if ( of_machine_is_compatible ( " ti,omap5 " ) )
data = omap5_clkctrl_data ;
# endif
2017-08-04 17:25:08 +03:00
# ifdef CONFIG_SOC_DRA7XX
2018-08-13 14:30:49 +03:00
if ( of_machine_is_compatible ( " ti,dra7 " ) ) {
if ( ti_clk_get_features ( ) - > flags & TI_CLK_CLKCTRL_COMPAT )
data = dra7_clkctrl_compat_data ;
else
data = dra7_clkctrl_data ;
}
2019-04-04 11:11:04 +03:00
if ( of_machine_is_compatible ( " ti,dra72 " ) )
soc_mask = CLKF_SOC_DRA72 ;
if ( of_machine_is_compatible ( " ti,dra74 " ) )
soc_mask = CLKF_SOC_DRA74 ;
if ( of_machine_is_compatible ( " ti,dra76 " ) )
soc_mask = CLKF_SOC_DRA76 ;
2017-08-04 17:25:08 +03:00
# endif
2017-08-09 11:59:29 +03:00
# ifdef CONFIG_SOC_AM33XX
2018-08-10 18:35:03 +03:00
if ( of_machine_is_compatible ( " ti,am33xx " ) ) {
if ( ti_clk_get_features ( ) - > flags & TI_CLK_CLKCTRL_COMPAT )
data = am3_clkctrl_compat_data ;
else
data = am3_clkctrl_data ;
}
2017-08-09 11:59:29 +03:00
# endif
2017-08-10 16:11:06 +03:00
# ifdef CONFIG_SOC_AM43XX
2018-08-13 10:48:52 +03:00
if ( of_machine_is_compatible ( " ti,am4372 " ) ) {
if ( ti_clk_get_features ( ) - > flags & TI_CLK_CLKCTRL_COMPAT )
data = am4_clkctrl_compat_data ;
else
data = am4_clkctrl_data ;
}
if ( of_machine_is_compatible ( " ti,am438x " ) ) {
if ( ti_clk_get_features ( ) - > flags & TI_CLK_CLKCTRL_COMPAT )
data = am438x_clkctrl_compat_data ;
else
data = am438x_clkctrl_data ;
}
2017-08-10 16:11:06 +03:00
# endif
2017-08-24 15:41:37 +03:00
# ifdef CONFIG_SOC_TI81XX
if ( of_machine_is_compatible ( " ti,dm814 " ) )
data = dm814_clkctrl_data ;
2017-08-24 15:44:14 +03:00
if ( of_machine_is_compatible ( " ti,dm816 " ) )
data = dm816_clkctrl_data ;
2017-08-24 15:41:37 +03:00
# endif
2017-02-09 11:34:23 +02:00
2019-04-04 11:11:05 +03:00
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DEVICE_TYPE_GP )
soc_mask | = CLKF_SOC_NONSEC ;
2017-02-09 11:27:11 +02:00
while ( data - > addr ) {
if ( addr = = data - > addr )
break ;
data + + ;
}
if ( ! data - > addr ) {
2017-09-18 21:01:26 +03:00
pr_err ( " %pOF not found from clkctrl data. \n " , node ) ;
2017-02-09 11:27:11 +02:00
return ;
}
provider = kzalloc ( sizeof ( * provider ) , GFP_KERNEL ) ;
if ( ! provider )
return ;
provider - > base = of_iomap ( node , 0 ) ;
2019-12-10 09:21:04 -08:00
legacy_naming = ti_clk_get_features ( ) - > flags & TI_CLK_CLKCTRL_COMPAT ;
clkctrl_name = clkctrl_get_name ( node ) ;
if ( clkctrl_name ) {
provider - > clkdm_name = kasprintf ( GFP_KERNEL ,
" %s_clkdm " , clkctrl_name ) ;
goto clkdm_found ;
}
/*
* The code below can be removed when all clkctrl nodes use domain
* specific compatible proprerty and standard clock node naming
*/
if ( legacy_naming ) {
2018-10-15 16:38:33 -07:00
provider - > clkdm_name = kasprintf ( GFP_KERNEL , " %pOFnxxx " , node - > parent ) ;
2018-08-10 11:29:09 +03:00
if ( ! provider - > clkdm_name ) {
kfree ( provider ) ;
return ;
}
/*
* Create default clkdm name , replace _cm from end of parent
* node name with _clkdm
*/
2019-03-07 12:10:56 -08:00
provider - > clkdm_name [ strlen ( provider - > clkdm_name ) - 2 ] = 0 ;
2018-08-10 11:29:09 +03:00
} else {
2018-10-15 16:38:33 -07:00
provider - > clkdm_name = kasprintf ( GFP_KERNEL , " %pOFn " , node ) ;
2018-08-10 11:29:09 +03:00
if ( ! provider - > clkdm_name ) {
kfree ( provider ) ;
return ;
}
/*
* Create default clkdm name , replace _clkctrl from end of
* node name with _clkdm
*/
provider - > clkdm_name [ strlen ( provider - > clkdm_name ) - 7 ] = 0 ;
2017-09-18 21:02:24 +03:00
}
strcat ( provider - > clkdm_name , " clkdm " ) ;
2018-08-30 09:58:31 +03:00
/* Replace any dash from the clkdm name with underscore */
c = provider - > clkdm_name ;
while ( * c ) {
if ( * c = = ' - ' )
* c = ' _ ' ;
c + + ;
}
2019-12-10 09:21:04 -08:00
clkdm_found :
2017-02-09 11:27:11 +02:00
INIT_LIST_HEAD ( & provider - > clocks ) ;
/* Generate clocks */
reg_data = data - > regs ;
while ( reg_data - > parent ) {
2019-04-04 11:11:04 +03:00
if ( ( reg_data - > flags & CLKF_SOC_MASK ) & &
( reg_data - > flags & soc_mask ) = = 0 ) {
reg_data + + ;
continue ;
}
2017-02-09 11:27:11 +02:00
hw = kzalloc ( sizeof ( * hw ) , GFP_KERNEL ) ;
if ( ! hw )
return ;
hw - > enable_reg . ptr = provider - > base + reg_data - > offset ;
_ti_clkctrl_setup_subclks ( provider , node , reg_data ,
2020-04-30 11:34:51 +03:00
hw - > enable_reg . ptr , clkctrl_name ) ;
2017-02-09 11:27:11 +02:00
if ( reg_data - > flags & CLKF_SW_SUP )
hw - > enable_bit = MODULEMODE_SWCTRL ;
if ( reg_data - > flags & CLKF_HW_SUP )
hw - > enable_bit = MODULEMODE_HWCTRL ;
if ( reg_data - > flags & CLKF_NO_IDLEST )
2019-09-12 16:26:05 +03:00
set_bit ( NO_IDLEST , & hw - > flags ) ;
2017-02-09 11:27:11 +02:00
2017-09-18 21:02:24 +03:00
if ( reg_data - > clkdm_name )
hw - > clkdm_name = reg_data - > clkdm_name ;
else
hw - > clkdm_name = provider - > clkdm_name ;
2017-02-09 11:27:11 +02:00
init . parent_names = & reg_data - > parent ;
init . num_parents = 1 ;
init . flags = 0 ;
2018-02-23 14:28:21 +02:00
if ( reg_data - > flags & CLKF_SET_RATE_PARENT )
init . flags | = CLK_SET_RATE_PARENT ;
2019-12-10 09:21:04 -08:00
init . name = clkctrl_get_clock_name ( node , clkctrl_name ,
reg_data - > offset , 0 ,
legacy_naming ) ;
if ( ! init . name )
goto cleanup ;
2017-02-09 11:27:11 +02:00
clkctrl_clk = kzalloc ( sizeof ( * clkctrl_clk ) , GFP_KERNEL ) ;
2019-12-10 09:21:04 -08:00
if ( ! clkctrl_clk )
2017-02-09 11:27:11 +02:00
goto cleanup ;
init . ops = & omap4_clkctrl_clk_ops ;
hw - > hw . init = & init ;
2019-09-12 16:26:04 +03:00
clk = ti_clk_register_omap_hw ( NULL , & hw - > hw , init . name ) ;
2017-02-09 11:27:11 +02:00
if ( IS_ERR_OR_NULL ( clk ) )
goto cleanup ;
clkctrl_clk - > reg_offset = reg_data - > offset ;
clkctrl_clk - > clk = & hw - > hw ;
list_add ( & clkctrl_clk - > node , & provider - > clocks ) ;
reg_data + + ;
}
2017-10-12 10:55:29 +03:00
ret = of_clk_add_hw_provider ( node , _ti_omap4_clkctrl_xlate , provider ) ;
if ( ret = = - EPROBE_DEFER )
ti_clk_retry_init ( node , provider , _clkctrl_add_provider ) ;
2019-12-10 09:21:04 -08:00
kfree ( clkctrl_name ) ;
2017-02-09 11:27:11 +02:00
return ;
cleanup :
kfree ( hw ) ;
kfree ( init . name ) ;
2019-12-10 09:21:04 -08:00
kfree ( clkctrl_name ) ;
2017-02-09 11:27:11 +02:00
kfree ( clkctrl_clk ) ;
}
CLK_OF_DECLARE ( ti_omap4_clkctrl_clock , " ti,clkctrl " ,
_ti_omap4_clkctrl_setup ) ;
2019-09-12 16:26:06 +03:00
/**
* ti_clk_is_in_standby - Check if clkctrl clock is in standby or not
* @ clk : clock to check standby status for
*
* Finds whether the provided clock is in standby mode or not . Returns
* true if the provided clock is a clkctrl type clock and it is in standby ,
* false otherwise .
*/
bool ti_clk_is_in_standby ( struct clk * clk )
{
struct clk_hw * hw ;
struct clk_hw_omap * hwclk ;
u32 val ;
hw = __clk_get_hw ( clk ) ;
if ( ! omap2_clk_is_hw_omap ( hw ) )
return false ;
hwclk = to_clk_hw_omap ( hw ) ;
val = ti_clk_ll_ops - > clk_readl ( & hwclk - > enable_reg ) ;
if ( val & OMAP4_STBYST_MASK )
return true ;
return false ;
}
EXPORT_SYMBOL_GPL ( ti_clk_is_in_standby ) ;