2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-09-03 16:46:01 +04:00
/*
* Copyright ( c ) 2012 , 2013 , NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/io.h>
# include <linux/clk-provider.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/delay.h>
# include <linux/export.h>
# include <linux/clk/tegra.h>
# include "clk.h"
# include "clk-id.h"
# define PLLX_BASE 0xe0
# define PLLX_MISC 0xe4
# define PLLX_MISC2 0x514
# define PLLX_MISC3 0x518
# define CCLKG_BURST_POLICY 0x368
# define CCLKLP_BURST_POLICY 0x370
# define SCLK_BURST_POLICY 0x028
# define SYSTEM_CLK_RATE 0x030
2015-06-19 00:28:35 +03:00
# define SCLK_DIVIDER 0x2c
2013-09-03 16:46:01 +04:00
static DEFINE_SPINLOCK ( sysrate_lock ) ;
2015-06-19 00:28:35 +03:00
enum tegra_super_gen {
gen4 = 4 ,
gen5 ,
} ;
struct tegra_super_gen_info {
enum tegra_super_gen gen ;
const char * * sclk_parents ;
const char * * cclk_g_parents ;
const char * * cclk_lp_parents ;
int num_sclk_parents ;
int num_cclk_g_parents ;
int num_cclk_lp_parents ;
} ;
2013-09-03 16:46:01 +04:00
static const char * sclk_parents [ ] = { " clk_m " , " pll_c_out1 " , " pll_p_out4 " ,
" pll_p " , " pll_p_out2 " , " unused " ,
" clk_32k " , " pll_m_out1 " } ;
static const char * cclk_g_parents [ ] = { " clk_m " , " pll_c " , " clk_32k " , " pll_m " ,
" pll_p " , " pll_p_out4 " , " unused " ,
2015-05-13 17:58:43 +03:00
" unused " , " pll_x " , " unused " , " unused " ,
" unused " , " unused " , " unused " , " unused " ,
" dfllCPU_out " } ;
2013-09-03 16:46:01 +04:00
static const char * cclk_lp_parents [ ] = { " clk_m " , " pll_c " , " clk_32k " , " pll_m " ,
" pll_p " , " pll_p_out4 " , " unused " ,
" unused " , " pll_x " , " pll_x_out0 " } ;
2015-12-04 20:04:25 +03:00
static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = {
2015-06-19 00:28:35 +03:00
. gen = gen4 ,
. sclk_parents = sclk_parents ,
. cclk_g_parents = cclk_g_parents ,
. cclk_lp_parents = cclk_lp_parents ,
. num_sclk_parents = ARRAY_SIZE ( sclk_parents ) ,
. num_cclk_g_parents = ARRAY_SIZE ( cclk_g_parents ) ,
. num_cclk_lp_parents = ARRAY_SIZE ( cclk_lp_parents ) ,
} ;
static const char * sclk_parents_gen5 [ ] = { " clk_m " , " pll_c_out1 " , " pll_c4_out3 " ,
" pll_p " , " pll_p_out2 " , " pll_c4_out1 " ,
" clk_32k " , " pll_c4_out2 " } ;
static const char * cclk_g_parents_gen5 [ ] = { " clk_m " , " unused " , " clk_32k " , " unused " ,
" pll_p " , " pll_p_out4 " , " unused " ,
" unused " , " pll_x " , " unused " , " unused " ,
" unused " , " unused " , " unused " , " unused " ,
" dfllCPU_out " } ;
static const char * cclk_lp_parents_gen5 [ ] = { " clk_m " , " unused " , " clk_32k " , " unused " ,
" pll_p " , " pll_p_out4 " , " unused " ,
" unused " , " pll_x " , " unused " , " unused " ,
" unused " , " unused " , " unused " , " unused " ,
" dfllCPU_out " } ;
2015-12-04 20:04:25 +03:00
static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = {
2015-06-19 00:28:35 +03:00
. gen = gen5 ,
. sclk_parents = sclk_parents_gen5 ,
. cclk_g_parents = cclk_g_parents_gen5 ,
. cclk_lp_parents = cclk_lp_parents_gen5 ,
. num_sclk_parents = ARRAY_SIZE ( sclk_parents_gen5 ) ,
. num_cclk_g_parents = ARRAY_SIZE ( cclk_g_parents_gen5 ) ,
. num_cclk_lp_parents = ARRAY_SIZE ( cclk_lp_parents_gen5 ) ,
} ;
2013-09-03 16:46:01 +04:00
static void __init tegra_sclk_init ( void __iomem * clk_base ,
2015-06-19 00:28:35 +03:00
struct tegra_clk * tegra_clks ,
const struct tegra_super_gen_info * gen_info )
2013-09-03 16:46:01 +04:00
{
struct clk * clk ;
struct clk * * dt_clk ;
2015-06-19 00:28:35 +03:00
/* SCLK_MUX */
dt_clk = tegra_lookup_dt_id ( tegra_clk_sclk_mux , tegra_clks ) ;
2013-09-03 16:46:01 +04:00
if ( dt_clk ) {
2015-06-19 00:28:35 +03:00
clk = tegra_clk_register_super_mux ( " sclk_mux " ,
gen_info - > sclk_parents ,
gen_info - > num_sclk_parents ,
2013-09-03 16:46:01 +04:00
CLK_SET_RATE_PARENT ,
clk_base + SCLK_BURST_POLICY ,
0 , 4 , 0 , 0 , NULL ) ;
* dt_clk = clk ;
2015-06-19 00:28:35 +03:00
/* SCLK */
dt_clk = tegra_lookup_dt_id ( tegra_clk_sclk , tegra_clks ) ;
if ( dt_clk ) {
2018-01-10 16:59:42 +03:00
clk = clk_register_divider ( NULL , " sclk " , " sclk_mux " ,
CLK_IS_CRITICAL ,
2015-06-19 00:28:35 +03:00
clk_base + SCLK_DIVIDER , 0 , 8 ,
0 , & sysrate_lock ) ;
* dt_clk = clk ;
}
} else {
/* SCLK */
dt_clk = tegra_lookup_dt_id ( tegra_clk_sclk , tegra_clks ) ;
if ( dt_clk ) {
clk = tegra_clk_register_super_mux ( " sclk " ,
gen_info - > sclk_parents ,
gen_info - > num_sclk_parents ,
2018-01-10 16:59:42 +03:00
CLK_SET_RATE_PARENT |
CLK_IS_CRITICAL ,
2015-06-19 00:28:35 +03:00
clk_base + SCLK_BURST_POLICY ,
0 , 4 , 0 , 0 , NULL ) ;
* dt_clk = clk ;
}
2013-09-03 16:46:01 +04:00
}
/* HCLK */
dt_clk = tegra_lookup_dt_id ( tegra_clk_hclk , tegra_clks ) ;
if ( dt_clk ) {
clk = clk_register_divider ( NULL , " hclk_div " , " sclk " , 0 ,
clk_base + SYSTEM_CLK_RATE , 4 , 2 , 0 ,
& sysrate_lock ) ;
clk = clk_register_gate ( NULL , " hclk " , " hclk_div " ,
2018-01-10 16:59:42 +03:00
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL ,
2013-09-03 16:46:01 +04:00
clk_base + SYSTEM_CLK_RATE ,
7 , CLK_GATE_SET_TO_DISABLE , & sysrate_lock ) ;
* dt_clk = clk ;
}
/* PCLK */
dt_clk = tegra_lookup_dt_id ( tegra_clk_pclk , tegra_clks ) ;
if ( ! dt_clk )
return ;
clk = clk_register_divider ( NULL , " pclk_div " , " hclk " , 0 ,
clk_base + SYSTEM_CLK_RATE , 0 , 2 , 0 ,
& sysrate_lock ) ;
clk = clk_register_gate ( NULL , " pclk " , " pclk_div " , CLK_SET_RATE_PARENT |
2017-10-23 14:12:52 +03:00
CLK_IS_CRITICAL , clk_base + SYSTEM_CLK_RATE ,
2013-09-03 16:46:01 +04:00
3 , CLK_GATE_SET_TO_DISABLE , & sysrate_lock ) ;
* dt_clk = clk ;
}
2015-12-04 20:04:25 +03:00
static void __init tegra_super_clk_init ( void __iomem * clk_base ,
2013-09-03 16:46:01 +04:00
void __iomem * pmc_base ,
struct tegra_clk * tegra_clks ,
2015-06-19 00:28:35 +03:00
struct tegra_clk_pll_params * params ,
const struct tegra_super_gen_info * gen_info )
2013-09-03 16:46:01 +04:00
{
struct clk * clk ;
struct clk * * dt_clk ;
/* CCLKG */
dt_clk = tegra_lookup_dt_id ( tegra_clk_cclk_g , tegra_clks ) ;
if ( dt_clk ) {
2015-06-19 00:28:35 +03:00
if ( gen_info - > gen = = gen5 ) {
clk = tegra_clk_register_super_mux ( " cclk_g " ,
gen_info - > cclk_g_parents ,
gen_info - > num_cclk_g_parents ,
CLK_SET_RATE_PARENT ,
clk_base + CCLKG_BURST_POLICY ,
2019-08-16 22:41:54 +03:00
TEGRA210_CPU_CLK , 4 , 8 , 0 , NULL ) ;
2015-06-19 00:28:35 +03:00
} else {
clk = tegra_clk_register_super_mux ( " cclk_g " ,
gen_info - > cclk_g_parents ,
gen_info - > num_cclk_g_parents ,
2013-09-03 16:46:01 +04:00
CLK_SET_RATE_PARENT ,
clk_base + CCLKG_BURST_POLICY ,
0 , 4 , 0 , 0 , NULL ) ;
2015-06-19 00:28:35 +03:00
}
2013-09-03 16:46:01 +04:00
* dt_clk = clk ;
}
/* CCLKLP */
dt_clk = tegra_lookup_dt_id ( tegra_clk_cclk_lp , tegra_clks ) ;
if ( dt_clk ) {
2015-06-19 00:28:35 +03:00
if ( gen_info - > gen = = gen5 ) {
2019-08-16 22:41:54 +03:00
/*
* TEGRA210_CPU_CLK flag is not needed for cclk_lp as
* cluster switching is not currently supported on
* Tegra210 and also cpu_lp is not used .
*/
2015-06-19 00:28:35 +03:00
clk = tegra_clk_register_super_mux ( " cclk_lp " ,
gen_info - > cclk_lp_parents ,
gen_info - > num_cclk_lp_parents ,
CLK_SET_RATE_PARENT ,
clk_base + CCLKLP_BURST_POLICY ,
0 , 4 , 8 , 0 , NULL ) ;
} else {
clk = tegra_clk_register_super_mux ( " cclk_lp " ,
gen_info - > cclk_lp_parents ,
gen_info - > num_cclk_lp_parents ,
2013-09-03 16:46:01 +04:00
CLK_SET_RATE_PARENT ,
clk_base + CCLKLP_BURST_POLICY ,
2013-12-27 04:44:26 +04:00
TEGRA_DIVIDER_2 , 4 , 8 , 9 , NULL ) ;
2015-06-19 00:28:35 +03:00
}
2013-09-03 16:46:01 +04:00
* dt_clk = clk ;
}
2015-06-19 00:28:35 +03:00
tegra_sclk_init ( clk_base , tegra_clks , gen_info ) ;
2013-09-03 16:46:01 +04:00
2015-06-19 00:28:35 +03:00
# if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
defined ( CONFIG_ARCH_TEGRA_124_SOC ) | | \
defined ( CONFIG_ARCH_TEGRA_210_SOC )
2013-09-03 16:46:01 +04:00
/* PLLX */
dt_clk = tegra_lookup_dt_id ( tegra_clk_pll_x , tegra_clks ) ;
if ( ! dt_clk )
return ;
2017-07-25 13:34:11 +03:00
# if defined(CONFIG_ARCH_TEGRA_210_SOC)
if ( gen_info - > gen = = gen5 )
clk = tegra_clk_register_pllc_tegra210 ( " pll_x " , " pll_ref " ,
clk_base , pmc_base , CLK_IGNORE_UNUSED , params , NULL ) ;
else
# endif
clk = tegra_clk_register_pllxc ( " pll_x " , " pll_ref " , clk_base ,
pmc_base , CLK_IGNORE_UNUSED , params , NULL ) ;
2013-09-03 16:46:01 +04:00
* dt_clk = clk ;
/* PLLX_OUT0 */
dt_clk = tegra_lookup_dt_id ( tegra_clk_pll_x_out0 , tegra_clks ) ;
if ( ! dt_clk )
return ;
clk = clk_register_fixed_factor ( NULL , " pll_x_out0 " , " pll_x " ,
CLK_SET_RATE_PARENT , 1 , 2 ) ;
* dt_clk = clk ;
# endif
}
2015-06-19 00:28:35 +03:00
void __init tegra_super_clk_gen4_init ( void __iomem * clk_base ,
void __iomem * pmc_base ,
struct tegra_clk * tegra_clks ,
struct tegra_clk_pll_params * params )
{
tegra_super_clk_init ( clk_base , pmc_base , tegra_clks , params ,
& tegra_super_gen_info_gen4 ) ;
}
void __init tegra_super_clk_gen5_init ( void __iomem * clk_base ,
void __iomem * pmc_base ,
struct tegra_clk * tegra_clks ,
struct tegra_clk_pll_params * params )
{
tegra_super_clk_init ( clk_base , pmc_base , tegra_clks , params ,
& tegra_super_gen_info_gen5 ) ;
}