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
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/clk.h>
# 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>
2015-04-11 01:17:11 +03:00
# if !defined(CONFIG_ARM)
2015-03-04 14:56:20 +03:00
# include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */
2015-04-11 01:17:11 +03:00
# endif
2015-03-04 14:56:20 +03:00
2013-06-05 13:30:48 +04:00
/**
2015-03-13 07:39:01 +03:00
* struct cpu_data
2013-06-05 13:30:48 +04:00
* @ parent : the parent node of cpu clock
* @ table : frequency table
*/
struct cpu_data {
struct device_node * parent ;
struct cpufreq_frequency_table * table ;
} ;
/**
* struct soc_data - SoC specific data
* @ freq_mask : mask the disallowed frequencies
* @ flag : unique flags
*/
struct soc_data {
u32 freq_mask [ 4 ] ;
u32 flag ;
} ;
# define FREQ_MASK 1
/* see hardware specification for the allowed frqeuencies */
static const struct soc_data sdata [ ] = {
{ /* used by p2041 and p3041 */
. freq_mask = { 0x8 , 0x8 , 0x2 , 0x2 } ,
. flag = FREQ_MASK ,
} ,
{ /* used by p5020 */
. freq_mask = { 0x8 , 0x2 } ,
. flag = FREQ_MASK ,
} ,
{ /* used by p4080, p5040 */
. freq_mask = { 0 } ,
. flag = 0 ,
} ,
} ;
/*
* the minimum allowed core frequency , in Hz
* for chassis v1 .0 , > = platform frequency
* for chassis v2 .0 , > = platform frequency / 2
*/
static u32 min_cpufreq ;
static const u32 * fmask ;
2015-03-13 07:39:01 +03:00
# if defined(CONFIG_ARM)
static int get_cpu_physical_id ( int cpu )
{
return topology_core_id ( cpu ) ;
}
# else
static int get_cpu_physical_id ( int cpu )
{
return get_hard_smp_processor_id ( cpu ) ;
}
# endif
static u32 get_bus_freq ( void )
{
struct device_node * soc ;
u32 sysfreq ;
soc = of_find_node_by_type ( NULL , " soc " ) ;
if ( ! soc )
return 0 ;
if ( of_property_read_u32 ( soc , " bus-frequency " , & sysfreq ) )
sysfreq = 0 ;
of_node_put ( soc ) ;
2013-06-05 13:30:48 +04:00
2015-03-13 07:39:01 +03:00
return sysfreq ;
}
2013-06-05 13:30:48 +04:00
2015-03-13 07:39:01 +03:00
static struct device_node * cpu_to_clk_node ( int cpu )
2013-06-05 13:30:48 +04:00
{
2015-03-13 07:39:01 +03:00
struct device_node * np , * clk_np ;
if ( ! cpu_present ( cpu ) )
return NULL ;
np = of_get_cpu_node ( cpu , NULL ) ;
if ( ! np )
return NULL ;
clk_np = of_parse_phandle ( np , " clocks " , 0 ) ;
if ( ! clk_np )
return NULL ;
of_node_put ( np ) ;
return clk_np ;
}
/* traverse cpu nodes to get cpu mask of sharing clock wire */
static void set_affected_cpus ( struct cpufreq_policy * policy )
{
struct device_node * np , * clk_np ;
struct cpumask * dstp = policy - > cpus ;
int i ;
np = cpu_to_clk_node ( policy - > cpu ) ;
if ( ! np )
return ;
for_each_present_cpu ( i ) {
clk_np = cpu_to_clk_node ( i ) ;
if ( ! clk_np )
continue ;
if ( clk_np = = np )
cpumask_set_cpu ( i , dstp ) ;
of_node_put ( clk_np ) ;
}
of_node_put ( np ) ;
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
{
struct device_node * np ;
int i , count , ret ;
u32 freq , mask ;
struct clk * clk ;
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 ;
}
data - > parent = of_parse_phandle ( np , " clocks " , 0 ) ;
if ( ! data - > parent ) {
pr_err ( " %s: could not get clock information \n " , __func__ ) ;
goto err_nomem2 ;
}
count = of_property_count_strings ( data - > parent , " clock-names " ) ;
table = kcalloc ( count + 1 , sizeof ( * table ) , GFP_KERNEL ) ;
if ( ! table ) {
pr_err ( " %s: no memory \n " , __func__ ) ;
goto err_node ;
}
if ( fmask )
2015-03-13 07:39:01 +03:00
mask = fmask [ get_cpu_physical_id ( cpu ) ] ;
2013-06-05 13:30:48 +04:00
else
mask = 0x0 ;
for ( i = 0 ; i < count ; i + + ) {
clk = of_clk_get ( data - > parent , i ) ;
freq = clk_get_rate ( clk ) ;
/*
* the clock is valid if its frequency is not masked
* and large than minimum allowed frequency .
*/
if ( freq < min_cpufreq | | ( mask & ( 1 < < i ) ) )
table [ i ] . frequency = CPUFREQ_ENTRY_INVALID ;
else
table [ i ] . frequency = freq / 1000 ;
table [ i ] . driver_data = i ;
}
freq_table_redup ( table , count ) ;
freq_table_sort ( table , count ) ;
table [ i ] . frequency = CPUFREQ_TABLE_END ;
/* set the min and max frequency properly */
2013-09-16 17:26:28 +04:00
ret = cpufreq_table_validate_and_show ( policy , table ) ;
2013-06-05 13:30:48 +04:00
if ( ret ) {
pr_err ( " invalid frequency table: %d \n " , ret ) ;
goto err_nomem1 ;
}
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 ;
err_nomem1 :
kfree ( table ) ;
err_node :
of_node_put ( data - > parent ) ;
err_nomem2 :
2015-03-13 07:39:01 +03:00
policy - > driver_data = NULL ;
2013-06-05 13:30:48 +04:00
kfree ( data ) ;
err_np :
of_node_put ( np ) ;
return - ENODEV ;
}
2015-03-13 07:39:01 +03:00
static int __exit 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
of_node_put ( data - > parent ) ;
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
2013-10-25 18:15:48 +04:00
parent = of_clk_get ( data - > parent , 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 " ,
2013-06-05 13:30:48 +04:00
. flags = CPUFREQ_CONST_LOOPS ,
2015-03-13 07:39:01 +03:00
. init = qoriq_cpufreq_cpu_init ,
. exit = __exit_p ( 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
} ;
2015-03-13 07:39:01 +03:00
static const struct of_device_id node_matches [ ] __initconst = {
2013-06-05 13:30:48 +04:00
{ . compatible = " fsl,p2041-clockgen " , . data = & sdata [ 0 ] , } ,
{ . compatible = " fsl,p3041-clockgen " , . data = & sdata [ 0 ] , } ,
{ . compatible = " fsl,p5020-clockgen " , . data = & sdata [ 1 ] , } ,
{ . compatible = " fsl,p4080-clockgen " , . data = & sdata [ 2 ] , } ,
{ . compatible = " fsl,p5040-clockgen " , . data = & sdata [ 2 ] , } ,
{ . compatible = " fsl,qoriq-clockgen-2.0 " , } ,
{ }
} ;
2015-03-13 07:39:01 +03:00
static int __init qoriq_cpufreq_init ( void )
2013-06-05 13:30:48 +04:00
{
int ret ;
struct device_node * np ;
const struct of_device_id * match ;
const struct soc_data * data ;
np = of_find_matching_node ( NULL , node_matches ) ;
if ( ! np )
return - ENODEV ;
match = of_match_node ( node_matches , np ) ;
data = match - > data ;
if ( data ) {
if ( data - > flag )
fmask = data - > freq_mask ;
2015-03-13 07:39:01 +03:00
min_cpufreq = get_bus_freq ( ) ;
2013-06-05 13:30:48 +04:00
} else {
2015-03-13 07:39:01 +03:00
min_cpufreq = get_bus_freq ( ) / 2 ;
2013-06-05 13:30:48 +04:00
}
of_node_put ( np ) ;
2015-03-13 07:39:01 +03:00
ret = cpufreq_register_driver ( & qoriq_cpufreq_driver ) ;
2013-06-05 13:30:48 +04:00
if ( ! ret )
2015-03-13 07:39:01 +03:00
pr_info ( " Freescale QorIQ CPU frequency scaling driver \n " ) ;
2013-06-05 13:30:48 +04:00
return ret ;
}
2015-03-13 07:39:01 +03:00
module_init ( qoriq_cpufreq_init ) ;
2013-06-05 13:30:48 +04:00
2015-03-13 07:39:01 +03:00
static void __exit qoriq_cpufreq_exit ( void )
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
}
2015-03-13 07:39:01 +03:00
module_exit ( qoriq_cpufreq_exit ) ;
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 " ) ;