2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-05-20 06:22:42 +03:00
/*
* Copyright ( C ) 2015 Altera Corporation . All rights reserved
*/
2015-06-20 01:00:46 +03:00
# include <linux/slab.h>
2015-05-20 06:22:42 +03:00
# include <linux/clk-provider.h>
# include <linux/io.h>
# include <linux/of.h>
# include "clk.h"
# define CLK_MGR_FREE_SHIFT 16
# define CLK_MGR_FREE_MASK 0x7
# define SOCFPGA_MPU_FREE_CLK "mpu_free_clk"
# define SOCFPGA_NOC_FREE_CLK "noc_free_clk"
# define SOCFPGA_SDMMC_FREE_CLK "sdmmc_free_clk"
# define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
static unsigned long clk_periclk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_periph_clk * socfpgaclk = to_socfpga_periph_clk ( hwclk ) ;
u32 div ;
if ( socfpgaclk - > fixed_div ) {
div = socfpgaclk - > fixed_div ;
} else if ( socfpgaclk - > div_reg ) {
div = readl ( socfpgaclk - > div_reg ) > > socfpgaclk - > shift ;
2015-07-13 17:07:43 +03:00
div & = GENMASK ( socfpgaclk - > width - 1 , 0 ) ;
2015-05-20 06:22:42 +03:00
div + = 1 ;
} else {
div = ( ( readl ( socfpgaclk - > hw . reg ) & 0x7ff ) + 1 ) ;
}
return parent_rate / div ;
}
static u8 clk_periclk_get_parent ( struct clk_hw * hwclk )
{
struct socfpga_periph_clk * socfpgaclk = to_socfpga_periph_clk ( hwclk ) ;
u32 clk_src ;
2019-07-31 22:35:14 +03:00
const char * name = clk_hw_get_name ( hwclk ) ;
2015-05-20 06:22:42 +03:00
clk_src = readl ( socfpgaclk - > hw . reg ) ;
2019-07-31 22:35:14 +03:00
if ( streq ( name , SOCFPGA_MPU_FREE_CLK ) | |
streq ( name , SOCFPGA_NOC_FREE_CLK ) | |
streq ( name , SOCFPGA_SDMMC_FREE_CLK ) )
2015-05-20 06:22:42 +03:00
return ( clk_src > > CLK_MGR_FREE_SHIFT ) &
CLK_MGR_FREE_MASK ;
else
return 0 ;
}
static const struct clk_ops periclk_ops = {
. recalc_rate = clk_periclk_recalc_rate ,
. get_parent = clk_periclk_get_parent ,
} ;
2022-12-09 18:29:09 +03:00
static void __init __socfpga_periph_init ( struct device_node * node ,
const struct clk_ops * ops )
2015-05-20 06:22:42 +03:00
{
u32 reg ;
2021-03-03 00:41:50 +03:00
struct clk_hw * hw_clk ;
2015-05-20 06:22:42 +03:00
struct socfpga_periph_clk * periph_clk ;
const char * clk_name = node - > name ;
2016-02-23 00:52:46 +03:00
const char * parent_name [ SOCFPGA_MAX_PARENTS ] ;
2015-05-20 06:22:42 +03:00
struct clk_init_data init ;
int rc ;
u32 fixed_div ;
u32 div_reg [ 3 ] ;
of_property_read_u32 ( node , " reg " , & reg ) ;
periph_clk = kzalloc ( sizeof ( * periph_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! periph_clk ) )
return ;
periph_clk - > hw . reg = clk_mgr_a10_base_addr + reg ;
rc = of_property_read_u32_array ( node , " div-reg " , div_reg , 3 ) ;
if ( ! rc ) {
periph_clk - > div_reg = clk_mgr_a10_base_addr + div_reg [ 0 ] ;
periph_clk - > shift = div_reg [ 1 ] ;
periph_clk - > width = div_reg [ 2 ] ;
} else {
periph_clk - > div_reg = NULL ;
}
rc = of_property_read_u32 ( node , " fixed-divider " , & fixed_div ) ;
if ( rc )
periph_clk - > fixed_div = 0 ;
else
periph_clk - > fixed_div = fixed_div ;
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
init . name = clk_name ;
init . ops = ops ;
init . flags = 0 ;
2016-02-23 00:52:46 +03:00
init . num_parents = of_clk_parent_fill ( node , parent_name , SOCFPGA_MAX_PARENTS ) ;
init . parent_names = parent_name ;
2015-05-20 06:22:42 +03:00
periph_clk - > hw . hw . init = & init ;
2021-03-03 00:41:50 +03:00
hw_clk = & periph_clk - > hw . hw ;
2022-12-09 18:29:09 +03:00
rc = clk_hw_register ( NULL , hw_clk ) ;
if ( rc ) {
pr_err ( " Could not register clock:%s \n " , clk_name ) ;
goto err_clk_hw_register ;
2015-05-20 06:22:42 +03:00
}
2022-12-09 18:29:09 +03:00
rc = of_clk_add_hw_provider ( node , of_clk_hw_simple_get , hw_clk ) ;
if ( rc ) {
2015-05-20 06:22:42 +03:00
pr_err ( " Could not register clock provider for node:%s \n " ,
clk_name ) ;
2022-12-09 18:29:09 +03:00
goto err_of_clk_add_hw_provider ;
2015-05-20 06:22:42 +03:00
}
return ;
2022-12-09 18:29:09 +03:00
err_of_clk_add_hw_provider :
2021-03-03 00:41:50 +03:00
clk_hw_unregister ( hw_clk ) ;
2022-12-09 18:29:09 +03:00
err_clk_hw_register :
kfree ( periph_clk ) ;
2015-05-20 06:22:42 +03:00
}
void __init socfpga_a10_periph_init ( struct device_node * node )
{
__socfpga_periph_init ( node , & periclk_ops ) ;
}