2018-08-14 17:42:29 +05:30
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018, The Linux Foundation. All rights reserved.
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/slab.h>
# include "clk-krait.h"
2022-11-09 01:56:31 +01:00
enum {
cpu0_mux = 0 ,
cpu1_mux ,
cpu2_mux ,
cpu3_mux ,
l2_mux ,
clks_max ,
} ;
2018-08-14 17:42:29 +05:30
static unsigned int sec_mux_map [ ] = {
2 ,
0 ,
} ;
static unsigned int pri_mux_map [ ] = {
1 ,
2 ,
0 ,
} ;
2018-08-14 17:42:31 +05:30
/*
* Notifier function for switching the muxes to safe parent
* while the hfpll is getting reprogrammed .
*/
static int krait_notifier_cb ( struct notifier_block * nb ,
unsigned long event ,
void * data )
{
int ret = 0 ;
struct krait_mux_clk * mux = container_of ( nb , struct krait_mux_clk ,
clk_nb ) ;
/* Switch to safe parent */
if ( event = = PRE_RATE_CHANGE ) {
mux - > old_index = krait_mux_clk_ops . get_parent ( & mux - > hw ) ;
ret = krait_mux_clk_ops . set_parent ( & mux - > hw , mux - > safe_sel ) ;
mux - > reparent = false ;
/*
* By the time POST_RATE_CHANGE notifier is called ,
* clk framework itself would have changed the parent for the new rate .
* Only otherwise , put back to the old parent .
*/
} else if ( event = = POST_RATE_CHANGE ) {
if ( ! mux - > reparent )
ret = krait_mux_clk_ops . set_parent ( & mux - > hw ,
mux - > old_index ) ;
}
return notifier_from_errno ( ret ) ;
}
static int krait_notifier_register ( struct device * dev , struct clk * clk ,
struct krait_mux_clk * mux )
{
int ret = 0 ;
mux - > clk_nb . notifier_call = krait_notifier_cb ;
2022-11-08 22:58:27 +01:00
ret = devm_clk_notifier_register ( dev , clk , & mux - > clk_nb ) ;
2018-08-14 17:42:31 +05:30
if ( ret )
dev_err ( dev , " failed to register clock notifier: %d \n " , ret ) ;
return ret ;
}
2022-11-09 01:56:31 +01:00
static struct clk_hw *
2018-08-14 17:42:29 +05:30
krait_add_div ( struct device * dev , int id , const char * s , unsigned int offset )
{
struct krait_div2_clk * div ;
2022-11-09 01:56:31 +01:00
static struct clk_parent_data p_data [ 1 ] ;
2018-08-14 17:42:29 +05:30
struct clk_init_data init = {
2022-11-09 01:56:31 +01:00
. num_parents = ARRAY_SIZE ( p_data ) ,
2018-08-14 17:42:29 +05:30
. ops = & krait_div2_clk_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ;
2022-11-09 01:56:31 +01:00
struct clk_hw * clk ;
char * parent_name ;
2022-11-09 01:56:30 +01:00
int cpu , ret ;
2018-08-14 17:42:29 +05:30
div = devm_kzalloc ( dev , sizeof ( * div ) , GFP_KERNEL ) ;
if ( ! div )
2022-11-09 01:56:31 +01:00
return ERR_PTR ( - ENOMEM ) ;
2018-08-14 17:42:29 +05:30
div - > width = 2 ;
div - > shift = 6 ;
div - > lpl = id > = 0 ;
div - > offset = offset ;
div - > hw . init = & init ;
init . name = kasprintf ( GFP_KERNEL , " hfpll%s_div " , s ) ;
if ( ! init . name )
2022-11-09 01:56:31 +01:00
return ERR_PTR ( - ENOMEM ) ;
2018-08-14 17:42:29 +05:30
2022-11-09 01:56:31 +01:00
init . parent_data = p_data ;
parent_name = kasprintf ( GFP_KERNEL , " hfpll%s " , s ) ;
if ( ! parent_name ) {
clk = ERR_PTR ( - ENOMEM ) ;
goto err_parent_name ;
2018-08-14 17:42:29 +05:30
}
2022-11-09 01:56:31 +01:00
p_data [ 0 ] . fw_name = parent_name ;
p_data [ 0 ] . name = parent_name ;
2022-11-09 01:56:30 +01:00
ret = devm_clk_hw_register ( dev , & div - > hw ) ;
2022-11-09 01:56:31 +01:00
if ( ret ) {
clk = ERR_PTR ( ret ) ;
goto err_clk ;
}
clk = & div - > hw ;
2022-11-09 01:56:28 +01:00
/* clk-krait ignore any rate change if mux is not flagged as enabled */
if ( id < 0 )
for_each_online_cpu ( cpu )
clk_prepare_enable ( div - > hw . clk ) ;
else
clk_prepare_enable ( div - > hw . clk ) ;
2022-11-09 01:56:31 +01:00
err_clk :
kfree ( parent_name ) ;
err_parent_name :
2018-08-14 17:42:29 +05:30
kfree ( init . name ) ;
2022-11-09 01:56:31 +01:00
return clk ;
2018-08-14 17:42:29 +05:30
}
2022-11-09 01:56:31 +01:00
static struct clk_hw *
2018-08-14 17:42:29 +05:30
krait_add_sec_mux ( struct device * dev , int id , const char * s ,
unsigned int offset , bool unique_aux )
{
2022-11-09 01:56:28 +01:00
int cpu , ret ;
2018-08-14 17:42:29 +05:30
struct krait_mux_clk * mux ;
2022-11-09 01:56:31 +01:00
static struct clk_parent_data sec_mux_list [ 2 ] = {
{ . name = " qsb " , . fw_name = " qsb " } ,
{ } ,
2018-08-14 17:42:29 +05:30
} ;
struct clk_init_data init = {
2022-11-09 01:56:31 +01:00
. parent_data = sec_mux_list ,
2018-08-14 17:42:29 +05:30
. num_parents = ARRAY_SIZE ( sec_mux_list ) ,
. ops = & krait_mux_clk_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ;
2022-11-09 01:56:31 +01:00
struct clk_hw * clk ;
char * parent_name ;
2018-08-14 17:42:29 +05:30
mux = devm_kzalloc ( dev , sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux )
2022-11-09 01:56:31 +01:00
return ERR_PTR ( - ENOMEM ) ;
2018-08-14 17:42:29 +05:30
mux - > offset = offset ;
mux - > lpl = id > = 0 ;
mux - > mask = 0x3 ;
mux - > shift = 2 ;
mux - > parent_map = sec_mux_map ;
mux - > hw . init = & init ;
2018-08-14 17:42:31 +05:30
mux - > safe_sel = 0 ;
2018-08-14 17:42:29 +05:30
2022-04-30 07:44:58 +02:00
/* Checking for qcom,krait-cc-v1 or qcom,krait-cc-v2 is not
* enough to limit this to apq / ipq8064 . Directly check machine
* compatible to correctly handle this errata .
*/
if ( of_machine_is_compatible ( " qcom,ipq8064 " ) | |
of_machine_is_compatible ( " qcom,apq8064 " ) )
mux - > disable_sec_src_gating = true ;
2018-08-14 17:42:29 +05:30
init . name = kasprintf ( GFP_KERNEL , " krait%s_sec_mux " , s ) ;
if ( ! init . name )
2022-11-09 01:56:31 +01:00
return ERR_PTR ( - ENOMEM ) ;
2018-08-14 17:42:29 +05:30
if ( unique_aux ) {
2022-11-09 01:56:31 +01:00
parent_name = kasprintf ( GFP_KERNEL , " acpu%s_aux " , s ) ;
if ( ! parent_name ) {
clk = ERR_PTR ( - ENOMEM ) ;
2018-08-14 17:42:29 +05:30
goto err_aux ;
}
2022-11-09 01:56:31 +01:00
sec_mux_list [ 1 ] . fw_name = parent_name ;
sec_mux_list [ 1 ] . name = parent_name ;
} else {
sec_mux_list [ 1 ] . name = " apu_aux " ;
2018-08-14 17:42:29 +05:30
}
2022-11-09 01:56:30 +01:00
ret = devm_clk_hw_register ( dev , & mux - > hw ) ;
2022-11-09 01:56:31 +01:00
if ( ret ) {
clk = ERR_PTR ( ret ) ;
goto err_clk ;
}
clk = & mux - > hw ;
2018-08-14 17:42:29 +05:30
2022-11-09 01:56:30 +01:00
ret = krait_notifier_register ( dev , mux - > hw . clk , mux ) ;
2022-11-09 01:56:31 +01:00
if ( ret ) {
clk = ERR_PTR ( ret ) ;
goto err_clk ;
}
2018-08-14 17:42:31 +05:30
2022-11-09 01:56:28 +01:00
/* clk-krait ignore any rate change if mux is not flagged as enabled */
if ( id < 0 )
for_each_online_cpu ( cpu )
clk_prepare_enable ( mux - > hw . clk ) ;
else
clk_prepare_enable ( mux - > hw . clk ) ;
2022-11-09 01:56:31 +01:00
err_clk :
2018-08-14 17:42:29 +05:30
if ( unique_aux )
2022-11-09 01:56:31 +01:00
kfree ( parent_name ) ;
2018-08-14 17:42:29 +05:30
err_aux :
kfree ( init . name ) ;
2022-11-09 01:56:31 +01:00
return clk ;
2018-08-14 17:42:29 +05:30
}
2022-11-09 01:56:31 +01:00
static struct clk_hw *
krait_add_pri_mux ( struct device * dev , struct clk_hw * hfpll_div , struct clk_hw * sec_mux ,
int id , const char * s , unsigned int offset )
2018-08-14 17:42:29 +05:30
{
2018-08-14 17:42:31 +05:30
int ret ;
2018-08-14 17:42:29 +05:30
struct krait_mux_clk * mux ;
2022-11-09 01:56:31 +01:00
static struct clk_parent_data p_data [ 3 ] ;
2018-08-14 17:42:29 +05:30
struct clk_init_data init = {
2022-11-09 01:56:31 +01:00
. parent_data = p_data ,
. num_parents = ARRAY_SIZE ( p_data ) ,
2018-08-14 17:42:29 +05:30
. ops = & krait_mux_clk_ops ,
. flags = CLK_SET_RATE_PARENT ,
} ;
2022-11-09 01:56:31 +01:00
struct clk_hw * clk ;
char * hfpll_name ;
2018-08-14 17:42:29 +05:30
mux = devm_kzalloc ( dev , sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux )
return ERR_PTR ( - ENOMEM ) ;
mux - > mask = 0x3 ;
mux - > shift = 0 ;
mux - > offset = offset ;
mux - > lpl = id > = 0 ;
mux - > parent_map = pri_mux_map ;
mux - > hw . init = & init ;
2018-08-14 17:42:31 +05:30
mux - > safe_sel = 2 ;
2018-08-14 17:42:29 +05:30
init . name = kasprintf ( GFP_KERNEL , " krait%s_pri_mux " , s ) ;
if ( ! init . name )
return ERR_PTR ( - ENOMEM ) ;
2022-11-09 01:56:31 +01:00
hfpll_name = kasprintf ( GFP_KERNEL , " hfpll%s " , s ) ;
if ( ! hfpll_name ) {
2018-08-14 17:42:29 +05:30
clk = ERR_PTR ( - ENOMEM ) ;
2022-11-09 01:56:31 +01:00
goto err_hfpll ;
2018-08-14 17:42:29 +05:30
}
2022-11-09 01:56:31 +01:00
p_data [ 0 ] . fw_name = hfpll_name ;
p_data [ 0 ] . name = hfpll_name ;
2018-08-14 17:42:29 +05:30
2022-11-09 01:56:31 +01:00
p_data [ 1 ] . hw = hfpll_div ;
p_data [ 2 ] . hw = sec_mux ;
2018-08-14 17:42:29 +05:30
2022-11-09 01:56:30 +01:00
ret = devm_clk_hw_register ( dev , & mux - > hw ) ;
if ( ret ) {
clk = ERR_PTR ( ret ) ;
2022-11-09 01:56:31 +01:00
goto err_clk ;
2022-11-09 01:56:30 +01:00
}
2022-11-09 01:56:31 +01:00
clk = & mux - > hw ;
2018-08-14 17:42:29 +05:30
2022-11-09 01:56:31 +01:00
ret = krait_notifier_register ( dev , mux - > hw . clk , mux ) ;
2018-08-14 17:42:31 +05:30
if ( ret )
2022-11-09 01:56:30 +01:00
clk = ERR_PTR ( ret ) ;
2022-11-09 01:56:31 +01:00
err_clk :
kfree ( hfpll_name ) ;
err_hfpll :
2018-08-14 17:42:29 +05:30
kfree ( init . name ) ;
return clk ;
}
/* id < 0 for L2, otherwise id == physical CPU number */
2022-11-09 01:56:31 +01:00
static struct clk_hw * krait_add_clks ( struct device * dev , int id , bool unique_aux )
2018-08-14 17:42:29 +05:30
{
2022-11-09 01:56:31 +01:00
struct clk_hw * hfpll_div , * sec_mux , * pri_mux ;
2018-08-14 17:42:29 +05:30
unsigned int offset ;
void * p = NULL ;
const char * s ;
if ( id > = 0 ) {
offset = 0x4501 + ( 0x1000 * id ) ;
s = p = kasprintf ( GFP_KERNEL , " %d " , id ) ;
if ( ! s )
return ERR_PTR ( - ENOMEM ) ;
} else {
offset = 0x500 ;
s = " _l2 " ;
}
2022-11-09 01:56:31 +01:00
hfpll_div = krait_add_div ( dev , id , s , offset ) ;
if ( IS_ERR ( hfpll_div ) ) {
pri_mux = hfpll_div ;
2018-08-14 17:42:29 +05:30
goto err ;
}
2022-11-09 01:56:31 +01:00
sec_mux = krait_add_sec_mux ( dev , id , s , offset , unique_aux ) ;
if ( IS_ERR ( sec_mux ) ) {
pri_mux = sec_mux ;
2018-08-14 17:42:29 +05:30
goto err ;
}
2022-11-09 01:56:31 +01:00
pri_mux = krait_add_pri_mux ( dev , hfpll_div , sec_mux , id , s , offset ) ;
2018-08-14 17:42:29 +05:30
err :
kfree ( p ) ;
2022-11-09 01:56:31 +01:00
return pri_mux ;
2018-08-14 17:42:29 +05:30
}
static struct clk * krait_of_get ( struct of_phandle_args * clkspec , void * data )
{
unsigned int idx = clkspec - > args [ 0 ] ;
struct clk * * clks = data ;
2022-11-09 01:56:31 +01:00
if ( idx > = clks_max ) {
2018-08-14 17:42:29 +05:30
pr_err ( " %s: invalid clock index %d \n " , __func__ , idx ) ;
return ERR_PTR ( - EINVAL ) ;
}
return clks [ idx ] ? : ERR_PTR ( - ENODEV ) ;
}
static const struct of_device_id krait_cc_match_table [ ] = {
{ . compatible = " qcom,krait-cc-v1 " , ( void * ) 1UL } ,
{ . compatible = " qcom,krait-cc-v2 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , krait_cc_match_table ) ;
static int krait_cc_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
const struct of_device_id * id ;
unsigned long cur_rate , aux_rate ;
int cpu ;
2022-11-09 01:56:31 +01:00
struct clk_hw * mux , * l2_pri_mux ;
struct clk * clk , * * clks ;
2018-08-14 17:42:29 +05:30
id = of_match_device ( krait_cc_match_table , dev ) ;
if ( ! id )
return - ENODEV ;
/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
clk = clk_register_fixed_rate ( dev , " qsb " , NULL , 0 , 1 ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
if ( ! id - > data ) {
clk = clk_register_fixed_factor ( dev , " acpu_aux " ,
" gpll0_vote " , 0 , 1 , 2 ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
}
/* Krait configurations have at most 4 CPUs and one L2 */
2022-11-09 01:56:31 +01:00
clks = devm_kcalloc ( dev , clks_max , sizeof ( * clks ) , GFP_KERNEL ) ;
2018-08-14 17:42:29 +05:30
if ( ! clks )
return - ENOMEM ;
for_each_possible_cpu ( cpu ) {
2022-11-09 01:56:31 +01:00
mux = krait_add_clks ( dev , cpu , id - > data ) ;
2023-01-04 16:02:35 +08:00
if ( IS_ERR ( mux ) )
return PTR_ERR ( mux ) ;
2022-11-09 01:56:31 +01:00
clks [ cpu ] = mux - > clk ;
2018-08-14 17:42:29 +05:30
}
2022-11-09 01:56:31 +01:00
l2_pri_mux = krait_add_clks ( dev , - 1 , id - > data ) ;
if ( IS_ERR ( l2_pri_mux ) )
return PTR_ERR ( l2_pri_mux ) ;
clks [ l2_mux ] = l2_pri_mux - > clk ;
2018-08-14 17:42:29 +05:30
/*
* We don ' t want the CPU or L2 clocks to be turned off at late init
* if CPUFREQ or HOTPLUG configs are disabled . So , bump up the
* refcount of these clocks . Any cpufreq / hotplug manager can assume
* that the clocks have already been prepared and enabled by the time
* they take over .
*/
for_each_online_cpu ( cpu ) {
2022-11-09 01:56:31 +01:00
clk_prepare_enable ( clks [ l2_mux ] ) ;
2018-08-14 17:42:29 +05:30
WARN ( clk_prepare_enable ( clks [ cpu ] ) ,
" Unable to turn on CPU%d clock " , cpu ) ;
}
/*
* Force reinit of HFPLLs and muxes to overwrite any potential
* incorrect configuration of HFPLLs and muxes by the bootloader .
* While at it , also make sure the cores are running at known rates
* and print the current rate .
*
* The clocks are set to aux clock rate first to make sure the
* secondary mux is not sourcing off of QSB . The rate is then set to
* two different rates to force a HFPLL reinit under all
* circumstances .
*/
2022-11-09 01:56:31 +01:00
cur_rate = clk_get_rate ( clks [ l2_mux ] ) ;
2018-08-14 17:42:29 +05:30
aux_rate = 384000000 ;
2022-11-09 01:56:29 +01:00
if ( cur_rate < aux_rate ) {
pr_info ( " L2 @ Undefined rate. Forcing new rate. \n " ) ;
2018-08-14 17:42:29 +05:30
cur_rate = aux_rate ;
}
2022-11-09 01:56:31 +01:00
clk_set_rate ( clks [ l2_mux ] , aux_rate ) ;
clk_set_rate ( clks [ l2_mux ] , 2 ) ;
clk_set_rate ( clks [ l2_mux ] , cur_rate ) ;
pr_info ( " L2 @ %lu KHz \n " , clk_get_rate ( clks [ l2_mux ] ) / 1000 ) ;
2018-08-14 17:42:29 +05:30
for_each_possible_cpu ( cpu ) {
clk = clks [ cpu ] ;
cur_rate = clk_get_rate ( clk ) ;
2022-11-09 01:56:29 +01:00
if ( cur_rate < aux_rate ) {
pr_info ( " CPU%d @ Undefined rate. Forcing new rate. \n " , cpu ) ;
2018-08-14 17:42:29 +05:30
cur_rate = aux_rate ;
}
clk_set_rate ( clk , aux_rate ) ;
clk_set_rate ( clk , 2 ) ;
clk_set_rate ( clk , cur_rate ) ;
pr_info ( " CPU%d @ %lu KHz \n " , cpu , clk_get_rate ( clk ) / 1000 ) ;
}
of_clk_add_provider ( dev - > of_node , krait_of_get , clks ) ;
return 0 ;
}
static struct platform_driver krait_cc_driver = {
. probe = krait_cc_probe ,
. driver = {
. name = " krait-cc " ,
. of_match_table = krait_cc_match_table ,
} ,
} ;
module_platform_driver ( krait_cc_driver ) ;
MODULE_DESCRIPTION ( " Krait CPU Clock Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:krait-cc " ) ;