2012-07-18 16:07:18 -06:00
/*
2013-04-11 10:55:26 -05:00
* Copyright 2011 - 2012 Calxeda , Inc .
* Copyright ( C ) 2012 - 2013 Altera Corporation < www . altera . com >
2012-07-18 16:07:18 -06:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
2013-04-11 10:55:26 -05:00
* Based from clk - highbank . c
*
2012-07-18 16:07:18 -06:00
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/clk.h>
# include <linux/clkdev.h>
# include <linux/clk-provider.h>
2013-04-11 10:55:26 -05:00
# include <linux/io.h>
# include <linux/of.h>
2012-07-18 16:07:18 -06:00
2013-04-11 10:55:26 -05:00
/* Clock Manager offsets */
2013-06-05 10:02:55 -05:00
# define CLKMGR_CTRL 0x0
# define CLKMGR_BYPASS 0x4
# define CLKMGR_L4SRC 0x70
# define CLKMGR_PERPLL_SRC 0xAC
2012-07-18 16:07:18 -06:00
2013-04-11 10:55:26 -05:00
/* Clock bypass bits */
2013-06-05 10:02:55 -05:00
# define MAINPLL_BYPASS (1<<0)
# define SDRAMPLL_BYPASS (1<<1)
# define SDRAMPLL_SRC_BYPASS (1<<2)
# define PERPLL_BYPASS (1<<3)
# define PERPLL_SRC_BYPASS (1<<4)
2013-04-11 10:55:26 -05:00
# define SOCFPGA_PLL_BG_PWRDWN 0
# define SOCFPGA_PLL_EXT_ENA 1
# define SOCFPGA_PLL_PWR_DOWN 2
# define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
# define SOCFPGA_PLL_DIVF_SHIFT 3
# define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
# define SOCFPGA_PLL_DIVQ_SHIFT 16
2013-06-05 10:02:55 -05:00
# define SOCFGPA_MAX_PARENTS 3
# define SOCFPGA_L4_MP_CLK "l4_mp_clk"
# define SOCFPGA_L4_SP_CLK "l4_sp_clk"
# define SOCFPGA_NAND_CLK "nand_clk"
# define SOCFPGA_NAND_X_CLK "nand_x_clk"
# define SOCFPGA_MMC_CLK "mmc_clk"
# define SOCFPGA_DB_CLK "gpio_db_clk"
# define div_mask(width) ((1 << (width)) - 1)
# define streq(a, b) (strcmp((a), (b)) == 0)
2013-04-11 10:55:26 -05:00
extern void __iomem * clk_mgr_base_addr ;
struct socfpga_clk {
struct clk_gate hw ;
char * parent_name ;
char * clk_name ;
u32 fixed_div ;
2013-06-05 10:02:55 -05:00
void __iomem * div_reg ;
u32 width ; /* only valid if div_reg != 0 */
u32 shift ; /* only valid if div_reg != 0 */
2013-04-11 10:55:26 -05:00
} ;
# define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
static unsigned long clk_pll_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
2012-07-18 16:07:18 -06:00
{
2013-04-11 10:55:26 -05:00
struct socfpga_clk * socfpgaclk = to_socfpga_clk ( hwclk ) ;
unsigned long divf , divq , vco_freq , reg ;
unsigned long bypass ;
reg = readl ( socfpgaclk - > hw . reg ) ;
bypass = readl ( clk_mgr_base_addr + CLKMGR_BYPASS ) ;
if ( bypass & MAINPLL_BYPASS )
return parent_rate ;
divf = ( reg & SOCFPGA_PLL_DIVF_MASK ) > > SOCFPGA_PLL_DIVF_SHIFT ;
divq = ( reg & SOCFPGA_PLL_DIVQ_MASK ) > > SOCFPGA_PLL_DIVQ_SHIFT ;
vco_freq = parent_rate * ( divf + 1 ) ;
return vco_freq / ( 1 + divq ) ;
}
static struct clk_ops clk_pll_ops = {
. recalc_rate = clk_pll_recalc_rate ,
} ;
static unsigned long clk_periclk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_clk * socfpgaclk = to_socfpga_clk ( hwclk ) ;
u32 div ;
if ( socfpgaclk - > fixed_div )
div = socfpgaclk - > fixed_div ;
else
div = ( ( readl ( socfpgaclk - > hw . reg ) & 0x1ff ) + 1 ) ;
return parent_rate / div ;
}
static const struct clk_ops periclk_ops = {
. recalc_rate = clk_periclk_recalc_rate ,
} ;
static __init struct clk * socfpga_clk_init ( struct device_node * node ,
const struct clk_ops * ops )
{
u32 reg ;
2012-07-18 16:07:18 -06:00
struct clk * clk ;
2013-04-11 10:55:26 -05:00
struct socfpga_clk * socfpga_clk ;
const char * clk_name = node - > name ;
const char * parent_name ;
struct clk_init_data init ;
int rc ;
u32 fixed_div ;
rc = of_property_read_u32 ( node , " reg " , & reg ) ;
if ( WARN_ON ( rc ) )
return NULL ;
socfpga_clk = kzalloc ( sizeof ( * socfpga_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! socfpga_clk ) )
return NULL ;
socfpga_clk - > hw . reg = clk_mgr_base_addr + reg ;
rc = of_property_read_u32 ( node , " fixed-divider " , & fixed_div ) ;
if ( rc )
socfpga_clk - > fixed_div = 0 ;
else
socfpga_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 ;
parent_name = of_clk_get_parent_name ( node , 0 ) ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
2012-07-18 16:07:18 -06:00
2013-04-11 10:55:26 -05:00
socfpga_clk - > hw . hw . init = & init ;
2012-07-18 16:07:18 -06:00
2013-06-05 10:02:55 -05:00
if ( streq ( clk_name , " main_pll " ) | |
streq ( clk_name , " periph_pll " ) | |
streq ( clk_name , " sdram_pll " ) ) {
2013-04-11 10:55:26 -05:00
socfpga_clk - > hw . bit_idx = SOCFPGA_PLL_EXT_ENA ;
clk_pll_ops . enable = clk_gate_ops . enable ;
clk_pll_ops . disable = clk_gate_ops . disable ;
}
2012-07-18 16:07:18 -06:00
2013-04-11 10:55:26 -05:00
clk = clk_register ( NULL , & socfpga_clk - > hw . hw ) ;
if ( WARN_ON ( IS_ERR ( clk ) ) ) {
kfree ( socfpga_clk ) ;
return NULL ;
}
rc = of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
return clk ;
}
2012-07-18 16:07:18 -06:00
2013-06-05 10:02:55 -05:00
static u8 socfpga_clk_get_parent ( struct clk_hw * hwclk )
{
u32 l4_src ;
u32 perpll_src ;
if ( streq ( hwclk - > init - > name , SOCFPGA_L4_MP_CLK ) ) {
l4_src = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
return l4_src & = 0x1 ;
}
if ( streq ( hwclk - > init - > name , SOCFPGA_L4_SP_CLK ) ) {
l4_src = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
return ! ! ( l4_src & 2 ) ;
}
perpll_src = readl ( clk_mgr_base_addr + CLKMGR_PERPLL_SRC ) ;
if ( streq ( hwclk - > init - > name , SOCFPGA_MMC_CLK ) )
return perpll_src & = 0x3 ;
if ( streq ( hwclk - > init - > name , SOCFPGA_NAND_CLK ) | |
streq ( hwclk - > init - > name , SOCFPGA_NAND_X_CLK ) )
return ( perpll_src > > 2 ) & 3 ;
/* QSPI clock */
return ( perpll_src > > 4 ) & 3 ;
}
static int socfpga_clk_set_parent ( struct clk_hw * hwclk , u8 parent )
{
u32 src_reg ;
if ( streq ( hwclk - > init - > name , SOCFPGA_L4_MP_CLK ) ) {
src_reg = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
src_reg & = ~ 0x1 ;
src_reg | = parent ;
writel ( src_reg , clk_mgr_base_addr + CLKMGR_L4SRC ) ;
} else if ( streq ( hwclk - > init - > name , SOCFPGA_L4_SP_CLK ) ) {
src_reg = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
src_reg & = ~ 0x2 ;
src_reg | = ( parent < < 1 ) ;
writel ( src_reg , clk_mgr_base_addr + CLKMGR_L4SRC ) ;
} else {
src_reg = readl ( clk_mgr_base_addr + CLKMGR_PERPLL_SRC ) ;
if ( streq ( hwclk - > init - > name , SOCFPGA_MMC_CLK ) ) {
src_reg & = ~ 0x3 ;
src_reg | = parent ;
} else if ( streq ( hwclk - > init - > name , SOCFPGA_NAND_CLK ) | |
streq ( hwclk - > init - > name , SOCFPGA_NAND_X_CLK ) ) {
src_reg & = ~ 0xC ;
src_reg | = ( parent < < 2 ) ;
} else { /* QSPI clock */
src_reg & = ~ 0x30 ;
src_reg | = ( parent < < 4 ) ;
}
writel ( src_reg , clk_mgr_base_addr + CLKMGR_PERPLL_SRC ) ;
}
return 0 ;
}
static unsigned long socfpga_clk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_clk * socfpgaclk = to_socfpga_clk ( hwclk ) ;
u32 div = 1 , val ;
if ( socfpgaclk - > fixed_div )
div = socfpgaclk - > fixed_div ;
else if ( socfpgaclk - > div_reg ) {
val = readl ( socfpgaclk - > div_reg ) > > socfpgaclk - > shift ;
val & = div_mask ( socfpgaclk - > width ) ;
if ( streq ( hwclk - > init - > name , SOCFPGA_DB_CLK ) )
div = val + 1 ;
else
div = ( 1 < < val ) ;
}
return parent_rate / div ;
}
static struct clk_ops gateclk_ops = {
. recalc_rate = socfpga_clk_recalc_rate ,
. get_parent = socfpga_clk_get_parent ,
. set_parent = socfpga_clk_set_parent ,
} ;
static void __init socfpga_gate_clk_init ( struct device_node * node ,
const struct clk_ops * ops )
{
u32 clk_gate [ 2 ] ;
u32 div_reg [ 3 ] ;
u32 fixed_div ;
struct clk * clk ;
struct socfpga_clk * socfpga_clk ;
const char * clk_name = node - > name ;
const char * parent_name [ SOCFGPA_MAX_PARENTS ] ;
struct clk_init_data init ;
int rc ;
int i = 0 ;
socfpga_clk = kzalloc ( sizeof ( * socfpga_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! socfpga_clk ) )
return ;
rc = of_property_read_u32_array ( node , " clk-gate " , clk_gate , 2 ) ;
if ( rc )
clk_gate [ 0 ] = 0 ;
if ( clk_gate [ 0 ] ) {
socfpga_clk - > hw . reg = clk_mgr_base_addr + clk_gate [ 0 ] ;
socfpga_clk - > hw . bit_idx = clk_gate [ 1 ] ;
gateclk_ops . enable = clk_gate_ops . enable ;
gateclk_ops . disable = clk_gate_ops . disable ;
}
rc = of_property_read_u32 ( node , " fixed-divider " , & fixed_div ) ;
if ( rc )
socfpga_clk - > fixed_div = 0 ;
else
socfpga_clk - > fixed_div = fixed_div ;
rc = of_property_read_u32_array ( node , " div-reg " , div_reg , 3 ) ;
if ( ! rc ) {
socfpga_clk - > div_reg = clk_mgr_base_addr + div_reg [ 0 ] ;
socfpga_clk - > shift = div_reg [ 1 ] ;
socfpga_clk - > width = div_reg [ 2 ] ;
} else {
socfpga_clk - > div_reg = 0 ;
}
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
init . name = clk_name ;
init . ops = ops ;
init . flags = 0 ;
while ( i < SOCFGPA_MAX_PARENTS & & ( parent_name [ i ] =
of_clk_get_parent_name ( node , i ) ) ! = NULL )
i + + ;
init . parent_names = parent_name ;
init . num_parents = i ;
socfpga_clk - > hw . hw . init = & init ;
clk = clk_register ( NULL , & socfpga_clk - > hw . hw ) ;
if ( WARN_ON ( IS_ERR ( clk ) ) ) {
kfree ( socfpga_clk ) ;
return ;
}
rc = of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
if ( WARN_ON ( rc ) )
return ;
}
2013-04-11 10:55:26 -05:00
static void __init socfpga_pll_init ( struct device_node * node )
{
socfpga_clk_init ( node , & clk_pll_ops ) ;
}
CLK_OF_DECLARE ( socfpga_pll , " altr,socfpga-pll-clock " , socfpga_pll_init ) ;
2012-07-18 16:07:18 -06:00
2013-04-11 10:55:26 -05:00
static void __init socfpga_periph_init ( struct device_node * node )
{
socfpga_clk_init ( node , & periclk_ops ) ;
}
CLK_OF_DECLARE ( socfpga_periph , " altr,socfpga-perip-clk " , socfpga_periph_init ) ;
2012-07-18 16:07:18 -06:00
2013-06-05 10:02:55 -05:00
static void __init socfpga_gate_init ( struct device_node * node )
{
socfpga_gate_clk_init ( node , & gateclk_ops ) ;
}
CLK_OF_DECLARE ( socfpga_gate , " altr,socfpga-gate-clk " , socfpga_gate_init ) ;
2013-04-11 10:55:26 -05:00
void __init socfpga_init_clocks ( void )
{
struct clk * clk ;
int ret ;
2012-07-18 16:07:18 -06:00
2013-04-11 10:55:26 -05:00
clk = clk_register_fixed_factor ( NULL , " smp_twd " , " mpuclk " , 0 , 1 , 4 ) ;
ret = clk_register_clkdev ( clk , NULL , " smp_twd " ) ;
if ( ret )
pr_err ( " smp_twd alias not registered \n " ) ;
2012-07-18 16:07:18 -06:00
}