2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-04-23 11:35:39 +03:00
/*
* Copyright ( c ) 2014 MediaTek Inc .
* Author : James Liao < jamesjj . liao @ mediatek . com >
*/
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/clkdev.h>
2022-02-08 15:40:06 +03:00
# include <linux/clk-provider.h>
# include <linux/container_of.h>
2022-02-08 15:40:05 +03:00
# include <linux/mfd/syscon.h>
2021-09-02 01:25:25 +03:00
# include <linux/module.h>
2022-02-08 15:40:06 +03:00
# include <linux/regmap.h>
2015-04-23 11:35:39 +03:00
# include "clk-gate.h"
2022-02-08 15:40:06 +03:00
struct mtk_clk_gate {
struct clk_hw hw ;
struct regmap * regmap ;
int set_ofs ;
int clr_ofs ;
int sta_ofs ;
u8 bit ;
} ;
static inline struct mtk_clk_gate * to_mtk_clk_gate ( struct clk_hw * hw )
{
return container_of ( hw , struct mtk_clk_gate , hw ) ;
}
2022-01-03 17:37:11 +03:00
static u32 mtk_get_clockgating ( struct clk_hw * hw )
2015-04-23 11:35:39 +03:00
{
2016-01-08 18:51:46 +03:00
struct mtk_clk_gate * cg = to_mtk_clk_gate ( hw ) ;
2015-04-23 11:35:39 +03:00
u32 val ;
regmap_read ( cg - > regmap , cg - > sta_ofs , & val ) ;
2022-01-03 17:37:11 +03:00
return val & BIT ( cg - > bit ) ;
}
2015-04-23 11:35:39 +03:00
2022-01-03 17:37:11 +03:00
static int mtk_cg_bit_is_cleared ( struct clk_hw * hw )
{
return mtk_get_clockgating ( hw ) = = 0 ;
2015-04-23 11:35:39 +03:00
}
static int mtk_cg_bit_is_set ( struct clk_hw * hw )
{
2022-01-03 17:37:11 +03:00
return mtk_get_clockgating ( hw ) ! = 0 ;
2015-04-23 11:35:39 +03:00
}
static void mtk_cg_set_bit ( struct clk_hw * hw )
{
2016-01-08 18:51:46 +03:00
struct mtk_clk_gate * cg = to_mtk_clk_gate ( hw ) ;
2015-04-23 11:35:39 +03:00
regmap_write ( cg - > regmap , cg - > set_ofs , BIT ( cg - > bit ) ) ;
}
static void mtk_cg_clr_bit ( struct clk_hw * hw )
{
2016-01-08 18:51:46 +03:00
struct mtk_clk_gate * cg = to_mtk_clk_gate ( hw ) ;
2015-04-23 11:35:39 +03:00
regmap_write ( cg - > regmap , cg - > clr_ofs , BIT ( cg - > bit ) ) ;
}
2016-11-04 10:43:05 +03:00
static void mtk_cg_set_bit_no_setclr ( struct clk_hw * hw )
{
struct mtk_clk_gate * cg = to_mtk_clk_gate ( hw ) ;
2022-01-03 17:37:12 +03:00
regmap_set_bits ( cg - > regmap , cg - > sta_ofs , BIT ( cg - > bit ) ) ;
2016-11-04 10:43:05 +03:00
}
static void mtk_cg_clr_bit_no_setclr ( struct clk_hw * hw )
{
struct mtk_clk_gate * cg = to_mtk_clk_gate ( hw ) ;
2022-01-03 17:37:12 +03:00
regmap_clear_bits ( cg - > regmap , cg - > sta_ofs , BIT ( cg - > bit ) ) ;
2016-11-04 10:43:05 +03:00
}
2015-04-23 11:35:39 +03:00
static int mtk_cg_enable ( struct clk_hw * hw )
{
mtk_cg_clr_bit ( hw ) ;
return 0 ;
}
static void mtk_cg_disable ( struct clk_hw * hw )
{
mtk_cg_set_bit ( hw ) ;
}
static int mtk_cg_enable_inv ( struct clk_hw * hw )
{
mtk_cg_set_bit ( hw ) ;
return 0 ;
}
static void mtk_cg_disable_inv ( struct clk_hw * hw )
{
mtk_cg_clr_bit ( hw ) ;
}
2016-11-04 10:43:05 +03:00
static int mtk_cg_enable_no_setclr ( struct clk_hw * hw )
{
mtk_cg_clr_bit_no_setclr ( hw ) ;
return 0 ;
}
static void mtk_cg_disable_no_setclr ( struct clk_hw * hw )
{
mtk_cg_set_bit_no_setclr ( hw ) ;
}
static int mtk_cg_enable_inv_no_setclr ( struct clk_hw * hw )
{
mtk_cg_set_bit_no_setclr ( hw ) ;
return 0 ;
}
static void mtk_cg_disable_inv_no_setclr ( struct clk_hw * hw )
{
mtk_cg_clr_bit_no_setclr ( hw ) ;
}
2015-04-23 11:35:39 +03:00
const struct clk_ops mtk_clk_gate_ops_setclr = {
. is_enabled = mtk_cg_bit_is_cleared ,
. enable = mtk_cg_enable ,
. disable = mtk_cg_disable ,
} ;
2021-09-02 01:25:25 +03:00
EXPORT_SYMBOL_GPL ( mtk_clk_gate_ops_setclr ) ;
2015-04-23 11:35:39 +03:00
const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
. is_enabled = mtk_cg_bit_is_set ,
. enable = mtk_cg_enable_inv ,
. disable = mtk_cg_disable_inv ,
} ;
2021-09-02 01:25:25 +03:00
EXPORT_SYMBOL_GPL ( mtk_clk_gate_ops_setclr_inv ) ;
2015-04-23 11:35:39 +03:00
2016-11-04 10:43:05 +03:00
const struct clk_ops mtk_clk_gate_ops_no_setclr = {
. is_enabled = mtk_cg_bit_is_cleared ,
. enable = mtk_cg_enable_no_setclr ,
. disable = mtk_cg_disable_no_setclr ,
} ;
2021-09-02 01:25:25 +03:00
EXPORT_SYMBOL_GPL ( mtk_clk_gate_ops_no_setclr ) ;
2016-11-04 10:43:05 +03:00
const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
. is_enabled = mtk_cg_bit_is_set ,
. enable = mtk_cg_enable_inv_no_setclr ,
. disable = mtk_cg_disable_inv_no_setclr ,
} ;
2021-09-02 01:25:25 +03:00
EXPORT_SYMBOL_GPL ( mtk_clk_gate_ops_no_setclr_inv ) ;
2016-11-04 10:43:05 +03:00
2022-02-08 15:40:06 +03:00
static struct clk * mtk_clk_register_gate ( const char * name ,
const char * parent_name ,
struct regmap * regmap , int set_ofs ,
int clr_ofs , int sta_ofs , u8 bit ,
const struct clk_ops * ops ,
unsigned long flags , struct device * dev )
2015-04-23 11:35:39 +03:00
{
struct mtk_clk_gate * cg ;
struct clk * clk ;
2015-05-18 17:00:26 +03:00
struct clk_init_data init = { } ;
2015-04-23 11:35:39 +03:00
cg = kzalloc ( sizeof ( * cg ) , GFP_KERNEL ) ;
if ( ! cg )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
2019-04-12 06:30:27 +03:00
init . flags = flags | CLK_SET_RATE_PARENT ;
2015-04-23 11:35:39 +03:00
init . parent_names = parent_name ? & parent_name : NULL ;
init . num_parents = parent_name ? 1 : 0 ;
init . ops = ops ;
cg - > regmap = regmap ;
cg - > set_ofs = set_ofs ;
cg - > clr_ofs = clr_ofs ;
cg - > sta_ofs = sta_ofs ;
cg - > bit = bit ;
cg - > hw . init = & init ;
2019-09-02 12:00:57 +03:00
clk = clk_register ( dev , & cg - > hw ) ;
2015-04-23 11:35:39 +03:00
if ( IS_ERR ( clk ) )
kfree ( cg ) ;
return clk ;
}
2021-09-02 01:25:25 +03:00
2022-02-08 15:40:05 +03:00
int mtk_clk_register_gates_with_dev ( struct device_node * node ,
const struct mtk_gate * clks , int num ,
struct clk_onecell_data * clk_data ,
struct device * dev )
{
int i ;
struct clk * clk ;
struct regmap * regmap ;
if ( ! clk_data )
return - ENOMEM ;
regmap = device_node_to_regmap ( node ) ;
if ( IS_ERR ( regmap ) ) {
pr_err ( " Cannot find regmap for %pOF: %pe \n " , node , regmap ) ;
return PTR_ERR ( regmap ) ;
}
for ( i = 0 ; i < num ; i + + ) {
const struct mtk_gate * gate = & clks [ i ] ;
if ( ! IS_ERR_OR_NULL ( clk_data - > clks [ gate - > id ] ) )
continue ;
clk = mtk_clk_register_gate ( gate - > name , gate - > parent_name ,
regmap ,
gate - > regs - > set_ofs ,
gate - > regs - > clr_ofs ,
gate - > regs - > sta_ofs ,
gate - > shift , gate - > ops ,
gate - > flags , dev ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " Failed to register clk %s: %pe \n " , gate - > name , clk ) ;
continue ;
}
clk_data - > clks [ gate - > id ] = clk ;
}
return 0 ;
}
int mtk_clk_register_gates ( struct device_node * node ,
const struct mtk_gate * clks , int num ,
struct clk_onecell_data * clk_data )
{
return mtk_clk_register_gates_with_dev ( node , clks , num , clk_data , NULL ) ;
}
EXPORT_SYMBOL_GPL ( mtk_clk_register_gates ) ;
2021-09-02 01:25:25 +03:00
MODULE_LICENSE ( " GPL " ) ;