2018-12-13 15:42:54 +00:00
// SPDX-License-Identifier: GPL-2.0+
/*
2021-04-23 11:33:33 +08:00
* Copyright 2018 - 2021 NXP
2018-12-13 15:42:54 +00:00
* Dong Aisheng < aisheng . dong @ nxp . com >
*/
2019-02-26 05:17:36 +00:00
# include <dt-bindings/firmware/imx/rsrc.h>
# include <linux/arm-smccc.h>
2021-04-23 11:33:33 +08:00
# include <linux/bsearch.h>
2018-12-13 15:42:54 +00:00
# include <linux/clk-provider.h>
# include <linux/err.h>
2020-07-29 16:00:10 +08:00
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
2020-07-29 16:00:13 +08:00
# include <linux/pm_runtime.h>
2018-12-13 15:42:54 +00:00
# include <linux/slab.h>
# include "clk-scu.h"
2019-02-26 05:17:36 +00:00
# define IMX_SIP_CPUFREQ 0xC2000001
# define IMX_SIP_SET_CPUFREQ 0x00
2018-12-13 15:42:54 +00:00
static struct imx_sc_ipc * ccm_ipc_handle ;
2020-11-04 19:19:31 +08:00
static struct device_node * pd_np ;
2020-10-30 23:37:33 +08:00
static struct platform_driver imx_clk_scu_driver ;
2021-04-23 11:33:33 +08:00
static const struct imx_clk_scu_rsrc_table * rsrc_table ;
2020-07-29 16:00:10 +08:00
struct imx_scu_clk_node {
const char * name ;
u32 rsrc ;
u8 clk_type ;
const char * const * parents ;
int num_parents ;
struct clk_hw * hw ;
struct list_head node ;
} ;
struct list_head imx_scu_clks [ IMX_SC_R_LAST ] ;
2018-12-13 15:42:54 +00:00
/*
* struct clk_scu - Description of one SCU clock
* @ hw : the common clk_hw
* @ rsrc_id : resource ID of this SCU clock
* @ clk_type : type of this clock resource
*/
struct clk_scu {
struct clk_hw hw ;
u16 rsrc_id ;
u8 clk_type ;
2020-07-29 16:00:14 +08:00
/* for state save&restore */
2021-06-04 17:09:42 +08:00
struct clk_hw * parent ;
u8 parent_index ;
2020-07-29 16:00:14 +08:00
bool is_enabled ;
u32 rate ;
2018-12-13 15:42:54 +00:00
} ;
2021-04-23 11:33:32 +08:00
/*
* struct clk_gpr_scu - Description of one SCU GPR clock
* @ hw : the common clk_hw
* @ rsrc_id : resource ID of this SCU clock
* @ gpr_id : GPR ID index to control the divider
*/
struct clk_gpr_scu {
struct clk_hw hw ;
u16 rsrc_id ;
u8 gpr_id ;
u8 flags ;
bool gate_invert ;
} ;
# define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw)
2018-12-13 15:42:54 +00:00
/*
* struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
* @ hdr : SCU protocol header
* @ rate : rate to set
* @ resource : clock resource to set rate
* @ clk : clk type of this resource
*
* This structure describes the SCU protocol of clock rate set
*/
struct imx_sc_msg_req_set_clock_rate {
struct imx_sc_rpc_msg hdr ;
__le32 rate ;
__le16 resource ;
u8 clk ;
2020-02-20 18:29:32 +02:00
} __packed __aligned ( 4 ) ;
2018-12-13 15:42:54 +00:00
struct req_get_clock_rate {
__le16 resource ;
u8 clk ;
2020-02-20 18:29:32 +02:00
} __packed __aligned ( 4 ) ;
2018-12-13 15:42:54 +00:00
struct resp_get_clock_rate {
__le32 rate ;
} ;
/*
* struct imx_sc_msg_get_clock_rate - clock get rate protocol
* @ hdr : SCU protocol header
* @ req : get rate request protocol
* @ resp : get rate response protocol
*
* This structure describes the SCU protocol of clock rate get
*/
struct imx_sc_msg_get_clock_rate {
struct imx_sc_rpc_msg hdr ;
union {
struct req_get_clock_rate req ;
struct resp_get_clock_rate resp ;
} data ;
} ;
2019-01-30 13:39:15 +00:00
/*
* struct imx_sc_msg_get_clock_parent - clock get parent protocol
* @ hdr : SCU protocol header
* @ req : get parent request protocol
* @ resp : get parent response protocol
*
* This structure describes the SCU protocol of clock get parent
*/
struct imx_sc_msg_get_clock_parent {
struct imx_sc_rpc_msg hdr ;
union {
struct req_get_clock_parent {
__le16 resource ;
u8 clk ;
2020-02-20 18:29:33 +02:00
} __packed __aligned ( 4 ) req ;
2019-01-30 13:39:15 +00:00
struct resp_get_clock_parent {
u8 parent ;
} resp ;
} data ;
} ;
/*
* struct imx_sc_msg_set_clock_parent - clock set parent protocol
* @ hdr : SCU protocol header
* @ req : set parent request protocol
*
* This structure describes the SCU protocol of clock set parent
*/
struct imx_sc_msg_set_clock_parent {
struct imx_sc_rpc_msg hdr ;
__le16 resource ;
u8 clk ;
u8 parent ;
} __packed ;
2018-12-13 15:42:54 +00:00
/*
* struct imx_sc_msg_req_clock_enable - clock gate protocol
* @ hdr : SCU protocol header
* @ resource : clock resource to gate
* @ clk : clk type of this resource
* @ enable : whether gate off the clock
* @ autog : HW auto gate enable
*
* This structure describes the SCU protocol of clock gate
*/
struct imx_sc_msg_req_clock_enable {
struct imx_sc_rpc_msg hdr ;
__le16 resource ;
u8 clk ;
u8 enable ;
u8 autog ;
2020-02-20 18:29:32 +02:00
} __packed __aligned ( 4 ) ;
2018-12-13 15:42:54 +00:00
static inline struct clk_scu * to_clk_scu ( struct clk_hw * hw )
{
return container_of ( hw , struct clk_scu , hw ) ;
}
2021-04-23 11:33:33 +08:00
static inline int imx_scu_clk_search_cmp ( const void * rsrc , const void * rsrc_p )
{
return * ( u32 * ) rsrc - * ( u32 * ) rsrc_p ;
}
static bool imx_scu_clk_is_valid ( u32 rsrc_id )
{
void * p ;
if ( ! rsrc_table )
return true ;
p = bsearch ( & rsrc_id , rsrc_table - > rsrc , rsrc_table - > num ,
sizeof ( rsrc_table - > rsrc [ 0 ] ) , imx_scu_clk_search_cmp ) ;
return p ! = NULL ;
}
int imx_clk_scu_init ( struct device_node * np ,
const struct imx_clk_scu_rsrc_table * data )
2018-12-13 15:42:54 +00:00
{
2020-07-29 16:00:10 +08:00
u32 clk_cells ;
int ret , i ;
ret = imx_scu_get_handle ( & ccm_ipc_handle ) ;
if ( ret )
return ret ;
of_property_read_u32 ( np , " #clock-cells " , & clk_cells ) ;
if ( clk_cells = = 2 ) {
for ( i = 0 ; i < IMX_SC_R_LAST ; i + + )
INIT_LIST_HEAD ( & imx_scu_clks [ i ] ) ;
2020-11-19 19:43:02 +08:00
/* pd_np will be used to attach power domains later */
2020-07-29 16:00:10 +08:00
pd_np = of_find_compatible_node ( NULL , NULL , " fsl,scu-pd " ) ;
2020-11-19 19:43:02 +08:00
if ( ! pd_np )
return - EINVAL ;
2021-04-23 11:33:33 +08:00
rsrc_table = data ;
2020-07-29 16:00:10 +08:00
}
2020-10-30 23:37:33 +08:00
return platform_driver_register ( & imx_clk_scu_driver ) ;
2018-12-13 15:42:54 +00:00
}
/*
* clk_scu_recalc_rate - Get clock rate for a SCU clock
* @ hw : clock to get rate for
* @ parent_rate : parent rate provided by common clock framework , not used
*
* Gets the current clock rate of a SCU clock . Returns the current
* clock rate , or zero in failure .
*/
static unsigned long clk_scu_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
struct imx_sc_msg_get_clock_rate msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
int ret ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_PM ;
hdr - > func = IMX_SC_PM_FUNC_GET_CLOCK_RATE ;
hdr - > size = 2 ;
msg . data . req . resource = cpu_to_le16 ( clk - > rsrc_id ) ;
msg . data . req . clk = clk - > clk_type ;
ret = imx_scu_call_rpc ( ccm_ipc_handle , & msg , true ) ;
if ( ret ) {
pr_err ( " %s: failed to get clock rate %d \n " ,
clk_hw_get_name ( hw ) , ret ) ;
return 0 ;
}
return le32_to_cpu ( msg . data . resp . rate ) ;
}
/*
* clk_scu_round_rate - Round clock rate for a SCU clock
* @ hw : clock to round rate for
* @ rate : rate to round
* @ parent_rate : parent rate provided by common clock framework , not used
*
* Returns the current clock rate , or zero in failure .
*/
static long clk_scu_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
/*
* Assume we support all the requested rate and let the SCU firmware
* to handle the left work
*/
return rate ;
}
2019-02-26 05:17:36 +00:00
static int clk_scu_atf_set_cpu_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
struct arm_smccc_res res ;
unsigned long cluster_id ;
2021-06-04 17:09:39 +08:00
if ( clk - > rsrc_id = = IMX_SC_R_A35 | | clk - > rsrc_id = = IMX_SC_R_A53 )
2019-02-26 05:17:36 +00:00
cluster_id = 0 ;
2021-06-04 17:09:40 +08:00
else if ( clk - > rsrc_id = = IMX_SC_R_A72 )
cluster_id = 1 ;
2019-02-26 05:17:36 +00:00
else
return - EINVAL ;
/* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
arm_smccc_smc ( IMX_SIP_CPUFREQ , IMX_SIP_SET_CPUFREQ ,
cluster_id , rate , 0 , 0 , 0 , 0 , & res ) ;
return 0 ;
}
2018-12-13 15:42:54 +00:00
/*
* clk_scu_set_rate - Set rate for a SCU clock
* @ hw : clock to change rate for
* @ rate : target rate for the clock
* @ parent_rate : rate of the clock parent , not used for SCU clocks
*
* Sets a clock frequency for a SCU clock . Returns the SCU
* protocol status .
*/
static int clk_scu_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
struct imx_sc_msg_req_set_clock_rate msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_PM ;
hdr - > func = IMX_SC_PM_FUNC_SET_CLOCK_RATE ;
hdr - > size = 3 ;
msg . rate = cpu_to_le32 ( rate ) ;
msg . resource = cpu_to_le16 ( clk - > rsrc_id ) ;
msg . clk = clk - > clk_type ;
return imx_scu_call_rpc ( ccm_ipc_handle , & msg , true ) ;
}
2019-01-30 13:39:15 +00:00
static u8 clk_scu_get_parent ( struct clk_hw * hw )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
struct imx_sc_msg_get_clock_parent msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
int ret ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_PM ;
hdr - > func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT ;
hdr - > size = 2 ;
msg . data . req . resource = cpu_to_le16 ( clk - > rsrc_id ) ;
msg . data . req . clk = clk - > clk_type ;
ret = imx_scu_call_rpc ( ccm_ipc_handle , & msg , true ) ;
if ( ret ) {
pr_err ( " %s: failed to get clock parent %d \n " ,
clk_hw_get_name ( hw ) , ret ) ;
return 0 ;
}
2021-06-04 17:09:42 +08:00
clk - > parent_index = msg . data . resp . parent ;
2019-01-30 13:39:15 +00:00
return msg . data . resp . parent ;
}
static int clk_scu_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
struct imx_sc_msg_set_clock_parent msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
2021-06-04 17:09:42 +08:00
int ret ;
2019-01-30 13:39:15 +00:00
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_PM ;
hdr - > func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT ;
hdr - > size = 2 ;
msg . resource = cpu_to_le16 ( clk - > rsrc_id ) ;
msg . clk = clk - > clk_type ;
msg . parent = index ;
2021-06-04 17:09:42 +08:00
ret = imx_scu_call_rpc ( ccm_ipc_handle , & msg , true ) ;
if ( ret ) {
pr_err ( " %s: failed to set clock parent %d \n " ,
clk_hw_get_name ( hw ) , ret ) ;
return ret ;
}
clk - > parent_index = index ;
return 0 ;
2019-01-30 13:39:15 +00:00
}
2018-12-13 15:42:54 +00:00
static int sc_pm_clock_enable ( struct imx_sc_ipc * ipc , u16 resource ,
u8 clk , bool enable , bool autog )
{
struct imx_sc_msg_req_clock_enable msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_PM ;
hdr - > func = IMX_SC_PM_FUNC_CLOCK_ENABLE ;
hdr - > size = 3 ;
msg . resource = cpu_to_le16 ( resource ) ;
msg . clk = clk ;
msg . enable = enable ;
msg . autog = autog ;
return imx_scu_call_rpc ( ccm_ipc_handle , & msg , true ) ;
}
/*
* clk_scu_prepare - Enable a SCU clock
* @ hw : clock to enable
*
* Enable the clock at the DSC slice level
*/
static int clk_scu_prepare ( struct clk_hw * hw )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
return sc_pm_clock_enable ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > clk_type , true , false ) ;
}
/*
* clk_scu_unprepare - Disable a SCU clock
* @ hw : clock to enable
*
* Disable the clock at the DSC slice level
*/
static void clk_scu_unprepare ( struct clk_hw * hw )
{
struct clk_scu * clk = to_clk_scu ( hw ) ;
int ret ;
ret = sc_pm_clock_enable ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > clk_type , false , false ) ;
if ( ret )
pr_warn ( " %s: clk unprepare failed %d \n " , clk_hw_get_name ( hw ) ,
ret ) ;
}
static const struct clk_ops clk_scu_ops = {
. recalc_rate = clk_scu_recalc_rate ,
. round_rate = clk_scu_round_rate ,
. set_rate = clk_scu_set_rate ,
2019-01-30 13:39:15 +00:00
. get_parent = clk_scu_get_parent ,
. set_parent = clk_scu_set_parent ,
2018-12-13 15:42:54 +00:00
. prepare = clk_scu_prepare ,
. unprepare = clk_scu_unprepare ,
} ;
2019-02-26 05:17:36 +00:00
static const struct clk_ops clk_scu_cpu_ops = {
. recalc_rate = clk_scu_recalc_rate ,
. round_rate = clk_scu_round_rate ,
. set_rate = clk_scu_atf_set_cpu_rate ,
. prepare = clk_scu_prepare ,
. unprepare = clk_scu_unprepare ,
} ;
2021-06-04 17:09:35 +08:00
static const struct clk_ops clk_scu_pi_ops = {
. recalc_rate = clk_scu_recalc_rate ,
. round_rate = clk_scu_round_rate ,
. set_rate = clk_scu_set_rate ,
} ;
2020-07-29 16:00:12 +08:00
struct clk_hw * __imx_clk_scu ( struct device * dev , const char * name ,
const char * const * parents , int num_parents ,
u32 rsrc_id , u8 clk_type )
2018-12-13 15:42:54 +00:00
{
struct clk_init_data init ;
struct clk_scu * clk ;
struct clk_hw * hw ;
int ret ;
clk = kzalloc ( sizeof ( * clk ) , GFP_KERNEL ) ;
if ( ! clk )
return ERR_PTR ( - ENOMEM ) ;
clk - > rsrc_id = rsrc_id ;
clk - > clk_type = clk_type ;
init . name = name ;
init . ops = & clk_scu_ops ;
2021-06-04 17:09:40 +08:00
if ( rsrc_id = = IMX_SC_R_A35 | | rsrc_id = = IMX_SC_R_A53 | | rsrc_id = = IMX_SC_R_A72 )
2019-02-26 05:17:36 +00:00
init . ops = & clk_scu_cpu_ops ;
2021-06-04 17:09:35 +08:00
else if ( rsrc_id = = IMX_SC_R_PI_0_PLL )
init . ops = & clk_scu_pi_ops ;
2019-02-26 05:17:36 +00:00
else
init . ops = & clk_scu_ops ;
2019-01-30 13:39:15 +00:00
init . parent_names = parents ;
init . num_parents = num_parents ;
2018-12-13 15:42:54 +00:00
/*
* Note on MX8 , the clocks are tightly coupled with power domain
* that once the power domain is off , the clock status may be
* lost . So we make it NOCACHE to let user to retrieve the real
* clock status from HW instead of using the possible invalid
* cached rate .
*/
init . flags = CLK_GET_RATE_NOCACHE ;
clk - > hw . init = & init ;
hw = & clk - > hw ;
2020-07-29 16:00:12 +08:00
ret = clk_hw_register ( dev , hw ) ;
2018-12-13 15:42:54 +00:00
if ( ret ) {
kfree ( clk ) ;
hw = ERR_PTR ( ret ) ;
2021-03-23 11:10:34 +08:00
return hw ;
2018-12-13 15:42:54 +00:00
}
2020-07-29 16:00:14 +08:00
if ( dev )
dev_set_drvdata ( dev , clk ) ;
2018-12-13 15:42:54 +00:00
return hw ;
}
2020-07-29 16:00:10 +08:00
struct clk_hw * imx_scu_of_clk_src_get ( struct of_phandle_args * clkspec ,
void * data )
{
unsigned int rsrc = clkspec - > args [ 0 ] ;
unsigned int idx = clkspec - > args [ 1 ] ;
struct list_head * scu_clks = data ;
struct imx_scu_clk_node * clk ;
list_for_each_entry ( clk , & scu_clks [ rsrc ] , node ) {
if ( clk - > clk_type = = idx )
return clk - > hw ;
}
return ERR_PTR ( - ENODEV ) ;
}
static int imx_clk_scu_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct imx_scu_clk_node * clk = dev_get_platdata ( dev ) ;
struct clk_hw * hw ;
2020-07-29 16:00:13 +08:00
int ret ;
2021-06-04 17:09:43 +08:00
if ( ! ( ( clk - > rsrc = = IMX_SC_R_A35 ) | | ( clk - > rsrc = = IMX_SC_R_A53 ) | |
( clk - > rsrc = = IMX_SC_R_A72 ) ) ) {
pm_runtime_set_suspended ( dev ) ;
pm_runtime_set_autosuspend_delay ( dev , 50 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_runtime_enable ( dev ) ;
ret = pm_runtime_get_sync ( dev ) ;
if ( ret ) {
pm_genpd_remove_device ( dev ) ;
pm_runtime_disable ( dev ) ;
return ret ;
}
2020-07-29 16:00:13 +08:00
}
2020-07-29 16:00:10 +08:00
2020-07-29 16:00:13 +08:00
hw = __imx_clk_scu ( dev , clk - > name , clk - > parents , clk - > num_parents ,
2020-07-29 16:00:10 +08:00
clk - > rsrc , clk - > clk_type ) ;
2020-07-29 16:00:13 +08:00
if ( IS_ERR ( hw ) ) {
pm_runtime_disable ( dev ) ;
2020-07-29 16:00:10 +08:00
return PTR_ERR ( hw ) ;
2020-07-29 16:00:13 +08:00
}
2020-07-29 16:00:10 +08:00
clk - > hw = hw ;
list_add_tail ( & clk - > node , & imx_scu_clks [ clk - > rsrc ] ) ;
2021-06-04 17:09:43 +08:00
if ( ! ( ( clk - > rsrc = = IMX_SC_R_A35 ) | | ( clk - > rsrc = = IMX_SC_R_A53 ) | |
( clk - > rsrc = = IMX_SC_R_A72 ) ) ) {
pm_runtime_mark_last_busy ( & pdev - > dev ) ;
pm_runtime_put_autosuspend ( & pdev - > dev ) ;
}
2020-07-29 16:00:13 +08:00
2020-07-29 16:00:10 +08:00
dev_dbg ( dev , " register SCU clock rsrc:%d type:%d \n " , clk - > rsrc ,
clk - > clk_type ) ;
return 0 ;
}
2020-07-29 16:00:14 +08:00
static int __maybe_unused imx_clk_scu_suspend ( struct device * dev )
{
struct clk_scu * clk = dev_get_drvdata ( dev ) ;
2021-06-04 17:09:36 +08:00
u32 rsrc_id = clk - > rsrc_id ;
if ( ( rsrc_id = = IMX_SC_R_A35 ) | | ( rsrc_id = = IMX_SC_R_A53 ) | |
( rsrc_id = = IMX_SC_R_A72 ) )
return 0 ;
2020-07-29 16:00:14 +08:00
2021-06-04 17:09:42 +08:00
clk - > parent = clk_hw_get_parent ( & clk - > hw ) ;
2021-06-04 17:09:41 +08:00
/* DC SS needs to handle bypass clock using non-cached clock rate */
if ( clk - > rsrc_id = = IMX_SC_R_DC_0_VIDEO0 | |
clk - > rsrc_id = = IMX_SC_R_DC_0_VIDEO1 | |
clk - > rsrc_id = = IMX_SC_R_DC_1_VIDEO0 | |
clk - > rsrc_id = = IMX_SC_R_DC_1_VIDEO1 )
clk - > rate = clk_scu_recalc_rate ( & clk - > hw , 0 ) ;
else
clk - > rate = clk_hw_get_rate ( & clk - > hw ) ;
2020-07-29 16:00:14 +08:00
clk - > is_enabled = clk_hw_is_enabled ( & clk - > hw ) ;
2021-06-04 17:09:42 +08:00
if ( clk - > parent )
dev_dbg ( dev , " save parent %s idx %u \n " , clk_hw_get_name ( clk - > parent ) ,
clk - > parent_index ) ;
2020-07-29 16:00:14 +08:00
if ( clk - > rate )
dev_dbg ( dev , " save rate %d \n " , clk - > rate ) ;
if ( clk - > is_enabled )
dev_dbg ( dev , " save enabled state \n " ) ;
return 0 ;
}
static int __maybe_unused imx_clk_scu_resume ( struct device * dev )
{
struct clk_scu * clk = dev_get_drvdata ( dev ) ;
2021-06-04 17:09:36 +08:00
u32 rsrc_id = clk - > rsrc_id ;
2020-07-29 16:00:14 +08:00
int ret = 0 ;
2021-06-04 17:09:36 +08:00
if ( ( rsrc_id = = IMX_SC_R_A35 ) | | ( rsrc_id = = IMX_SC_R_A53 ) | |
( rsrc_id = = IMX_SC_R_A72 ) )
return 0 ;
2021-06-04 17:09:42 +08:00
if ( clk - > parent ) {
ret = clk_scu_set_parent ( & clk - > hw , clk - > parent_index ) ;
dev_dbg ( dev , " restore parent %s idx %u %s \n " ,
clk_hw_get_name ( clk - > parent ) ,
clk - > parent_index , ! ret ? " success " : " failed " ) ;
}
2020-07-29 16:00:14 +08:00
if ( clk - > rate ) {
ret = clk_scu_set_rate ( & clk - > hw , clk - > rate , 0 ) ;
dev_dbg ( dev , " restore rate %d %s \n " , clk - > rate ,
! ret ? " success " : " failed " ) ;
}
2021-06-04 17:09:38 +08:00
if ( clk - > is_enabled & & rsrc_id ! = IMX_SC_R_PI_0_PLL ) {
2020-07-29 16:00:14 +08:00
ret = clk_scu_prepare ( & clk - > hw ) ;
dev_dbg ( dev , " restore enabled state %s \n " ,
! ret ? " success " : " failed " ) ;
}
return ret ;
}
static const struct dev_pm_ops imx_clk_scu_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS ( imx_clk_scu_suspend ,
imx_clk_scu_resume )
} ;
2020-07-29 16:00:10 +08:00
static struct platform_driver imx_clk_scu_driver = {
. driver = {
. name = " imx-scu-clk " ,
. suppress_bind_attrs = true ,
2020-07-29 16:00:14 +08:00
. pm = & imx_clk_scu_pm_ops ,
2020-07-29 16:00:10 +08:00
} ,
. probe = imx_clk_scu_probe ,
} ;
static int imx_clk_scu_attach_pd ( struct device * dev , u32 rsrc_id )
{
struct of_phandle_args genpdspec = {
. np = pd_np ,
. args_count = 1 ,
. args [ 0 ] = rsrc_id ,
} ;
2020-07-29 16:00:11 +08:00
if ( rsrc_id = = IMX_SC_R_A35 | | rsrc_id = = IMX_SC_R_A53 | |
rsrc_id = = IMX_SC_R_A72 )
return 0 ;
2020-07-29 16:00:10 +08:00
return of_genpd_add_device ( & genpdspec , dev ) ;
}
struct clk_hw * imx_clk_scu_alloc_dev ( const char * name ,
const char * const * parents ,
int num_parents , u32 rsrc_id , u8 clk_type )
{
struct imx_scu_clk_node clk = {
. name = name ,
. rsrc = rsrc_id ,
. clk_type = clk_type ,
. parents = parents ,
. num_parents = num_parents ,
} ;
struct platform_device * pdev ;
int ret ;
2021-04-23 11:33:33 +08:00
if ( ! imx_scu_clk_is_valid ( rsrc_id ) )
return ERR_PTR ( - EINVAL ) ;
2020-07-29 16:00:10 +08:00
pdev = platform_device_alloc ( name , PLATFORM_DEVID_NONE ) ;
if ( ! pdev ) {
pr_err ( " %s: failed to allocate scu clk dev rsrc %d type %d \n " ,
name , rsrc_id , clk_type ) ;
return ERR_PTR ( - ENOMEM ) ;
}
ret = platform_device_add_data ( pdev , & clk , sizeof ( clk ) ) ;
if ( ret ) {
platform_device_put ( pdev ) ;
return ERR_PTR ( ret ) ;
}
pdev - > driver_override = " imx-scu-clk " ;
ret = imx_clk_scu_attach_pd ( & pdev - > dev , rsrc_id ) ;
if ( ret )
pr_warn ( " %s: failed to attached the power domain %d \n " ,
name , ret ) ;
platform_device_add ( pdev ) ;
/* For API backwards compatiblilty, simply return NULL for success */
return NULL ;
}
void imx_clk_scu_unregister ( void )
{
struct imx_scu_clk_node * clk ;
int i ;
for ( i = 0 ; i < IMX_SC_R_LAST ; i + + ) {
list_for_each_entry ( clk , & imx_scu_clks [ i ] , node ) {
clk_hw_unregister ( clk - > hw ) ;
kfree ( clk ) ;
}
}
}
2021-04-23 11:33:32 +08:00
static unsigned long clk_gpr_div_scu_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
unsigned long rate = 0 ;
u32 val ;
int err ;
err = imx_sc_misc_get_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , & val ) ;
rate = val ? parent_rate / 2 : parent_rate ;
return err ? 0 : rate ;
}
static long clk_gpr_div_scu_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
if ( rate < * prate )
rate = * prate / 2 ;
else
rate = * prate ;
return rate ;
}
static int clk_gpr_div_scu_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
uint32_t val ;
int err ;
val = ( rate < parent_rate ) ? 1 : 0 ;
err = imx_sc_misc_set_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , val ) ;
return err ? - EINVAL : 0 ;
}
static const struct clk_ops clk_gpr_div_scu_ops = {
. recalc_rate = clk_gpr_div_scu_recalc_rate ,
. round_rate = clk_gpr_div_scu_round_rate ,
. set_rate = clk_gpr_div_scu_set_rate ,
} ;
static u8 clk_gpr_mux_scu_get_parent ( struct clk_hw * hw )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
u32 val = 0 ;
imx_sc_misc_get_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , & val ) ;
return ( u8 ) val ;
}
static int clk_gpr_mux_scu_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
return imx_sc_misc_set_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , index ) ;
}
static const struct clk_ops clk_gpr_mux_scu_ops = {
. get_parent = clk_gpr_mux_scu_get_parent ,
. set_parent = clk_gpr_mux_scu_set_parent ,
} ;
static int clk_gpr_gate_scu_prepare ( struct clk_hw * hw )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
return imx_sc_misc_set_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , ! clk - > gate_invert ) ;
}
static void clk_gpr_gate_scu_unprepare ( struct clk_hw * hw )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
int ret ;
ret = imx_sc_misc_set_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , clk - > gate_invert ) ;
if ( ret )
pr_err ( " %s: clk unprepare failed %d \n " , clk_hw_get_name ( hw ) ,
ret ) ;
}
static int clk_gpr_gate_scu_is_prepared ( struct clk_hw * hw )
{
struct clk_gpr_scu * clk = to_clk_gpr_scu ( hw ) ;
int ret ;
u32 val ;
ret = imx_sc_misc_get_control ( ccm_ipc_handle , clk - > rsrc_id ,
clk - > gpr_id , & val ) ;
if ( ret )
return ret ;
return clk - > gate_invert ? ! val : val ;
}
static const struct clk_ops clk_gpr_gate_scu_ops = {
. prepare = clk_gpr_gate_scu_prepare ,
. unprepare = clk_gpr_gate_scu_unprepare ,
. is_prepared = clk_gpr_gate_scu_is_prepared ,
} ;
struct clk_hw * __imx_clk_gpr_scu ( const char * name , const char * const * parent_name ,
int num_parents , u32 rsrc_id , u8 gpr_id , u8 flags ,
bool invert )
{
struct imx_scu_clk_node * clk_node ;
struct clk_gpr_scu * clk ;
struct clk_hw * hw ;
struct clk_init_data init ;
int ret ;
if ( rsrc_id > = IMX_SC_R_LAST | | gpr_id > = IMX_SC_C_LAST )
return ERR_PTR ( - EINVAL ) ;
clk_node = kzalloc ( sizeof ( * clk_node ) , GFP_KERNEL ) ;
if ( ! clk_node )
return ERR_PTR ( - ENOMEM ) ;
2021-04-23 11:33:33 +08:00
if ( ! imx_scu_clk_is_valid ( rsrc_id ) )
return ERR_PTR ( - EINVAL ) ;
2021-04-23 11:33:32 +08:00
clk = kzalloc ( sizeof ( * clk ) , GFP_KERNEL ) ;
if ( ! clk ) {
kfree ( clk_node ) ;
return ERR_PTR ( - ENOMEM ) ;
}
clk - > rsrc_id = rsrc_id ;
clk - > gpr_id = gpr_id ;
clk - > flags = flags ;
clk - > gate_invert = invert ;
if ( flags & IMX_SCU_GPR_CLK_GATE )
init . ops = & clk_gpr_gate_scu_ops ;
if ( flags & IMX_SCU_GPR_CLK_DIV )
init . ops = & clk_gpr_div_scu_ops ;
if ( flags & IMX_SCU_GPR_CLK_MUX )
init . ops = & clk_gpr_mux_scu_ops ;
init . flags = 0 ;
init . name = name ;
init . parent_names = parent_name ;
init . num_parents = num_parents ;
clk - > hw . init = & init ;
hw = & clk - > hw ;
ret = clk_hw_register ( NULL , hw ) ;
if ( ret ) {
kfree ( clk ) ;
kfree ( clk_node ) ;
hw = ERR_PTR ( ret ) ;
} else {
clk_node - > hw = hw ;
clk_node - > clk_type = gpr_id ;
list_add_tail ( & clk_node - > node , & imx_scu_clks [ rsrc_id ] ) ;
}
return hw ;
}