2018-07-16 08:54:32 +03:00
// SPDX-License-Identifier: GPL-2.0
2014-01-15 22:47:26 +04:00
/*
* Copyright ( c ) 2013 , The Linux Foundation . All rights reserved .
*/
# include <linux/kernel.h>
# include <linux/bitops.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/export.h>
# include <linux/clk-provider.h>
# include <linux/regmap.h>
# include "clk-branch.h"
static bool clk_branch_in_hwcg_mode ( const struct clk_branch * br )
{
u32 val ;
if ( ! br - > hwcg_reg )
2018-08-05 03:22:22 +03:00
return false ;
2014-01-15 22:47:26 +04:00
regmap_read ( br - > clkr . regmap , br - > hwcg_reg , & val ) ;
return ! ! ( val & BIT ( br - > hwcg_bit ) ) ;
}
static bool clk_branch_check_halt ( const struct clk_branch * br , bool enabling )
{
bool invert = ( br - > halt_check = = BRANCH_HALT_ENABLE ) ;
u32 val ;
regmap_read ( br - > clkr . regmap , br - > halt_reg , & val ) ;
val & = BIT ( br - > halt_bit ) ;
if ( invert )
val = ! val ;
return ! ! val = = ! enabling ;
}
static bool clk_branch2_check_halt ( const struct clk_branch * br , bool enabling )
{
u32 val ;
u32 mask ;
2023-05-12 15:23:43 +03:00
bool invert = ( br - > halt_check = = BRANCH_HALT_ENABLE ) ;
2014-01-15 22:47:26 +04:00
2023-02-08 12:13:34 +03:00
mask = CBCR_NOC_FSM_STATUS ;
mask | = CBCR_CLK_OFF ;
2014-01-15 22:47:26 +04:00
regmap_read ( br - > clkr . regmap , br - > halt_reg , & val ) ;
if ( enabling ) {
val & = mask ;
2023-05-12 15:23:43 +03:00
return ( val & CBCR_CLK_OFF ) = = ( invert ? CBCR_CLK_OFF : 0 ) | |
2023-02-08 12:13:34 +03:00
FIELD_GET ( CBCR_NOC_FSM_STATUS , val ) = = FSM_STATUS_ON ;
2014-01-15 22:47:26 +04:00
}
2023-05-12 15:23:43 +03:00
return ( val & CBCR_CLK_OFF ) = = ( invert ? 0 : CBCR_CLK_OFF ) ;
2014-01-15 22:47:26 +04:00
}
static int clk_branch_wait ( const struct clk_branch * br , bool enabling ,
bool ( check_halt ) ( const struct clk_branch * , bool ) )
{
bool voted = br - > halt_check & BRANCH_VOTED ;
2015-08-12 21:42:23 +03:00
const char * name = clk_hw_get_name ( & br - > clkr . hw ) ;
2014-01-15 22:47:26 +04:00
2018-04-30 19:20:09 +03:00
/*
* Skip checking halt bit if we ' re explicitly ignoring the bit or the
* clock is in hardware gated mode
*/
if ( br - > halt_check = = BRANCH_HALT_SKIP | | clk_branch_in_hwcg_mode ( br ) )
2014-01-15 22:47:26 +04:00
return 0 ;
if ( br - > halt_check = = BRANCH_HALT_DELAY | | ( ! enabling & & voted ) ) {
udelay ( 10 ) ;
} else if ( br - > halt_check = = BRANCH_HALT_ENABLE | |
br - > halt_check = = BRANCH_HALT | |
( enabling & & voted ) ) {
int count = 200 ;
while ( count - - > 0 ) {
if ( check_halt ( br , enabling ) )
return 0 ;
udelay ( 1 ) ;
}
WARN ( 1 , " %s status stuck at 'o%s' " , name ,
enabling ? " ff " : " n " ) ;
return - EBUSY ;
}
return 0 ;
}
static int clk_branch_toggle ( struct clk_hw * hw , bool en ,
bool ( check_halt ) ( const struct clk_branch * , bool ) )
{
struct clk_branch * br = to_clk_branch ( hw ) ;
int ret ;
if ( en ) {
ret = clk_enable_regmap ( hw ) ;
if ( ret )
return ret ;
} else {
clk_disable_regmap ( hw ) ;
}
return clk_branch_wait ( br , en , check_halt ) ;
}
static int clk_branch_enable ( struct clk_hw * hw )
{
return clk_branch_toggle ( hw , true , clk_branch_check_halt ) ;
}
static void clk_branch_disable ( struct clk_hw * hw )
{
clk_branch_toggle ( hw , false , clk_branch_check_halt ) ;
}
const struct clk_ops clk_branch_ops = {
. enable = clk_branch_enable ,
. disable = clk_branch_disable ,
. is_enabled = clk_is_enabled_regmap ,
} ;
EXPORT_SYMBOL_GPL ( clk_branch_ops ) ;
static int clk_branch2_enable ( struct clk_hw * hw )
{
return clk_branch_toggle ( hw , true , clk_branch2_check_halt ) ;
}
static void clk_branch2_disable ( struct clk_hw * hw )
{
clk_branch_toggle ( hw , false , clk_branch2_check_halt ) ;
}
const struct clk_ops clk_branch2_ops = {
. enable = clk_branch2_enable ,
. disable = clk_branch2_disable ,
. is_enabled = clk_is_enabled_regmap ,
} ;
EXPORT_SYMBOL_GPL ( clk_branch2_ops ) ;
2019-03-06 20:47:57 +03:00
const struct clk_ops clk_branch2_aon_ops = {
. enable = clk_branch2_enable ,
. is_enabled = clk_is_enabled_regmap ,
} ;
EXPORT_SYMBOL_GPL ( clk_branch2_aon_ops ) ;
2014-01-15 22:47:26 +04:00
const struct clk_ops clk_branch_simple_ops = {
. enable = clk_enable_regmap ,
. disable = clk_disable_regmap ,
. is_enabled = clk_is_enabled_regmap ,
} ;
EXPORT_SYMBOL_GPL ( clk_branch_simple_ops ) ;