2018-09-12 15:40:17 +02:00
// SPDX-License-Identifier: GPL-2.0
2016-04-14 17:33:33 +02:00
/*
* Marvell Armada CP110 System Controller
*
* Copyright ( C ) 2016 Marvell
*
* Thomas Petazzoni < thomas . petazzoni @ free - electrons . com >
*
*/
/*
2017-05-31 15:19:15 +02:00
* CP110 has 6 core clocks :
2016-04-14 17:33:33 +02:00
*
2018-02-28 15:07:51 +01:00
* - PLL0 ( 1 Ghz )
* - PPv2 core ( 1 / 3 PLL0 )
* - x2 Core ( 1 / 2 PLL0 )
* - Core ( 1 / 2 x2 Core )
* - SDIO ( 2 / 5 PLL0 )
2016-04-14 17:33:33 +02:00
*
* - NAND clock , which is either :
2017-05-31 15:19:15 +02:00
* - Equal to SDIO clock
2018-02-28 15:07:51 +01:00
* - 2 / 5 PLL0
2016-04-14 17:33:33 +02:00
*
2019-04-16 12:56:16 +01:00
* CP110 has 32 gateable clocks , for the various peripherals in the IP .
2016-04-14 17:33:33 +02:00
*/
# define pr_fmt(fmt) "cp110-system-controller: " fmt
2019-07-10 15:43:42 +02:00
# include "armada_ap_cp_helper.h"
2016-04-14 17:33:33 +02:00
# include <linux/clk-provider.h>
# include <linux/mfd/syscon.h>
2016-07-04 17:12:13 -04:00
# include <linux/init.h>
2016-04-14 17:33:33 +02:00
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# define CP110_PM_CLOCK_GATING_REG 0x220
# define CP110_NAND_FLASH_CLK_CTRL_REG 0x700
# define NF_CLOCK_SEL_400_MASK BIT(0)
enum {
CP110_CLK_TYPE_CORE ,
CP110_CLK_TYPE_GATABLE ,
} ;
2017-05-31 15:19:15 +02:00
# define CP110_MAX_CORE_CLOCKS 6
2016-04-14 17:33:33 +02:00
# define CP110_MAX_GATABLE_CLOCKS 32
# define CP110_CLK_NUM \
( CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS )
2018-02-28 15:07:51 +01:00
# define CP110_CORE_PLL0 0
2016-04-14 17:33:33 +02:00
# define CP110_CORE_PPV2 1
2018-02-28 15:07:51 +01:00
# define CP110_CORE_X2CORE 2
2016-04-14 17:33:33 +02:00
# define CP110_CORE_CORE 3
# define CP110_CORE_NAND 4
2017-05-31 15:19:15 +02:00
# define CP110_CORE_SDIO 5
2016-04-14 17:33:33 +02:00
2019-04-16 12:56:16 +01:00
/* A number of gateable clocks need special handling */
2016-04-14 17:33:33 +02:00
# define CP110_GATE_AUDIO 0
# define CP110_GATE_COMM_UNIT 1
# define CP110_GATE_NAND 2
# define CP110_GATE_PPV2 3
# define CP110_GATE_SDIO 4
2016-12-21 11:26:56 +01:00
# define CP110_GATE_MG 5
# define CP110_GATE_MG_CORE 6
2016-04-14 17:33:33 +02:00
# define CP110_GATE_XOR1 7
# define CP110_GATE_XOR0 8
2016-12-21 11:26:56 +01:00
# define CP110_GATE_GOP_DP 9
2016-04-14 17:33:33 +02:00
# define CP110_GATE_PCIE_X1_0 11
# define CP110_GATE_PCIE_X1_1 12
# define CP110_GATE_PCIE_X4 13
# define CP110_GATE_PCIE_XOR 14
# define CP110_GATE_SATA 15
# define CP110_GATE_SATA_USB 16
# define CP110_GATE_MAIN 17
2016-12-21 11:26:56 +01:00
# define CP110_GATE_SDMMC_GOP 18
2016-04-14 17:33:33 +02:00
# define CP110_GATE_SLOW_IO 21
# define CP110_GATE_USB3H0 22
# define CP110_GATE_USB3H1 23
# define CP110_GATE_USB3DEV 24
# define CP110_GATE_EIP150 25
# define CP110_GATE_EIP197 26
2017-06-19 17:11:47 -07:00
static const char * const gate_base_names [ ] = {
2017-05-31 15:11:09 +02:00
[ CP110_GATE_AUDIO ] = " audio " ,
[ CP110_GATE_COMM_UNIT ] = " communit " ,
[ CP110_GATE_NAND ] = " nand " ,
[ CP110_GATE_PPV2 ] = " ppv2 " ,
[ CP110_GATE_SDIO ] = " sdio " ,
[ CP110_GATE_MG ] = " mg-domain " ,
[ CP110_GATE_MG_CORE ] = " mg-core " ,
[ CP110_GATE_XOR1 ] = " xor1 " ,
[ CP110_GATE_XOR0 ] = " xor0 " ,
[ CP110_GATE_GOP_DP ] = " gop-dp " ,
[ CP110_GATE_PCIE_X1_0 ] = " pcie_x10 " ,
[ CP110_GATE_PCIE_X1_1 ] = " pcie_x11 " ,
[ CP110_GATE_PCIE_X4 ] = " pcie_x4 " ,
[ CP110_GATE_PCIE_XOR ] = " pcie-xor " ,
[ CP110_GATE_SATA ] = " sata " ,
[ CP110_GATE_SATA_USB ] = " sata-usb " ,
[ CP110_GATE_MAIN ] = " main " ,
[ CP110_GATE_SDMMC_GOP ] = " sd-mmc-gop " ,
[ CP110_GATE_SLOW_IO ] = " slow-io " ,
[ CP110_GATE_USB3H0 ] = " usb3h0 " ,
[ CP110_GATE_USB3H1 ] = " usb3h1 " ,
[ CP110_GATE_USB3DEV ] = " usb3dev " ,
[ CP110_GATE_EIP150 ] = " eip150 " ,
[ CP110_GATE_EIP197 ] = " eip197 "
} ;
2016-04-14 17:33:33 +02:00
struct cp110_gate_clk {
struct clk_hw hw ;
struct regmap * regmap ;
u8 bit_idx ;
} ;
2016-09-25 09:47:53 +02:00
# define to_cp110_gate_clk(hw) container_of(hw, struct cp110_gate_clk, hw)
2016-04-14 17:33:33 +02:00
static int cp110_gate_enable ( struct clk_hw * hw )
{
struct cp110_gate_clk * gate = to_cp110_gate_clk ( hw ) ;
regmap_update_bits ( gate - > regmap , CP110_PM_CLOCK_GATING_REG ,
BIT ( gate - > bit_idx ) , BIT ( gate - > bit_idx ) ) ;
return 0 ;
}
static void cp110_gate_disable ( struct clk_hw * hw )
{
struct cp110_gate_clk * gate = to_cp110_gate_clk ( hw ) ;
regmap_update_bits ( gate - > regmap , CP110_PM_CLOCK_GATING_REG ,
BIT ( gate - > bit_idx ) , 0 ) ;
}
static int cp110_gate_is_enabled ( struct clk_hw * hw )
{
struct cp110_gate_clk * gate = to_cp110_gate_clk ( hw ) ;
u32 val ;
regmap_read ( gate - > regmap , CP110_PM_CLOCK_GATING_REG , & val ) ;
return val & BIT ( gate - > bit_idx ) ;
}
static const struct clk_ops cp110_gate_ops = {
. enable = cp110_gate_enable ,
. disable = cp110_gate_disable ,
. is_enabled = cp110_gate_is_enabled ,
} ;
2016-09-25 09:47:53 +02:00
static struct clk_hw * cp110_register_gate ( const char * name ,
const char * parent_name ,
struct regmap * regmap , u8 bit_idx )
2016-04-14 17:33:33 +02:00
{
struct cp110_gate_clk * gate ;
2016-09-25 09:47:53 +02:00
struct clk_hw * hw ;
2016-04-14 17:33:33 +02:00
struct clk_init_data init ;
2016-09-25 09:47:53 +02:00
int ret ;
2016-04-14 17:33:33 +02:00
gate = kzalloc ( sizeof ( * gate ) , GFP_KERNEL ) ;
if ( ! gate )
return ERR_PTR ( - ENOMEM ) ;
2016-09-21 11:05:57 +02:00
memset ( & init , 0 , sizeof ( init ) ) ;
2016-04-14 17:33:33 +02:00
init . name = name ;
init . ops = & cp110_gate_ops ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
gate - > regmap = regmap ;
gate - > bit_idx = bit_idx ;
gate - > hw . init = & init ;
2016-09-25 09:47:53 +02:00
hw = & gate - > hw ;
ret = clk_hw_register ( NULL , hw ) ;
if ( ret ) {
2016-04-14 17:33:33 +02:00
kfree ( gate ) ;
2016-09-25 09:47:53 +02:00
hw = ERR_PTR ( ret ) ;
}
2016-04-14 17:33:33 +02:00
2016-09-25 09:47:53 +02:00
return hw ;
2016-04-14 17:33:33 +02:00
}
2016-09-25 09:47:53 +02:00
static void cp110_unregister_gate ( struct clk_hw * hw )
2016-04-14 17:33:33 +02:00
{
2016-09-25 09:47:53 +02:00
clk_hw_unregister ( hw ) ;
2016-04-14 17:33:33 +02:00
kfree ( to_cp110_gate_clk ( hw ) ) ;
}
2016-09-25 09:47:53 +02:00
static struct clk_hw * cp110_of_clk_get ( struct of_phandle_args * clkspec ,
void * data )
2016-04-14 17:33:33 +02:00
{
2016-09-25 09:47:53 +02:00
struct clk_hw_onecell_data * clk_data = data ;
2016-04-14 17:33:33 +02:00
unsigned int type = clkspec - > args [ 0 ] ;
unsigned int idx = clkspec - > args [ 1 ] ;
if ( type = = CP110_CLK_TYPE_CORE ) {
2018-12-03 17:50:55 +03:00
if ( idx > = CP110_MAX_CORE_CLOCKS )
2016-04-14 17:33:33 +02:00
return ERR_PTR ( - EINVAL ) ;
2016-09-25 09:47:53 +02:00
return clk_data - > hws [ idx ] ;
2016-04-14 17:33:33 +02:00
} else if ( type = = CP110_CLK_TYPE_GATABLE ) {
2018-12-03 17:50:55 +03:00
if ( idx > = CP110_MAX_GATABLE_CLOCKS )
2016-04-14 17:33:33 +02:00
return ERR_PTR ( - EINVAL ) ;
2016-09-25 09:47:53 +02:00
return clk_data - > hws [ CP110_MAX_CORE_CLOCKS + idx ] ;
2016-04-14 17:33:33 +02:00
}
return ERR_PTR ( - EINVAL ) ;
}
2017-05-30 17:46:06 +02:00
static int cp110_syscon_common_probe ( struct platform_device * pdev ,
struct device_node * syscon_node )
2016-04-14 17:33:33 +02:00
{
struct regmap * regmap ;
2017-05-31 15:11:09 +02:00
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
2018-02-28 15:07:51 +01:00
const char * ppv2_name , * pll0_name , * core_name , * x2core_name , * nand_name ,
2017-05-31 15:19:15 +02:00
* sdio_name ;
2016-09-25 09:47:53 +02:00
struct clk_hw_onecell_data * cp110_clk_data ;
struct clk_hw * hw , * * cp110_clks ;
2016-04-14 17:33:33 +02:00
u32 nand_clk_ctrl ;
int i , ret ;
2017-05-31 15:11:09 +02:00
char * gate_name [ ARRAY_SIZE ( gate_base_names ) ] ;
2016-04-14 17:33:33 +02:00
2017-05-30 17:46:06 +02:00
regmap = syscon_node_to_regmap ( syscon_node ) ;
2016-04-14 17:33:33 +02:00
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
ret = regmap_read ( regmap , CP110_NAND_FLASH_CLK_CTRL_REG ,
& nand_clk_ctrl ) ;
if ( ret )
return ret ;
2019-09-27 20:51:10 +02:00
cp110_clk_data = devm_kzalloc ( dev , struct_size ( cp110_clk_data , hws ,
CP110_CLK_NUM ) ,
2016-09-21 11:05:58 +02:00
GFP_KERNEL ) ;
if ( ! cp110_clk_data )
return - ENOMEM ;
2016-09-25 09:47:53 +02:00
cp110_clks = cp110_clk_data - > hws ;
cp110_clk_data - > num = CP110_CLK_NUM ;
2016-09-21 11:05:58 +02:00
2018-02-28 15:07:51 +01:00
/* Register the PLL0 which is the root of the hw tree */
2019-07-10 15:43:42 +02:00
pll0_name = ap_cp_unique_name ( dev , syscon_node , " pll0 " ) ;
2018-02-28 15:07:51 +01:00
hw = clk_hw_register_fixed_rate ( NULL , pll0_name , NULL , 0 ,
2016-09-25 09:47:53 +02:00
1000 * 1000 * 1000 ) ;
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
2018-02-28 15:07:51 +01:00
goto fail_pll0 ;
2016-04-14 17:33:33 +02:00
}
2018-02-28 15:07:51 +01:00
cp110_clks [ CP110_CORE_PLL0 ] = hw ;
2016-04-14 17:33:33 +02:00
2018-02-28 15:07:51 +01:00
/* PPv2 is PLL0/3 */
2019-07-10 15:43:42 +02:00
ppv2_name = ap_cp_unique_name ( dev , syscon_node , " ppv2-core " ) ;
2018-02-28 15:07:51 +01:00
hw = clk_hw_register_fixed_factor ( NULL , ppv2_name , pll0_name , 0 , 1 , 3 ) ;
2016-09-25 09:47:53 +02:00
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
2017-04-07 15:47:08 +02:00
goto fail_ppv2 ;
2016-04-14 17:33:33 +02:00
}
2016-09-25 09:47:53 +02:00
cp110_clks [ CP110_CORE_PPV2 ] = hw ;
2016-04-14 17:33:33 +02:00
2018-02-28 15:07:51 +01:00
/* X2CORE clock is PLL0/2 */
2019-07-10 15:43:42 +02:00
x2core_name = ap_cp_unique_name ( dev , syscon_node , " x2core " ) ;
2018-02-28 15:07:51 +01:00
hw = clk_hw_register_fixed_factor ( NULL , x2core_name , pll0_name ,
0 , 1 , 2 ) ;
2016-09-25 09:47:53 +02:00
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
2017-04-07 15:47:08 +02:00
goto fail_eip ;
2016-04-14 17:33:33 +02:00
}
2018-02-28 15:07:51 +01:00
cp110_clks [ CP110_CORE_X2CORE ] = hw ;
2016-04-14 17:33:33 +02:00
2018-02-28 15:07:51 +01:00
/* Core clock is X2CORE/2 */
2019-07-10 15:43:42 +02:00
core_name = ap_cp_unique_name ( dev , syscon_node , " core " ) ;
2018-02-28 15:07:51 +01:00
hw = clk_hw_register_fixed_factor ( NULL , core_name , x2core_name ,
0 , 1 , 2 ) ;
2016-09-25 09:47:53 +02:00
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
2017-04-07 15:47:08 +02:00
goto fail_core ;
2016-04-14 17:33:33 +02:00
}
2016-09-25 09:47:53 +02:00
cp110_clks [ CP110_CORE_CORE ] = hw ;
2018-02-28 15:07:51 +01:00
/* NAND can be either PLL0/2.5 or core clock */
2019-07-10 15:43:42 +02:00
nand_name = ap_cp_unique_name ( dev , syscon_node , " nand-core " ) ;
2016-04-14 17:33:33 +02:00
if ( nand_clk_ctrl & NF_CLOCK_SEL_400_MASK )
2016-09-25 09:47:53 +02:00
hw = clk_hw_register_fixed_factor ( NULL , nand_name ,
2018-02-28 15:07:51 +01:00
pll0_name , 0 , 2 , 5 ) ;
2016-04-14 17:33:33 +02:00
else
2016-09-25 09:47:53 +02:00
hw = clk_hw_register_fixed_factor ( NULL , nand_name ,
core_name , 0 , 1 , 1 ) ;
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
2017-04-07 15:47:08 +02:00
goto fail_nand ;
2016-04-14 17:33:33 +02:00
}
2016-09-25 09:47:53 +02:00
cp110_clks [ CP110_CORE_NAND ] = hw ;
2016-04-14 17:33:33 +02:00
2018-02-28 15:07:51 +01:00
/* SDIO clock is PLL0/2.5 */
2019-07-10 15:43:42 +02:00
sdio_name = ap_cp_unique_name ( dev , syscon_node , " sdio-core " ) ;
2017-05-31 15:19:15 +02:00
hw = clk_hw_register_fixed_factor ( NULL , sdio_name ,
2018-02-28 15:07:51 +01:00
pll0_name , 0 , 2 , 5 ) ;
2017-05-31 15:19:15 +02:00
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
goto fail_sdio ;
}
cp110_clks [ CP110_CORE_SDIO ] = hw ;
2017-05-31 15:11:09 +02:00
/* create the unique name for all the gate clocks */
for ( i = 0 ; i < ARRAY_SIZE ( gate_base_names ) ; i + + )
2019-07-10 15:43:42 +02:00
gate_name [ i ] = ap_cp_unique_name ( dev , syscon_node ,
2017-05-30 17:46:06 +02:00
gate_base_names [ i ] ) ;
2016-04-14 17:33:33 +02:00
2017-05-31 15:11:09 +02:00
for ( i = 0 ; i < ARRAY_SIZE ( gate_base_names ) ; i + + ) {
const char * parent ;
2016-04-14 17:33:33 +02:00
2017-05-31 15:11:09 +02:00
if ( gate_name [ i ] = = NULL )
2016-04-14 17:33:33 +02:00
continue ;
switch ( i ) {
case CP110_GATE_NAND :
parent = nand_name ;
break ;
2018-02-28 15:07:51 +01:00
case CP110_GATE_MG :
case CP110_GATE_GOP_DP :
2016-04-14 17:33:33 +02:00
case CP110_GATE_PPV2 :
parent = ppv2_name ;
break ;
case CP110_GATE_SDIO :
2017-05-31 15:19:15 +02:00
parent = sdio_name ;
break ;
2018-02-28 15:07:51 +01:00
case CP110_GATE_MAIN :
case CP110_GATE_PCIE_XOR :
2016-04-14 17:33:33 +02:00
case CP110_GATE_PCIE_X4 :
2018-02-28 15:07:51 +01:00
case CP110_GATE_EIP150 :
case CP110_GATE_EIP197 :
parent = x2core_name ;
2016-04-14 17:33:33 +02:00
break ;
default :
parent = core_name ;
break ;
}
2017-05-31 15:11:09 +02:00
hw = cp110_register_gate ( gate_name [ i ] , parent , regmap , i ) ;
2016-04-14 17:33:33 +02:00
2016-09-25 09:47:53 +02:00
if ( IS_ERR ( hw ) ) {
ret = PTR_ERR ( hw ) ;
2016-04-14 17:33:33 +02:00
goto fail_gate ;
}
2016-09-25 09:47:53 +02:00
cp110_clks [ CP110_MAX_CORE_CLOCKS + i ] = hw ;
2016-04-14 17:33:33 +02:00
}
2016-09-25 09:47:53 +02:00
ret = of_clk_add_hw_provider ( np , cp110_of_clk_get , cp110_clk_data ) ;
2016-04-14 17:33:33 +02:00
if ( ret )
goto fail_clk_add ;
2016-09-21 11:05:58 +02:00
platform_set_drvdata ( pdev , cp110_clks ) ;
2016-04-14 17:33:33 +02:00
return 0 ;
fail_clk_add :
fail_gate :
for ( i = 0 ; i < CP110_MAX_GATABLE_CLOCKS ; i + + ) {
2016-09-25 09:47:53 +02:00
hw = cp110_clks [ CP110_MAX_CORE_CLOCKS + i ] ;
2016-04-14 17:33:33 +02:00
2016-09-25 09:47:53 +02:00
if ( hw )
cp110_unregister_gate ( hw ) ;
2016-04-14 17:33:33 +02:00
}
2017-05-31 15:19:15 +02:00
clk_hw_unregister_fixed_factor ( cp110_clks [ CP110_CORE_SDIO ] ) ;
fail_sdio :
2016-09-25 09:47:53 +02:00
clk_hw_unregister_fixed_factor ( cp110_clks [ CP110_CORE_NAND ] ) ;
2017-04-07 15:47:08 +02:00
fail_nand :
2016-09-25 09:47:53 +02:00
clk_hw_unregister_fixed_factor ( cp110_clks [ CP110_CORE_CORE ] ) ;
2017-04-07 15:47:08 +02:00
fail_core :
2018-02-28 15:07:51 +01:00
clk_hw_unregister_fixed_factor ( cp110_clks [ CP110_CORE_X2CORE ] ) ;
2017-04-07 15:47:08 +02:00
fail_eip :
2016-09-25 09:47:53 +02:00
clk_hw_unregister_fixed_factor ( cp110_clks [ CP110_CORE_PPV2 ] ) ;
2017-04-07 15:47:08 +02:00
fail_ppv2 :
2018-02-28 15:07:51 +01:00
clk_hw_unregister_fixed_rate ( cp110_clks [ CP110_CORE_PLL0 ] ) ;
fail_pll0 :
2016-04-14 17:33:33 +02:00
return ret ;
}
2017-05-30 17:46:06 +02:00
static int cp110_syscon_legacy_clk_probe ( struct platform_device * pdev )
{
dev_warn ( & pdev - > dev , FW_WARN " Using legacy device tree binding \n " ) ;
dev_warn ( & pdev - > dev , FW_WARN " Update your device tree: \n " ) ;
dev_warn ( & pdev - > dev , FW_WARN
" This binding won't be supported in future kernels \n " ) ;
return cp110_syscon_common_probe ( pdev , pdev - > dev . of_node ) ;
}
static int cp110_clk_probe ( struct platform_device * pdev )
{
return cp110_syscon_common_probe ( pdev , pdev - > dev . of_node - > parent ) ;
}
static const struct of_device_id cp110_syscon_legacy_of_match [ ] = {
2016-04-14 17:33:33 +02:00
{ . compatible = " marvell,cp110-system-controller0 " , } ,
{ }
} ;
2017-05-30 17:46:06 +02:00
static struct platform_driver cp110_syscon_legacy_driver = {
2017-06-19 17:11:47 -07:00
. probe = cp110_syscon_legacy_clk_probe ,
2016-04-14 17:33:33 +02:00
. driver = {
. name = " marvell-cp110-system-controller0 " ,
2017-05-30 17:46:06 +02:00
. of_match_table = cp110_syscon_legacy_of_match ,
. suppress_bind_attrs = true ,
} ,
} ;
builtin_platform_driver ( cp110_syscon_legacy_driver ) ;
static const struct of_device_id cp110_clock_of_match [ ] = {
{ . compatible = " marvell,cp110-clock " , } ,
{ }
} ;
static struct platform_driver cp110_clock_driver = {
. probe = cp110_clk_probe ,
. driver = {
. name = " marvell-cp110-clock " ,
. of_match_table = cp110_clock_of_match ,
2016-07-04 17:12:13 -04:00
. suppress_bind_attrs = true ,
2016-04-14 17:33:33 +02:00
} ,
} ;
2017-05-30 17:46:06 +02:00
builtin_platform_driver ( cp110_clock_driver ) ;