2006-06-22 02:35:28 +04:00
/*
* Procedures for creating , accessing and interpreting the device tree .
*
* Paul Mackerras August 1996.
* Copyright ( C ) 1996 - 2005 Paul Mackerras .
*
* Adapted for 64 bit PowerPC by Dave Engebretsen and Peter Bergner .
* { engebret | bergner } @ us . ibm . com
*
* Adapted for sparc64 by David S . Miller davem @ davemloft . net
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/mm.h>
2010-07-12 08:36:09 +04:00
# include <linux/memblock.h>
2010-07-13 13:42:26 +04:00
# include <linux/of.h>
2006-06-22 02:35:28 +04:00
# include <asm/prom.h>
# include <asm/oplib.h>
2006-06-30 02:07:37 +04:00
# include <asm/irq.h>
# include <asm/asi.h>
# include <asm/upa.h>
2007-05-26 02:49:59 +04:00
# include <asm/smp.h>
2006-06-22 02:35:28 +04:00
2008-12-05 07:12:20 +03:00
# include "prom.h"
2006-06-26 10:18:36 +04:00
2008-12-05 11:40:43 +03:00
void * __init prom_early_alloc ( unsigned long size )
2006-06-22 02:35:28 +04:00
{
2010-07-12 08:36:09 +04:00
unsigned long paddr = memblock_alloc ( size , SMP_CACHE_BYTES ) ;
2006-06-22 02:35:28 +04:00
void * ret ;
2008-02-14 06:21:51 +03:00
if ( ! paddr ) {
prom_printf ( " prom_early_alloc(%lu) failed \n " ) ;
prom_halt ( ) ;
}
2006-06-22 02:35:28 +04:00
2008-02-14 06:21:51 +03:00
ret = __va ( paddr ) ;
memset ( ret , 0 , size ) ;
2006-06-22 02:35:28 +04:00
prom_early_allocated + = size ;
return ret ;
}
/* The following routines deal with the black magic of fully naming a
* node .
*
* Certain well known named nodes are just the simple name string .
*
* Actual devices have an address specifier appended to the base name
* string , like this " foo@addr " . The " addr " can be in any number of
* formats , and the platform plus the type of the node determine the
* format and how it is constructed .
*
* For children of the ROOT node , the naming convention is fixed and
* determined by whether this is a sun4u or sun4v system .
*
* For children of other nodes , it is bus type specific . So
* we walk up the tree until we discover a " device_type " property
* we recognize and we go from there .
*
* As an example , the boot device on my workstation has a full path :
*
* / pci @ 1 e , 600000 / ide @ d / disk @ 0 , 0 : c
*/
static void __init sun4v_path_component ( struct device_node * dp , char * tmp_buf )
{
struct linux_prom64_registers * regs ;
struct property * rprop ;
u32 high_bits , low_bits , type ;
rprop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! rprop )
return ;
regs = rprop - > value ;
2010-07-13 13:42:26 +04:00
if ( ! of_node_is_root ( dp - > parent ) ) {
2006-06-22 02:35:28 +04:00
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
( unsigned int ) ( regs - > phys_addr > > 32UL ) ,
( unsigned int ) ( regs - > phys_addr & 0xffffffffUL ) ) ;
return ;
}
type = regs - > phys_addr > > 60UL ;
high_bits = ( regs - > phys_addr > > 32UL ) & 0x0fffffffUL ;
low_bits = ( regs - > phys_addr & 0xffffffffUL ) ;
if ( type = = 0 | | type = = 8 ) {
const char * prefix = ( type = = 0 ) ? " m " : " i " ;
if ( low_bits )
sprintf ( tmp_buf , " %s@%s%x,%x " ,
dp - > name , prefix ,
high_bits , low_bits ) ;
else
sprintf ( tmp_buf , " %s@%s%x " ,
dp - > name ,
prefix ,
high_bits ) ;
} else if ( type = = 12 ) {
sprintf ( tmp_buf , " %s@%x " ,
dp - > name , high_bits ) ;
}
}
static void __init sun4u_path_component ( struct device_node * dp , char * tmp_buf )
{
struct linux_prom64_registers * regs ;
struct property * prop ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
2010-07-13 13:42:26 +04:00
if ( ! of_node_is_root ( dp - > parent ) ) {
2006-06-22 02:35:28 +04:00
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
( unsigned int ) ( regs - > phys_addr > > 32UL ) ,
( unsigned int ) ( regs - > phys_addr & 0xffffffffUL ) ) ;
return ;
}
prop = of_find_property ( dp , " upa-portid " , NULL ) ;
if ( ! prop )
prop = of_find_property ( dp , " portid " , NULL ) ;
if ( prop ) {
unsigned long mask = 0xffffffffUL ;
if ( tlb_type > = cheetah )
mask = 0x7fffff ;
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
* ( u32 * ) prop - > value ,
( unsigned int ) ( regs - > phys_addr & mask ) ) ;
}
}
/* "name@slot,offset" */
static void __init sbus_path_component ( struct device_node * dp , char * tmp_buf )
{
struct linux_prom_registers * regs ;
struct property * prop ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
regs - > which_io ,
regs - > phys_addr ) ;
}
/* "name@devnum[,func]" */
static void __init pci_path_component ( struct device_node * dp , char * tmp_buf )
{
struct linux_prom_pci_registers * regs ;
struct property * prop ;
unsigned int devfn ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
devfn = ( regs - > phys_hi > > 8 ) & 0xff ;
if ( devfn & 0x07 ) {
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
devfn > > 3 ,
devfn & 0x07 ) ;
} else {
sprintf ( tmp_buf , " %s@%x " ,
dp - > name ,
devfn > > 3 ) ;
}
}
/* "name@UPA_PORTID,offset" */
static void __init upa_path_component ( struct device_node * dp , char * tmp_buf )
{
struct linux_prom64_registers * regs ;
struct property * prop ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
prop = of_find_property ( dp , " upa-portid " , NULL ) ;
if ( ! prop )
return ;
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
* ( u32 * ) prop - > value ,
( unsigned int ) ( regs - > phys_addr & 0xffffffffUL ) ) ;
}
/* "name@reg" */
static void __init vdev_path_component ( struct device_node * dp , char * tmp_buf )
{
struct property * prop ;
u32 * regs ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
sprintf ( tmp_buf , " %s@%x " , dp - > name , * regs ) ;
}
/* "name@addrhi,addrlo" */
static void __init ebus_path_component ( struct device_node * dp , char * tmp_buf )
{
struct linux_prom64_registers * regs ;
struct property * prop ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name ,
( unsigned int ) ( regs - > phys_addr > > 32UL ) ,
( unsigned int ) ( regs - > phys_addr & 0xffffffffUL ) ) ;
}
/* "name@bus,addr" */
static void __init i2c_path_component ( struct device_node * dp , char * tmp_buf )
{
struct property * prop ;
u32 * regs ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
/* This actually isn't right... should look at the #address-cells
* property of the i2c bus node etc . etc .
*/
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name , regs [ 0 ] , regs [ 1 ] ) ;
}
/* "name@reg0[,reg1]" */
static void __init usb_path_component ( struct device_node * dp , char * tmp_buf )
{
struct property * prop ;
u32 * regs ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
if ( prop - > length = = sizeof ( u32 ) | | regs [ 1 ] = = 1 ) {
sprintf ( tmp_buf , " %s@%x " ,
dp - > name , regs [ 0 ] ) ;
} else {
sprintf ( tmp_buf , " %s@%x,%x " ,
dp - > name , regs [ 0 ] , regs [ 1 ] ) ;
}
}
/* "name@reg0reg1[,reg2reg3]" */
static void __init ieee1394_path_component ( struct device_node * dp , char * tmp_buf )
{
struct property * prop ;
u32 * regs ;
prop = of_find_property ( dp , " reg " , NULL ) ;
if ( ! prop )
return ;
regs = prop - > value ;
if ( regs [ 2 ] | | regs [ 3 ] ) {
sprintf ( tmp_buf , " %s@%08x%08x,%04x%08x " ,
dp - > name , regs [ 0 ] , regs [ 1 ] , regs [ 2 ] , regs [ 3 ] ) ;
} else {
sprintf ( tmp_buf , " %s@%08x%08x " ,
dp - > name , regs [ 0 ] , regs [ 1 ] ) ;
}
}
static void __init __build_path_component ( struct device_node * dp , char * tmp_buf )
{
struct device_node * parent = dp - > parent ;
if ( parent ! = NULL ) {
if ( ! strcmp ( parent - > type , " pci " ) | |
2008-09-12 10:52:35 +04:00
! strcmp ( parent - > type , " pciex " ) ) {
pci_path_component ( dp , tmp_buf ) ;
return ;
}
if ( ! strcmp ( parent - > type , " sbus " ) ) {
sbus_path_component ( dp , tmp_buf ) ;
return ;
}
if ( ! strcmp ( parent - > type , " upa " ) ) {
upa_path_component ( dp , tmp_buf ) ;
return ;
}
if ( ! strcmp ( parent - > type , " ebus " ) ) {
ebus_path_component ( dp , tmp_buf ) ;
return ;
}
2006-06-22 02:35:28 +04:00
if ( ! strcmp ( parent - > name , " usb " ) | |
2008-09-12 10:52:35 +04:00
! strcmp ( parent - > name , " hub " ) ) {
usb_path_component ( dp , tmp_buf ) ;
return ;
}
if ( ! strcmp ( parent - > type , " i2c " ) ) {
i2c_path_component ( dp , tmp_buf ) ;
return ;
}
if ( ! strcmp ( parent - > type , " firewire " ) ) {
ieee1394_path_component ( dp , tmp_buf ) ;
return ;
}
if ( ! strcmp ( parent - > type , " virtual-devices " ) ) {
vdev_path_component ( dp , tmp_buf ) ;
return ;
}
2006-06-22 02:35:28 +04:00
/* "isa" is handled with platform naming */
}
/* Use platform naming convention. */
2008-09-12 10:52:35 +04:00
if ( tlb_type = = hypervisor ) {
sun4v_path_component ( dp , tmp_buf ) ;
return ;
} else {
sun4u_path_component ( dp , tmp_buf ) ;
}
2006-06-22 02:35:28 +04:00
}
2008-12-05 12:21:41 +03:00
char * __init build_path_component ( struct device_node * dp )
2006-06-22 02:35:28 +04:00
{
char tmp_buf [ 64 ] , * n ;
tmp_buf [ 0 ] = ' \0 ' ;
__build_path_component ( dp , tmp_buf ) ;
if ( tmp_buf [ 0 ] = = ' \0 ' )
strcpy ( tmp_buf , dp - > name ) ;
n = prom_early_alloc ( strlen ( tmp_buf ) + 1 ) ;
strcpy ( n , tmp_buf ) ;
return n ;
}
2007-05-26 02:49:59 +04:00
static const char * get_mid_prop ( void )
{
return ( tlb_type = = spitfire ? " upa-portid " : " portid " ) ;
}
2009-05-27 08:46:14 +04:00
static void * of_iterate_over_cpus ( void * ( * func ) ( struct device_node * , int , int ) , int arg )
2007-05-26 02:49:59 +04:00
{
struct device_node * dp ;
2008-12-06 05:16:48 +03:00
const char * mid_prop ;
mid_prop = get_mid_prop ( ) ;
2007-05-26 02:49:59 +04:00
for_each_node_by_type ( dp , " cpu " ) {
int cpuid = of_getintprop_default ( dp , mid_prop , - 1 ) ;
const char * this_mid_prop = mid_prop ;
2009-05-27 08:46:14 +04:00
void * ret ;
2007-05-26 02:49:59 +04:00
if ( cpuid < 0 ) {
this_mid_prop = " cpuid " ;
cpuid = of_getintprop_default ( dp , this_mid_prop , - 1 ) ;
}
if ( cpuid < 0 ) {
prom_printf ( " OF: Serious problem, cpu lacks "
" %s property " , this_mid_prop ) ;
prom_halt ( ) ;
}
# ifdef CONFIG_SMP
2007-09-17 01:45:06 +04:00
if ( cpuid > = NR_CPUS ) {
printk ( KERN_WARNING " Ignoring CPU %d which is "
" >= NR_CPUS (%d) \n " ,
cpuid , NR_CPUS ) ;
2007-05-26 02:49:59 +04:00
continue ;
2007-09-17 01:45:06 +04:00
}
2007-05-26 02:49:59 +04:00
# endif
2009-05-27 08:46:14 +04:00
ret = func ( dp , cpuid , arg ) ;
if ( ret )
return ret ;
}
return NULL ;
}
2007-05-26 02:49:59 +04:00
2009-05-27 08:46:14 +04:00
static void * check_cpu_node ( struct device_node * dp , int cpuid , int id )
{
if ( id = = cpuid )
return dp ;
return NULL ;
}
struct device_node * of_find_node_by_cpuid ( int cpuid )
{
return of_iterate_over_cpus ( check_cpu_node , cpuid ) ;
}
static void * record_one_cpu ( struct device_node * dp , int cpuid , int arg )
{
ncpus_probed + + ;
2007-06-05 08:48:33 +04:00
# ifdef CONFIG_SMP
2009-05-27 08:46:14 +04:00
set_cpu_present ( cpuid , true ) ;
set_cpu_possible ( cpuid , true ) ;
2007-06-05 08:48:33 +04:00
# endif
2009-05-27 08:46:14 +04:00
return NULL ;
}
2007-05-26 02:49:59 +04:00
2009-05-27 08:46:14 +04:00
void __init of_populate_present_mask ( void )
{
if ( tlb_type = = hypervisor )
return ;
ncpus_probed = 0 ;
of_iterate_over_cpus ( record_one_cpu , 0 ) ;
}
2007-05-26 02:49:59 +04:00
2009-05-27 08:46:14 +04:00
static void * fill_in_one_cpu ( struct device_node * dp , int cpuid , int arg )
{
struct device_node * portid_parent = NULL ;
int portid = - 1 ;
if ( of_find_property ( dp , " cpuid " , NULL ) ) {
int limit = 2 ;
portid_parent = dp ;
while ( limit - - ) {
portid_parent = portid_parent - > parent ;
if ( ! portid_parent )
break ;
portid = of_getintprop_default ( portid_parent ,
" portid " , - 1 ) ;
if ( portid > = 0 )
break ;
}
}
# ifndef CONFIG_SMP
/* On uniprocessor we only want the values for the
* real physical cpu the kernel booted onto , however
* cpu_data ( ) only has one entry at index 0.
*/
if ( cpuid ! = real_hard_smp_processor_id ( ) )
return NULL ;
cpuid = 0 ;
# endif
cpu_data ( cpuid ) . clock_tick =
of_getintprop_default ( dp , " clock-frequency " , 0 ) ;
if ( portid_parent ) {
cpu_data ( cpuid ) . dcache_size =
of_getintprop_default ( dp , " l1-dcache-size " ,
16 * 1024 ) ;
cpu_data ( cpuid ) . dcache_line_size =
of_getintprop_default ( dp , " l1-dcache-line-size " ,
32 ) ;
cpu_data ( cpuid ) . icache_size =
of_getintprop_default ( dp , " l1-icache-size " ,
8 * 1024 ) ;
cpu_data ( cpuid ) . icache_line_size =
of_getintprop_default ( dp , " l1-icache-line-size " ,
32 ) ;
cpu_data ( cpuid ) . ecache_size =
of_getintprop_default ( dp , " l2-cache-size " , 0 ) ;
cpu_data ( cpuid ) . ecache_line_size =
of_getintprop_default ( dp , " l2-cache-line-size " , 0 ) ;
if ( ! cpu_data ( cpuid ) . ecache_size | |
! cpu_data ( cpuid ) . ecache_line_size ) {
2007-05-26 02:49:59 +04:00
cpu_data ( cpuid ) . ecache_size =
2009-05-27 08:46:14 +04:00
of_getintprop_default ( portid_parent ,
" l2-cache-size " ,
2007-05-26 02:49:59 +04:00
( 4 * 1024 * 1024 ) ) ;
cpu_data ( cpuid ) . ecache_line_size =
2009-05-27 08:46:14 +04:00
of_getintprop_default ( portid_parent ,
" l2-cache-line-size " , 64 ) ;
2007-05-26 02:49:59 +04:00
}
2009-05-27 08:46:14 +04:00
cpu_data ( cpuid ) . core_id = portid + 1 ;
cpu_data ( cpuid ) . proc_id = portid ;
2007-05-26 02:49:59 +04:00
# ifdef CONFIG_SMP
2009-05-27 08:46:14 +04:00
sparc64_multi_core = 1 ;
2007-05-26 02:49:59 +04:00
# endif
2009-05-27 08:46:14 +04:00
} else {
cpu_data ( cpuid ) . dcache_size =
of_getintprop_default ( dp , " dcache-size " , 16 * 1024 ) ;
cpu_data ( cpuid ) . dcache_line_size =
of_getintprop_default ( dp , " dcache-line-size " , 32 ) ;
cpu_data ( cpuid ) . icache_size =
of_getintprop_default ( dp , " icache-size " , 16 * 1024 ) ;
cpu_data ( cpuid ) . icache_line_size =
of_getintprop_default ( dp , " icache-line-size " , 32 ) ;
cpu_data ( cpuid ) . ecache_size =
of_getintprop_default ( dp , " ecache-size " ,
( 4 * 1024 * 1024 ) ) ;
cpu_data ( cpuid ) . ecache_line_size =
of_getintprop_default ( dp , " ecache-line-size " , 64 ) ;
cpu_data ( cpuid ) . core_id = 0 ;
cpu_data ( cpuid ) . proc_id = - 1 ;
2007-05-26 02:49:59 +04:00
}
2009-05-27 08:46:14 +04:00
return NULL ;
}
void __init of_fill_in_cpu_data ( void )
{
if ( tlb_type = = hypervisor )
return ;
of_iterate_over_cpus ( fill_in_one_cpu , 0 ) ;
2007-05-26 02:49:59 +04:00
smp_fill_in_sib_core_maps ( ) ;
}
2008-12-06 05:16:48 +03:00
void __init of_console_init ( void )
2007-07-21 03:59:26 +04:00
{
char * msg = " OF stdout device is: %s \n " ;
struct device_node * dp ;
const char * type ;
phandle node ;
of_console_path = prom_early_alloc ( 256 ) ;
if ( prom_ihandle2path ( prom_stdout , of_console_path , 256 ) < 0 ) {
prom_printf ( " Cannot obtain path of stdout. \n " ) ;
prom_halt ( ) ;
}
of_console_options = strrchr ( of_console_path , ' : ' ) ;
if ( of_console_options ) {
of_console_options + + ;
if ( * of_console_options = = ' \0 ' )
of_console_options = NULL ;
}
node = prom_inst2pkg ( prom_stdout ) ;
if ( ! node ) {
prom_printf ( " Cannot resolve stdout node from "
" instance %08x. \n " , prom_stdout ) ;
prom_halt ( ) ;
}
dp = of_find_node_by_phandle ( node ) ;
type = of_get_property ( dp , " device_type " , NULL ) ;
if ( ! type ) {
prom_printf ( " Console stdout lacks device_type property. \n " ) ;
prom_halt ( ) ;
}
if ( strcmp ( type , " display " ) & & strcmp ( type , " serial " ) ) {
prom_printf ( " Console device_type is neither display "
" nor serial. \n " ) ;
prom_halt ( ) ;
}
of_console_device = dp ;
printk ( msg , of_console_path ) ;
}