2018-03-21 17:20:12 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2017 , Intel Corporation
*/
# include <linux/slab.h>
# include <linux/clk-provider.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2018-03-21 17:20:12 +03:00
# include "stratix10-clk.h"
# include "clk.h"
# define CLK_MGR_FREE_SHIFT 16
# define CLK_MGR_FREE_MASK 0x7
# define SWCTRLBTCLKSEN_SHIFT 8
# define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
2021-02-12 17:30:59 +03:00
static unsigned long n5x_clk_peri_c_clk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_periph_clk * socfpgaclk = to_periph_clk ( hwclk ) ;
unsigned long div ;
unsigned long shift = socfpgaclk - > shift ;
u32 val ;
val = readl ( socfpgaclk - > hw . reg ) ;
val & = ( 0x1f < < shift ) ;
div = ( val > > shift ) + 1 ;
return parent_rate / div ;
}
2018-03-21 17:20:12 +03:00
static unsigned long clk_peri_c_clk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_periph_clk * socfpgaclk = to_periph_clk ( hwclk ) ;
unsigned long div = 1 ;
u32 val ;
val = readl ( socfpgaclk - > hw . reg ) ;
val & = GENMASK ( SWCTRLBTCLKSEN_SHIFT - 1 , 0 ) ;
parent_rate / = val ;
return parent_rate / div ;
}
static unsigned long clk_peri_cnt_clk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_periph_clk * socfpgaclk = to_periph_clk ( hwclk ) ;
unsigned long div = 1 ;
if ( socfpgaclk - > fixed_div ) {
div = socfpgaclk - > fixed_div ;
} else {
2019-08-14 18:30:14 +03:00
if ( socfpgaclk - > hw . reg )
2018-03-21 17:20:12 +03:00
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_periph_clk ( hwclk ) ;
u32 clk_src , mask ;
u8 parent ;
if ( socfpgaclk - > bypass_reg ) {
mask = ( 0x1 < < socfpgaclk - > bypass_shift ) ;
parent = ( ( readl ( socfpgaclk - > bypass_reg ) & mask ) > >
socfpgaclk - > bypass_shift ) ;
} else {
clk_src = readl ( socfpgaclk - > hw . reg ) ;
parent = ( clk_src > > CLK_MGR_FREE_SHIFT ) &
CLK_MGR_FREE_MASK ;
}
return parent ;
}
2021-02-12 17:30:59 +03:00
static const struct clk_ops n5x_peri_c_clk_ops = {
. recalc_rate = n5x_clk_peri_c_clk_recalc_rate ,
. get_parent = clk_periclk_get_parent ,
} ;
2018-03-21 17:20:12 +03:00
static const struct clk_ops peri_c_clk_ops = {
. recalc_rate = clk_peri_c_clk_recalc_rate ,
. get_parent = clk_periclk_get_parent ,
} ;
static const struct clk_ops peri_cnt_clk_ops = {
. recalc_rate = clk_peri_cnt_clk_recalc_rate ,
. get_parent = clk_periclk_get_parent ,
} ;
2020-01-14 19:07:26 +03:00
struct clk * s10_register_periph ( const struct stratix10_perip_c_clock * clks ,
void __iomem * reg )
2018-03-21 17:20:12 +03:00
{
struct clk * clk ;
struct socfpga_periph_clk * periph_clk ;
struct clk_init_data init ;
2020-01-14 19:07:26 +03:00
const char * name = clks - > name ;
const char * parent_name = clks - > parent_name ;
2018-03-21 17:20:12 +03:00
periph_clk = kzalloc ( sizeof ( * periph_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! periph_clk ) )
return NULL ;
2020-01-14 19:07:26 +03:00
periph_clk - > hw . reg = reg + clks - > offset ;
2018-03-21 17:20:12 +03:00
init . name = name ;
init . ops = & peri_c_clk_ops ;
2020-01-14 19:07:26 +03:00
init . flags = clks - > flags ;
2018-03-21 17:20:12 +03:00
2020-01-14 19:07:26 +03:00
init . num_parents = clks - > num_parents ;
2020-05-12 21:16:43 +03:00
init . parent_names = parent_name ? & parent_name : NULL ;
if ( init . parent_names = = NULL )
init . parent_data = clks - > parent_data ;
2018-03-21 17:20:12 +03:00
periph_clk - > hw . hw . init = & init ;
clk = clk_register ( NULL , & periph_clk - > hw . hw ) ;
if ( WARN_ON ( IS_ERR ( clk ) ) ) {
kfree ( periph_clk ) ;
return NULL ;
}
return clk ;
}
2021-02-12 17:30:59 +03:00
struct clk * n5x_register_periph ( const struct n5x_perip_c_clock * clks ,
void __iomem * regbase )
{
struct clk * clk ;
struct socfpga_periph_clk * periph_clk ;
struct clk_init_data init ;
const char * name = clks - > name ;
const char * parent_name = clks - > parent_name ;
periph_clk = kzalloc ( sizeof ( * periph_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! periph_clk ) )
return NULL ;
periph_clk - > hw . reg = regbase + clks - > offset ;
periph_clk - > shift = clks - > shift ;
init . name = name ;
init . ops = & n5x_peri_c_clk_ops ;
init . flags = clks - > flags ;
init . num_parents = clks - > num_parents ;
init . parent_names = parent_name ? & parent_name : NULL ;
periph_clk - > hw . hw . init = & init ;
clk = clk_register ( NULL , & periph_clk - > hw . hw ) ;
if ( WARN_ON ( IS_ERR ( clk ) ) ) {
kfree ( periph_clk ) ;
return NULL ;
}
return clk ;
}
2020-01-14 19:07:26 +03:00
struct clk * s10_register_cnt_periph ( const struct stratix10_perip_cnt_clock * clks ,
void __iomem * regbase )
2018-03-21 17:20:12 +03:00
{
struct clk * clk ;
struct socfpga_periph_clk * periph_clk ;
struct clk_init_data init ;
2020-01-14 19:07:26 +03:00
const char * name = clks - > name ;
const char * parent_name = clks - > parent_name ;
2018-03-21 17:20:12 +03:00
periph_clk = kzalloc ( sizeof ( * periph_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! periph_clk ) )
return NULL ;
2020-01-14 19:07:26 +03:00
if ( clks - > offset )
periph_clk - > hw . reg = regbase + clks - > offset ;
2018-03-21 17:20:12 +03:00
else
periph_clk - > hw . reg = NULL ;
2020-01-14 19:07:26 +03:00
if ( clks - > bypass_reg )
periph_clk - > bypass_reg = regbase + clks - > bypass_reg ;
2018-03-21 17:20:12 +03:00
else
periph_clk - > bypass_reg = NULL ;
2020-01-14 19:07:26 +03:00
periph_clk - > bypass_shift = clks - > bypass_shift ;
periph_clk - > fixed_div = clks - > fixed_divider ;
2018-03-21 17:20:12 +03:00
init . name = name ;
init . ops = & peri_cnt_clk_ops ;
2020-01-14 19:07:26 +03:00
init . flags = clks - > flags ;
2018-03-21 17:20:12 +03:00
2020-01-14 19:07:26 +03:00
init . num_parents = clks - > num_parents ;
2020-05-12 21:16:43 +03:00
init . parent_names = parent_name ? & parent_name : NULL ;
if ( init . parent_names = = NULL )
init . parent_data = clks - > parent_data ;
2018-03-21 17:20:12 +03:00
periph_clk - > hw . hw . init = & init ;
clk = clk_register ( NULL , & periph_clk - > hw . hw ) ;
if ( WARN_ON ( IS_ERR ( clk ) ) ) {
kfree ( periph_clk ) ;
return NULL ;
}
return clk ;
}