2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-06-05 13:30:48 +04:00
/*
* Copyright 2013 Freescale Semiconductor , Inc .
*
2015-03-13 07:39:01 +03:00
* CPU Frequency Scaling driver for Freescale QorIQ SoCs .
2013-06-05 13:30:48 +04:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/clk.h>
2017-02-09 05:33:02 +03:00
# include <linux/clk-provider.h>
2013-06-05 13:30:48 +04:00
# include <linux/cpufreq.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/slab.h>
# include <linux/smp.h>
2020-04-21 11:29:59 +03:00
# include <linux/platform_device.h>
2013-06-05 13:30:48 +04:00
/**
2015-03-13 07:39:01 +03:00
* struct cpu_data
2015-06-04 09:25:42 +03:00
* @ pclk : the parent clock of cpu
2013-06-05 13:30:48 +04:00
* @ table : frequency table
*/
struct cpu_data {
2015-06-04 09:25:42 +03:00
struct clk * * pclk ;
2013-06-05 13:30:48 +04:00
struct cpufreq_frequency_table * table ;
} ;
/**
* struct soc_data - SoC specific data
2017-02-09 05:33:02 +03:00
* @ flags : SOC_xxx
2013-06-05 13:30:48 +04:00
*/
struct soc_data {
2017-02-09 05:33:02 +03:00
u32 flags ;
2013-06-05 13:30:48 +04:00
} ;
2015-03-13 07:39:01 +03:00
static u32 get_bus_freq ( void )
{
struct device_node * soc ;
u32 sysfreq ;
2017-03-10 04:28:43 +03:00
struct clk * pltclk ;
int ret ;
2015-03-13 07:39:01 +03:00
2017-03-10 04:28:43 +03:00
/* get platform freq by searching bus-frequency property */
2015-03-13 07:39:01 +03:00
soc = of_find_node_by_type ( NULL , " soc " ) ;
2017-03-10 04:28:43 +03:00
if ( soc ) {
ret = of_property_read_u32 ( soc , " bus-frequency " , & sysfreq ) ;
of_node_put ( soc ) ;
if ( ! ret )
return sysfreq ;
}
2015-03-13 07:39:01 +03:00
2017-03-10 04:28:43 +03:00
/* get platform freq by its clock name */
pltclk = clk_get ( NULL , " cg-pll0-div1 " ) ;
if ( IS_ERR ( pltclk ) ) {
pr_err ( " %s: can't get bus frequency %ld \n " ,
__func__ , PTR_ERR ( pltclk ) ) ;
return PTR_ERR ( pltclk ) ;
}
2013-06-05 13:30:48 +04:00
2017-03-10 04:28:43 +03:00
return clk_get_rate ( pltclk ) ;
2015-03-13 07:39:01 +03:00
}
2013-06-05 13:30:48 +04:00
2017-02-09 05:33:02 +03:00
static struct clk * cpu_to_clk ( int cpu )
2013-06-05 13:30:48 +04:00
{
2017-02-09 05:33:02 +03:00
struct device_node * np ;
struct clk * clk ;
2015-03-13 07:39:01 +03:00
if ( ! cpu_present ( cpu ) )
return NULL ;
np = of_get_cpu_node ( cpu , NULL ) ;
if ( ! np )
return NULL ;
2017-02-09 05:33:02 +03:00
clk = of_clk_get ( np , 0 ) ;
2015-03-13 07:39:01 +03:00
of_node_put ( np ) ;
2017-02-09 05:33:02 +03:00
return clk ;
2015-03-13 07:39:01 +03:00
}
/* traverse cpu nodes to get cpu mask of sharing clock wire */
static void set_affected_cpus ( struct cpufreq_policy * policy )
{
struct cpumask * dstp = policy - > cpus ;
2017-02-09 05:33:02 +03:00
struct clk * clk ;
2015-03-13 07:39:01 +03:00
int i ;
for_each_present_cpu ( i ) {
2017-02-09 05:33:02 +03:00
clk = cpu_to_clk ( i ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: no clock for cpu %d \n " , __func__ , i ) ;
2015-03-13 07:39:01 +03:00
continue ;
2017-02-09 05:33:02 +03:00
}
2015-03-13 07:39:01 +03:00
2017-02-09 05:33:02 +03:00
if ( clk_is_match ( policy - > clk , clk ) )
2015-03-13 07:39:01 +03:00
cpumask_set_cpu ( i , dstp ) ;
}
2013-06-05 13:30:48 +04:00
}
/* reduce the duplicated frequencies in frequency table */
static void freq_table_redup ( struct cpufreq_frequency_table * freq_table ,
int count )
{
int i , j ;
for ( i = 1 ; i < count ; i + + ) {
for ( j = 0 ; j < i ; j + + ) {
if ( freq_table [ j ] . frequency = = CPUFREQ_ENTRY_INVALID | |
freq_table [ j ] . frequency ! =
freq_table [ i ] . frequency )
continue ;
freq_table [ i ] . frequency = CPUFREQ_ENTRY_INVALID ;
break ;
}
}
}
/* sort the frequencies in frequency table in descenting order */
static void freq_table_sort ( struct cpufreq_frequency_table * freq_table ,
int count )
{
int i , j , ind ;
unsigned int freq , max_freq ;
struct cpufreq_frequency_table table ;
2015-03-13 07:39:01 +03:00
2013-06-05 13:30:48 +04:00
for ( i = 0 ; i < count - 1 ; i + + ) {
max_freq = freq_table [ i ] . frequency ;
ind = i ;
for ( j = i + 1 ; j < count ; j + + ) {
freq = freq_table [ j ] . frequency ;
if ( freq = = CPUFREQ_ENTRY_INVALID | |
freq < = max_freq )
continue ;
ind = j ;
max_freq = freq ;
}
if ( ind ! = i ) {
/* exchange the frequencies */
table . driver_data = freq_table [ i ] . driver_data ;
table . frequency = freq_table [ i ] . frequency ;
freq_table [ i ] . driver_data = freq_table [ ind ] . driver_data ;
freq_table [ i ] . frequency = freq_table [ ind ] . frequency ;
freq_table [ ind ] . driver_data = table . driver_data ;
freq_table [ ind ] . frequency = table . frequency ;
}
}
}
2015-03-13 07:39:01 +03:00
static int qoriq_cpufreq_cpu_init ( struct cpufreq_policy * policy )
2013-06-05 13:30:48 +04:00
{
2017-02-09 05:33:02 +03:00
struct device_node * np ;
2018-02-26 08:09:01 +03:00
int i , count ;
2017-02-09 05:33:02 +03:00
u32 freq ;
2013-06-05 13:30:48 +04:00
struct clk * clk ;
2017-02-09 05:33:02 +03:00
const struct clk_hw * hwclk ;
2013-06-05 13:30:48 +04:00
struct cpufreq_frequency_table * table ;
struct cpu_data * data ;
unsigned int cpu = policy - > cpu ;
2014-06-06 03:56:17 +04:00
u64 u64temp ;
2013-06-05 13:30:48 +04:00
np = of_get_cpu_node ( cpu , NULL ) ;
if ( ! np )
return - ENODEV ;
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2015-03-13 07:39:01 +03:00
if ( ! data )
2013-06-05 13:30:48 +04:00
goto err_np ;
2014-01-09 19:08:43 +04:00
policy - > clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( policy - > clk ) ) {
2013-06-05 13:30:48 +04:00
pr_err ( " %s: no clock information \n " , __func__ ) ;
goto err_nomem2 ;
}
2017-02-09 05:33:02 +03:00
hwclk = __clk_get_hw ( policy - > clk ) ;
count = clk_hw_get_num_parents ( hwclk ) ;
2013-06-05 13:30:48 +04:00
2015-06-04 09:25:42 +03:00
data - > pclk = kcalloc ( count , sizeof ( struct clk * ) , GFP_KERNEL ) ;
2018-02-15 20:00:37 +03:00
if ( ! data - > pclk )
2017-02-09 05:33:02 +03:00
goto err_nomem2 ;
2015-06-04 09:25:42 +03:00
2013-06-05 13:30:48 +04:00
table = kcalloc ( count + 1 , sizeof ( * table ) , GFP_KERNEL ) ;
2018-02-15 20:00:37 +03:00
if ( ! table )
2015-06-04 09:25:42 +03:00
goto err_pclk ;
2013-06-05 13:30:48 +04:00
for ( i = 0 ; i < count ; i + + ) {
2017-02-09 05:33:02 +03:00
clk = clk_hw_get_parent_by_index ( hwclk , i ) - > clk ;
2015-06-04 09:25:42 +03:00
data - > pclk [ i ] = clk ;
2013-06-05 13:30:48 +04:00
freq = clk_get_rate ( clk ) ;
2017-02-09 05:33:02 +03:00
table [ i ] . frequency = freq / 1000 ;
2013-06-05 13:30:48 +04:00
table [ i ] . driver_data = i ;
}
freq_table_redup ( table , count ) ;
freq_table_sort ( table , count ) ;
table [ i ] . frequency = CPUFREQ_TABLE_END ;
2018-02-26 08:09:01 +03:00
policy - > freq_table = table ;
2013-06-05 13:30:48 +04:00
data - > table = table ;
/* update ->cpus if we have cluster, no harm if not */
2015-03-13 07:39:01 +03:00
set_affected_cpus ( policy ) ;
policy - > driver_data = data ;
2013-06-05 13:30:48 +04:00
2014-06-06 03:56:17 +04:00
/* Minimum transition latency is 12 platform clocks */
u64temp = 12ULL * NSEC_PER_SEC ;
2015-03-13 07:39:01 +03:00
do_div ( u64temp , get_bus_freq ( ) ) ;
2014-06-06 03:56:17 +04:00
policy - > cpuinfo . transition_latency = u64temp + 1 ;
2014-04-28 20:18:18 +04:00
2013-06-05 13:30:48 +04:00
of_node_put ( np ) ;
return 0 ;
2015-06-04 09:25:42 +03:00
err_pclk :
kfree ( data - > pclk ) ;
2013-06-05 13:30:48 +04:00
err_nomem2 :
kfree ( data ) ;
err_np :
of_node_put ( np ) ;
return - ENODEV ;
}
2016-04-19 12:00:06 +03:00
static int qoriq_cpufreq_cpu_exit ( struct cpufreq_policy * policy )
2013-06-05 13:30:48 +04:00
{
2015-03-13 07:39:01 +03:00
struct cpu_data * data = policy - > driver_data ;
2013-06-05 13:30:48 +04:00
2015-06-04 09:25:42 +03:00
kfree ( data - > pclk ) ;
2013-06-05 13:30:48 +04:00
kfree ( data - > table ) ;
kfree ( data ) ;
2015-03-13 07:39:01 +03:00
policy - > driver_data = NULL ;
2013-06-05 13:30:48 +04:00
return 0 ;
}
2015-03-13 07:39:01 +03:00
static int qoriq_cpufreq_target ( struct cpufreq_policy * policy ,
2013-10-25 18:15:48 +04:00
unsigned int index )
2013-06-05 13:30:48 +04:00
{
struct clk * parent ;
2015-03-13 07:39:01 +03:00
struct cpu_data * data = policy - > driver_data ;
2013-06-05 13:30:48 +04:00
2015-06-04 09:25:42 +03:00
parent = data - > pclk [ data - > table [ index ] . driver_data ] ;
2014-01-09 19:08:43 +04:00
return clk_set_parent ( policy - > clk , parent ) ;
2013-06-05 13:30:48 +04:00
}
2015-03-13 07:39:01 +03:00
static struct cpufreq_driver qoriq_cpufreq_driver = {
. name = " qoriq_cpufreq " ,
2019-01-29 07:55:13 +03:00
. flags = CPUFREQ_CONST_LOOPS |
CPUFREQ_IS_COOLING_DEV ,
2015-03-13 07:39:01 +03:00
. init = qoriq_cpufreq_cpu_init ,
2016-04-19 12:00:06 +03:00
. exit = qoriq_cpufreq_cpu_exit ,
2013-10-03 18:58:18 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2015-03-13 07:39:01 +03:00
. target_index = qoriq_cpufreq_target ,
2014-01-09 19:08:43 +04:00
. get = cpufreq_generic_get ,
2013-10-03 18:58:18 +04:00
. attr = cpufreq_generic_attr ,
2013-06-05 13:30:48 +04:00
} ;
2020-04-21 11:29:59 +03:00
static const struct of_device_id qoriq_cpufreq_blacklist [ ] = {
2017-02-09 05:33:02 +03:00
/* e6500 cannot use cpufreq due to erratum A-008083 */
2020-04-21 11:29:59 +03:00
{ . compatible = " fsl,b4420-clockgen " , } ,
{ . compatible = " fsl,b4860-clockgen " , } ,
{ . compatible = " fsl,t2080-clockgen " , } ,
{ . compatible = " fsl,t4240-clockgen " , } ,
2013-06-05 13:30:48 +04:00
{ }
} ;
2020-04-21 11:29:59 +03:00
static int qoriq_cpufreq_probe ( struct platform_device * pdev )
2013-06-05 13:30:48 +04:00
{
int ret ;
2020-04-21 11:29:59 +03:00
struct device_node * np ;
2013-06-05 13:30:48 +04:00
2020-04-21 11:29:59 +03:00
np = of_find_matching_node ( NULL , qoriq_cpufreq_blacklist ) ;
if ( np ) {
2022-06-15 12:48:07 +03:00
of_node_put ( np ) ;
2020-04-21 11:29:59 +03:00
dev_info ( & pdev - > dev , " Disabling due to erratum A-008083 " ) ;
2017-02-09 05:33:02 +03:00
return - ENODEV ;
2020-04-21 11:29:59 +03:00
}
2017-02-09 05:33:02 +03:00
2015-03-13 07:39:01 +03:00
ret = cpufreq_register_driver ( & qoriq_cpufreq_driver ) ;
2020-04-21 11:29:59 +03:00
if ( ret )
return ret ;
2013-06-05 13:30:48 +04:00
2020-04-21 11:29:59 +03:00
dev_info ( & pdev - > dev , " Freescale QorIQ CPU frequency scaling driver \n " ) ;
return 0 ;
2013-06-05 13:30:48 +04:00
}
2023-07-12 12:33:21 +03:00
static void qoriq_cpufreq_remove ( struct platform_device * pdev )
2013-06-05 13:30:48 +04:00
{
2015-03-13 07:39:01 +03:00
cpufreq_unregister_driver ( & qoriq_cpufreq_driver ) ;
2013-06-05 13:30:48 +04:00
}
2020-04-21 11:29:59 +03:00
static struct platform_driver qoriq_cpufreq_platform_driver = {
. driver = {
. name = " qoriq-cpufreq " ,
} ,
. probe = qoriq_cpufreq_probe ,
2023-07-12 12:33:21 +03:00
. remove_new = qoriq_cpufreq_remove ,
2020-04-21 11:29:59 +03:00
} ;
module_platform_driver ( qoriq_cpufreq_platform_driver ) ;
MODULE_ALIAS ( " platform:qoriq-cpufreq " ) ;
2013-06-05 13:30:48 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Tang Yuantian <Yuantian.Tang@freescale.com> " ) ;
2015-03-13 07:39:01 +03:00
MODULE_DESCRIPTION ( " cpufreq driver for Freescale QorIQ series SoCs " ) ;