2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-12-07 04:32:32 +03:00
/*
* Copyright ( C ) 2016 Socionext Inc .
* Author : Masahiro Yamada < yamada . masahiro @ socionext . com >
*/
# include <linux/clk-provider.h>
# include <linux/device.h>
# include <linux/regmap.h>
# include "clk-uniphier.h"
# define UNIPHIER_CLK_CPUGEAR_STAT 0 /* status */
# define UNIPHIER_CLK_CPUGEAR_SET 4 /* set */
# define UNIPHIER_CLK_CPUGEAR_UPD 8 /* update */
# define UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0)
struct uniphier_clk_cpugear {
struct clk_hw hw ;
struct regmap * regmap ;
unsigned int regbase ;
unsigned int mask ;
} ;
# define to_uniphier_clk_cpugear(_hw) \
container_of ( _hw , struct uniphier_clk_cpugear , hw )
static int uniphier_clk_cpugear_set_parent ( struct clk_hw * hw , u8 index )
{
struct uniphier_clk_cpugear * gear = to_uniphier_clk_cpugear ( hw ) ;
int ret ;
unsigned int val ;
ret = regmap_write_bits ( gear - > regmap ,
gear - > regbase + UNIPHIER_CLK_CPUGEAR_SET ,
gear - > mask , index ) ;
if ( ret )
return ret ;
ret = regmap_write_bits ( gear - > regmap ,
2019-02-08 05:25:23 +03:00
gear - > regbase + UNIPHIER_CLK_CPUGEAR_UPD ,
2016-12-07 04:32:32 +03:00
UNIPHIER_CLK_CPUGEAR_UPD_BIT ,
UNIPHIER_CLK_CPUGEAR_UPD_BIT ) ;
if ( ret )
return ret ;
return regmap_read_poll_timeout ( gear - > regmap ,
gear - > regbase + UNIPHIER_CLK_CPUGEAR_UPD ,
val , ! ( val & UNIPHIER_CLK_CPUGEAR_UPD_BIT ) ,
0 , 1 ) ;
}
static u8 uniphier_clk_cpugear_get_parent ( struct clk_hw * hw )
{
struct uniphier_clk_cpugear * gear = to_uniphier_clk_cpugear ( hw ) ;
int num_parents = clk_hw_get_num_parents ( hw ) ;
int ret ;
unsigned int val ;
ret = regmap_read ( gear - > regmap ,
gear - > regbase + UNIPHIER_CLK_CPUGEAR_STAT , & val ) ;
if ( ret )
return ret ;
val & = gear - > mask ;
return val < num_parents ? val : - EINVAL ;
}
static const struct clk_ops uniphier_clk_cpugear_ops = {
. determine_rate = __clk_mux_determine_rate ,
. set_parent = uniphier_clk_cpugear_set_parent ,
. get_parent = uniphier_clk_cpugear_get_parent ,
} ;
struct clk_hw * uniphier_clk_register_cpugear ( struct device * dev ,
struct regmap * regmap ,
const char * name ,
const struct uniphier_clk_cpugear_data * data )
{
struct uniphier_clk_cpugear * gear ;
struct clk_init_data init ;
int ret ;
gear = devm_kzalloc ( dev , sizeof ( * gear ) , GFP_KERNEL ) ;
if ( ! gear )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & uniphier_clk_cpugear_ops ;
init . flags = CLK_SET_RATE_PARENT ;
init . parent_names = data - > parent_names ;
2020-09-27 22:12:11 +03:00
init . num_parents = data - > num_parents ;
2016-12-07 04:32:32 +03:00
gear - > regmap = regmap ;
gear - > regbase = data - > regbase ;
gear - > mask = data - > mask ;
gear - > hw . init = & init ;
ret = devm_clk_hw_register ( dev , & gear - > hw ) ;
if ( ret )
return ERR_PTR ( ret ) ;
return & gear - > hw ;
}