2011-08-08 13:21:59 +01:00
/*
* arch / arm / kernel / topology . c
*
* Copyright ( C ) 2011 Linaro Limited .
* Written by : Vincent Guittot
*
* based on arch / sh / kernel / topology . c
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/cpu.h>
# include <linux/cpumask.h>
# include <linux/init.h>
# include <linux/percpu.h>
# include <linux/node.h>
# include <linux/nodemask.h>
2012-07-10 14:13:12 +01:00
# include <linux/of.h>
2011-08-08 13:21:59 +01:00
# include <linux/sched.h>
2012-07-10 14:13:12 +01:00
# include <linux/slab.h>
2011-08-08 13:21:59 +01:00
# include <asm/cputype.h>
# include <asm/topology.h>
2012-07-10 14:08:40 +01:00
/*
* cpu power scale management
*/
/*
* cpu power table
* This per cpu data structure describes the relative capacity of each core .
* On a heteregenous system , cores don ' t have the same computation capacity
* and we reflect that difference in the cpu_power field so the scheduler can
* take this difference into account during load balance . A per cpu structure
* is preferred because each CPU updates its own cpu_power field during the
* load balance except for idle cores . One idle core is selected to run the
* rebalance_domains for all idle cores and the cpu_power can be updated
* during this sequence .
*/
static DEFINE_PER_CPU ( unsigned long , cpu_scale ) ;
unsigned long arch_scale_freq_power ( struct sched_domain * sd , int cpu )
{
return per_cpu ( cpu_scale , cpu ) ;
}
static void set_power_scale ( unsigned int cpu , unsigned long power )
{
per_cpu ( cpu_scale , cpu ) = power ;
}
2012-07-10 14:13:12 +01:00
# ifdef CONFIG_OF
struct cpu_efficiency {
const char * compatible ;
unsigned long efficiency ;
} ;
/*
* Table of relative efficiency of each processors
* The efficiency value must fit in 20 bit and the final
* cpu_scale value must be in the range
* 0 < cpu_scale < 3 * SCHED_POWER_SCALE / 2
* in order to return at most 1 when DIV_ROUND_CLOSEST
* is used to compute the capacity of a CPU .
* Processors that are not defined in the table ,
* use the default SCHED_POWER_SCALE value for cpu_scale .
*/
struct cpu_efficiency table_efficiency [ ] = {
{ " arm,cortex-a15 " , 3891 } ,
{ " arm,cortex-a7 " , 2048 } ,
{ NULL , } ,
} ;
struct cpu_capacity {
unsigned long hwid ;
unsigned long capacity ;
} ;
struct cpu_capacity * cpu_capacity ;
unsigned long middle_capacity = 1 ;
/*
* Iterate all CPUs ' descriptor in DT and compute the efficiency
* ( as per table_efficiency ) . Also calculate a middle efficiency
* as close as possible to ( max { eff_i } - min { eff_i } ) / 2
* This is later used to scale the cpu_power field such that an
* ' average ' CPU is of middle power . Also see the comments near
* table_efficiency [ ] and update_cpu_power ( ) .
*/
static void __init parse_dt_topology ( void )
{
struct cpu_efficiency * cpu_eff ;
struct device_node * cn = NULL ;
unsigned long min_capacity = ( unsigned long ) ( - 1 ) ;
unsigned long max_capacity = 0 ;
unsigned long capacity = 0 ;
int alloc_size , cpu = 0 ;
alloc_size = nr_cpu_ids * sizeof ( struct cpu_capacity ) ;
cpu_capacity = ( struct cpu_capacity * ) kzalloc ( alloc_size , GFP_NOWAIT ) ;
while ( ( cn = of_find_node_by_type ( cn , " cpu " ) ) ) {
const u32 * rate , * reg ;
int len ;
if ( cpu > = num_possible_cpus ( ) )
break ;
for ( cpu_eff = table_efficiency ; cpu_eff - > compatible ; cpu_eff + + )
if ( of_device_is_compatible ( cn , cpu_eff - > compatible ) )
break ;
if ( cpu_eff - > compatible = = NULL )
continue ;
rate = of_get_property ( cn , " clock-frequency " , & len ) ;
if ( ! rate | | len ! = 4 ) {
pr_err ( " %s missing clock-frequency property \n " ,
cn - > full_name ) ;
continue ;
}
reg = of_get_property ( cn , " reg " , & len ) ;
if ( ! reg | | len ! = 4 ) {
pr_err ( " %s missing reg property \n " , cn - > full_name ) ;
continue ;
}
capacity = ( ( be32_to_cpup ( rate ) ) > > 20 ) * cpu_eff - > efficiency ;
/* Save min capacity of the system */
if ( capacity < min_capacity )
min_capacity = capacity ;
/* Save max capacity of the system */
if ( capacity > max_capacity )
max_capacity = capacity ;
cpu_capacity [ cpu ] . capacity = capacity ;
cpu_capacity [ cpu + + ] . hwid = be32_to_cpup ( reg ) ;
}
if ( cpu < num_possible_cpus ( ) )
cpu_capacity [ cpu ] . hwid = ( unsigned long ) ( - 1 ) ;
/* If min and max capacities are equals, we bypass the update of the
* cpu_scale because all CPUs have the same capacity . Otherwise , we
* compute a middle_capacity factor that will ensure that the capacity
* of an ' average ' CPU of the system will be as close as possible to
* SCHED_POWER_SCALE , which is the default value , but with the
* constraint explained near table_efficiency [ ] .
*/
if ( min_capacity = = max_capacity )
cpu_capacity [ 0 ] . hwid = ( unsigned long ) ( - 1 ) ;
else if ( 4 * max_capacity < ( 3 * ( max_capacity + min_capacity ) ) )
middle_capacity = ( min_capacity + max_capacity )
> > ( SCHED_POWER_SHIFT + 1 ) ;
else
middle_capacity = ( ( max_capacity / 3 )
> > ( SCHED_POWER_SHIFT - 1 ) ) + 1 ;
}
/*
* Look for a customed capacity of a CPU in the cpu_capacity table during the
* boot . The update of all CPUs is in O ( n ^ 2 ) for heteregeneous system but the
* function returns directly for SMP system .
*/
void update_cpu_power ( unsigned int cpu , unsigned long hwid )
{
unsigned int idx = 0 ;
/* look for the cpu's hwid in the cpu capacity table */
for ( idx = 0 ; idx < num_possible_cpus ( ) ; idx + + ) {
if ( cpu_capacity [ idx ] . hwid = = hwid )
break ;
if ( cpu_capacity [ idx ] . hwid = = - 1 )
return ;
}
if ( idx = = num_possible_cpus ( ) )
return ;
set_power_scale ( cpu , cpu_capacity [ idx ] . capacity / middle_capacity ) ;
printk ( KERN_INFO " CPU%u: update cpu_power %lu \n " ,
cpu , arch_scale_freq_power ( NULL , cpu ) ) ;
}
# else
static inline void parse_dt_topology ( void ) { }
static inline void update_cpu_power ( unsigned int cpuid , unsigned int mpidr ) { }
# endif
2012-11-15 17:30:32 +00:00
/*
2012-07-10 14:08:40 +01:00
* cpu topology table
*/
2011-08-08 13:21:59 +01:00
struct cputopo_arm cpu_topology [ NR_CPUS ] ;
2011-11-29 15:50:20 +01:00
const struct cpumask * cpu_coregroup_mask ( int cpu )
2011-08-08 13:21:59 +01:00
{
return & cpu_topology [ cpu ] . core_sibling ;
}
2012-07-10 14:11:11 +01:00
void update_siblings_masks ( unsigned int cpuid )
{
struct cputopo_arm * cpu_topo , * cpuid_topo = & cpu_topology [ cpuid ] ;
int cpu ;
/* update core and thread sibling masks */
for_each_possible_cpu ( cpu ) {
cpu_topo = & cpu_topology [ cpu ] ;
if ( cpuid_topo - > socket_id ! = cpu_topo - > socket_id )
continue ;
cpumask_set_cpu ( cpuid , & cpu_topo - > core_sibling ) ;
if ( cpu ! = cpuid )
cpumask_set_cpu ( cpu , & cpuid_topo - > core_sibling ) ;
if ( cpuid_topo - > core_id ! = cpu_topo - > core_id )
continue ;
cpumask_set_cpu ( cpuid , & cpu_topo - > thread_sibling ) ;
if ( cpu ! = cpuid )
cpumask_set_cpu ( cpu , & cpuid_topo - > thread_sibling ) ;
}
smp_wmb ( ) ;
}
2011-08-08 13:21:59 +01:00
/*
* store_cpu_topology is called at boot when only one cpu is running
* and with the mutex cpu_hotplug . lock locked , when several cpus have booted ,
* which prevents simultaneous write access to cpu_topology array
*/
void store_cpu_topology ( unsigned int cpuid )
{
struct cputopo_arm * cpuid_topo = & cpu_topology [ cpuid ] ;
unsigned int mpidr ;
/* If the cpu topology has been already set, just return */
if ( cpuid_topo - > core_id ! = - 1 )
return ;
mpidr = read_cpuid_mpidr ( ) ;
/* create cpu topology mapping */
if ( ( mpidr & MPIDR_SMP_BITMASK ) = = MPIDR_SMP_VALUE ) {
/*
* This is a multiprocessor system
* multiprocessor format & multiprocessor mode field are set
*/
if ( mpidr & MPIDR_MT_BITMASK ) {
/* core performance interdependency */
2012-11-16 15:24:06 +00:00
cpuid_topo - > thread_id = MPIDR_AFFINITY_LEVEL ( mpidr , 0 ) ;
cpuid_topo - > core_id = MPIDR_AFFINITY_LEVEL ( mpidr , 1 ) ;
cpuid_topo - > socket_id = MPIDR_AFFINITY_LEVEL ( mpidr , 2 ) ;
2011-08-08 13:21:59 +01:00
} else {
/* largely independent cores */
cpuid_topo - > thread_id = - 1 ;
2012-11-16 15:24:06 +00:00
cpuid_topo - > core_id = MPIDR_AFFINITY_LEVEL ( mpidr , 0 ) ;
cpuid_topo - > socket_id = MPIDR_AFFINITY_LEVEL ( mpidr , 1 ) ;
2011-08-08 13:21:59 +01:00
}
} else {
/*
* This is an uniprocessor system
* we are in multiprocessor format but uniprocessor system
* or in the old uniprocessor format
*/
cpuid_topo - > thread_id = - 1 ;
cpuid_topo - > core_id = 0 ;
cpuid_topo - > socket_id = - 1 ;
}
2012-07-10 14:11:11 +01:00
update_siblings_masks ( cpuid ) ;
2011-08-08 13:21:59 +01:00
2012-07-10 14:13:12 +01:00
update_cpu_power ( cpuid , mpidr & MPIDR_HWID_BITMASK ) ;
2011-08-08 13:21:59 +01:00
printk ( KERN_INFO " CPU%u: thread %d, cpu %d, socket %d, mpidr %x \n " ,
cpuid , cpu_topology [ cpuid ] . thread_id ,
cpu_topology [ cpuid ] . core_id ,
cpu_topology [ cpuid ] . socket_id , mpidr ) ;
}
/*
* init_cpu_topology is called at boot when only one cpu is running
* which prevent simultaneous write access to cpu_topology array
*/
2012-08-03 07:58:33 +01:00
void __init init_cpu_topology ( void )
2011-08-08 13:21:59 +01:00
{
unsigned int cpu ;
2012-07-10 14:08:40 +01:00
/* init core mask and power*/
2011-08-08 13:21:59 +01:00
for_each_possible_cpu ( cpu ) {
struct cputopo_arm * cpu_topo = & ( cpu_topology [ cpu ] ) ;
cpu_topo - > thread_id = - 1 ;
cpu_topo - > core_id = - 1 ;
cpu_topo - > socket_id = - 1 ;
cpumask_clear ( & cpu_topo - > core_sibling ) ;
cpumask_clear ( & cpu_topo - > thread_sibling ) ;
2012-07-10 14:08:40 +01:00
set_power_scale ( cpu , SCHED_POWER_SCALE ) ;
2011-08-08 13:21:59 +01:00
}
smp_wmb ( ) ;
2012-07-10 14:13:12 +01:00
parse_dt_topology ( ) ;
2011-08-08 13:21:59 +01:00
}