2016-04-08 15:50:25 -07:00
/*
* OF NUMA Parsing support .
*
* Copyright ( C ) 2015 - 2016 Cavium Inc .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2016-09-01 14:54:58 +08:00
# define pr_fmt(fmt) "OF: NUMA: " fmt
2016-04-08 15:50:25 -07:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/nodemask.h>
# include <asm/numa.h>
/* define default numa node to 0 */
# define DEFAULT_NODE 0
/*
* Even though we connect cpus to numa domains later in SMP
* init , we need to know the node ids now for all cpus .
*/
static void __init of_numa_parse_cpu_nodes ( void )
{
u32 nid ;
int r ;
struct device_node * cpus ;
struct device_node * np = NULL ;
cpus = of_find_node_by_path ( " /cpus " ) ;
if ( ! cpus )
return ;
for_each_child_of_node ( cpus , np ) {
/* Skip things that are not CPUs */
if ( of_node_cmp ( np - > type , " cpu " ) ! = 0 )
continue ;
r = of_property_read_u32 ( np , " numa-node-id " , & nid ) ;
if ( r )
continue ;
2016-09-01 14:54:58 +08:00
pr_debug ( " CPU on %u \n " , nid ) ;
2016-04-08 15:50:25 -07:00
if ( nid > = MAX_NUMNODES )
2016-09-01 14:54:58 +08:00
pr_warn ( " Node id %u exceeds maximum value \n " , nid ) ;
2016-04-08 15:50:25 -07:00
else
node_set ( nid , numa_nodes_parsed ) ;
}
}
static int __init of_numa_parse_memory_nodes ( void )
{
struct device_node * np = NULL ;
struct resource rsrc ;
u32 nid ;
2016-09-01 14:54:53 +08:00
int i , r ;
2016-04-08 15:50:25 -07:00
2016-09-01 14:54:53 +08:00
for_each_node_by_type ( np , " memory " ) {
2016-04-08 15:50:25 -07:00
r = of_property_read_u32 ( np , " numa-node-id " , & nid ) ;
if ( r = = - EINVAL )
/*
* property doesn ' t exist if - EINVAL , continue
* looking for more memory nodes with
* " numa-node-id " property
*/
continue ;
2016-09-01 14:54:54 +08:00
if ( nid > = MAX_NUMNODES ) {
2016-09-01 14:54:58 +08:00
pr_warn ( " Node id %u exceeds maximum value \n " , nid ) ;
2016-09-01 14:54:54 +08:00
r = - EINVAL ;
}
2016-09-01 14:54:53 +08:00
for ( i = 0 ; ! r & & ! of_address_to_resource ( np , i , & rsrc ) ; i + + )
r = numa_add_memblk ( nid , rsrc . start , rsrc . end + 1 ) ;
if ( ! i | | r ) {
of_node_put ( np ) ;
2016-09-01 14:54:58 +08:00
pr_err ( " bad property in memory node \n " ) ;
2016-09-01 14:54:53 +08:00
return r ? : - EINVAL ;
}
2016-04-08 15:50:25 -07:00
}
2016-09-01 14:54:53 +08:00
return 0 ;
2016-04-08 15:50:25 -07:00
}
static int __init of_numa_parse_distance_map_v1 ( struct device_node * map )
{
const __be32 * matrix ;
int entry_count ;
int i ;
2016-09-01 14:54:58 +08:00
pr_info ( " parsing numa-distance-map-v1 \n " ) ;
2016-04-08 15:50:25 -07:00
matrix = of_get_property ( map , " distance-matrix " , NULL ) ;
if ( ! matrix ) {
2016-09-01 14:54:58 +08:00
pr_err ( " No distance-matrix property in distance-map \n " ) ;
2016-04-08 15:50:25 -07:00
return - EINVAL ;
}
entry_count = of_property_count_u32_elems ( map , " distance-matrix " ) ;
if ( entry_count < = 0 ) {
2016-09-01 14:54:58 +08:00
pr_err ( " Invalid distance-matrix \n " ) ;
2016-04-08 15:50:25 -07:00
return - EINVAL ;
}
for ( i = 0 ; i + 2 < entry_count ; i + = 3 ) {
u32 nodea , nodeb , distance ;
nodea = of_read_number ( matrix , 1 ) ;
matrix + + ;
nodeb = of_read_number ( matrix , 1 ) ;
matrix + + ;
distance = of_read_number ( matrix , 1 ) ;
matrix + + ;
numa_set_distance ( nodea , nodeb , distance ) ;
2016-09-01 14:54:58 +08:00
pr_debug ( " distance[node%d -> node%d] = %d \n " ,
2016-04-08 15:50:25 -07:00
nodea , nodeb , distance ) ;
/* Set default distance of node B->A same as A->B */
if ( nodeb > nodea )
numa_set_distance ( nodeb , nodea , distance ) ;
}
return 0 ;
}
static int __init of_numa_parse_distance_map ( void )
{
int ret = 0 ;
struct device_node * np ;
np = of_find_compatible_node ( NULL , NULL ,
" numa-distance-map-v1 " ) ;
if ( np )
ret = of_numa_parse_distance_map_v1 ( np ) ;
of_node_put ( np ) ;
return ret ;
}
int of_node_to_nid ( struct device_node * device )
{
struct device_node * np ;
u32 nid ;
int r = - ENODATA ;
np = of_node_get ( device ) ;
while ( np ) {
r = of_property_read_u32 ( np , " numa-node-id " , & nid ) ;
/*
* - EINVAL indicates the property was not found , and
* we walk up the tree trying to find a parent with a
* " numa-node-id " . Any other type of error indicates
* a bad device tree and we give up .
*/
if ( r ! = - EINVAL )
break ;
2016-09-01 14:54:57 +08:00
np = of_get_next_parent ( np ) ;
2016-04-08 15:50:25 -07:00
}
if ( np & & r )
2016-09-01 14:54:58 +08:00
pr_warn ( " Invalid \" numa-node-id \" property in node %s \n " ,
2016-04-08 15:50:25 -07:00
np - > name ) ;
of_node_put ( np ) ;
2016-09-01 14:54:55 +08:00
if ( ! r )
return nid ;
2016-04-08 15:50:25 -07:00
return NUMA_NO_NODE ;
}
EXPORT_SYMBOL ( of_node_to_nid ) ;
int __init of_numa_init ( void )
{
int r ;
of_numa_parse_cpu_nodes ( ) ;
r = of_numa_parse_memory_nodes ( ) ;
if ( r )
return r ;
return of_numa_parse_distance_map ( ) ;
}