2007-03-22 09:02:21 +03:00
/*
* devtree . c - convenience functions for device tree manipulation
* Copyright 2007 David Gibson , IBM Corporation .
* Copyright ( c ) 2007 Freescale Semiconductor , Inc .
*
* Authors : David Gibson < david @ gibson . dropbear . id . au >
* Scott Wood < scottwood @ freescale . com >
*
* 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 <stdarg.h>
# include <stddef.h>
# include "types.h"
# include "string.h"
# include "stdio.h"
# include "ops.h"
void dt_fixup_memory ( u64 start , u64 size )
{
void * root , * memory ;
int naddr , nsize , i ;
u32 memreg [ 4 ] ;
root = finddevice ( " / " ) ;
if ( getprop ( root , " #address-cells " , & naddr , sizeof ( naddr ) ) < 0 )
naddr = 2 ;
if ( naddr < 1 | | naddr > 2 )
fatal ( " Can't cope with #address-cells == %d in / \n \r " , naddr ) ;
if ( getprop ( root , " #size-cells " , & nsize , sizeof ( nsize ) ) < 0 )
nsize = 1 ;
if ( nsize < 1 | | nsize > 2 )
fatal ( " Can't cope with #size-cells == %d in / \n \r " , nsize ) ;
i = 0 ;
if ( naddr = = 2 )
memreg [ i + + ] = start > > 32 ;
memreg [ i + + ] = start & 0xffffffff ;
if ( nsize = = 2 )
memreg [ i + + ] = size > > 32 ;
memreg [ i + + ] = size & 0xffffffff ;
memory = finddevice ( " /memory " ) ;
if ( ! memory ) {
memory = create_node ( NULL , " memory " ) ;
setprop_str ( memory , " device_type " , " memory " ) ;
}
printf ( " Memory <- <0x%x " , memreg [ 0 ] ) ;
for ( i = 1 ; i < ( naddr + nsize ) ; i + + )
printf ( " 0x%x " , memreg [ i ] ) ;
printf ( " > (%ldMB) \n \r " , ( unsigned long ) ( size > > 20 ) ) ;
setprop ( memory , " reg " , memreg , ( naddr + nsize ) * sizeof ( u32 ) ) ;
}
# define MHZ(x) ((x + 500000) / 1000000)
void dt_fixup_cpu_clocks ( u32 cpu , u32 tb , u32 bus )
{
void * devp = NULL ;
printf ( " CPU clock-frequency <- 0x%x (%dMHz) \n \r " , cpu , MHZ ( cpu ) ) ;
printf ( " CPU timebase-frequency <- 0x%x (%dMHz) \n \r " , tb , MHZ ( tb ) ) ;
if ( bus > 0 )
printf ( " CPU bus-frequency <- 0x%x (%dMHz) \n \r " , bus , MHZ ( bus ) ) ;
while ( ( devp = find_node_by_devtype ( devp , " cpu " ) ) ) {
setprop_val ( devp , " clock-frequency " , cpu ) ;
setprop_val ( devp , " timebase-frequency " , tb ) ;
if ( bus > 0 )
setprop_val ( devp , " bus-frequency " , bus ) ;
}
2007-08-20 21:39:45 +04:00
timebase_period_ns = 1000000000 / tb ;
2007-03-22 09:02:21 +03:00
}
void dt_fixup_clock ( const char * path , u32 freq )
{
void * devp = finddevice ( path ) ;
if ( devp ) {
printf ( " %s: clock-frequency <- %x (%dMHz) \n \r " , path , freq , MHZ ( freq ) ) ;
setprop_val ( devp , " clock-frequency " , freq ) ;
}
}
2008-01-15 18:30:32 +03:00
void dt_fixup_mac_address_by_alias ( const char * alias , const u8 * addr )
{
void * devp = find_node_by_alias ( alias ) ;
if ( devp ) {
printf ( " %s: local-mac-address <- "
" %02x:%02x:%02x:%02x:%02x:%02x \n \r " , alias ,
addr [ 0 ] , addr [ 1 ] , addr [ 2 ] ,
addr [ 3 ] , addr [ 4 ] , addr [ 5 ] ) ;
setprop ( devp , " local-mac-address " , addr , 6 ) ;
}
}
2007-09-25 00:09:11 +04:00
void dt_fixup_mac_address ( u32 index , const u8 * addr )
{
void * devp = find_node_by_prop_value ( NULL , " linux,network-index " ,
( void * ) & index , sizeof ( index ) ) ;
if ( devp ) {
printf ( " ENET%d: local-mac-address <- "
" %02x:%02x:%02x:%02x:%02x:%02x \n \r " , index ,
addr [ 0 ] , addr [ 1 ] , addr [ 2 ] ,
addr [ 3 ] , addr [ 4 ] , addr [ 5 ] ) ;
setprop ( devp , " local-mac-address " , addr , 6 ) ;
}
}
2007-03-22 09:02:21 +03:00
void __dt_fixup_mac_addresses ( u32 startindex , . . . )
{
va_list ap ;
u32 index = startindex ;
const u8 * addr ;
va_start ( ap , startindex ) ;
2007-09-25 00:09:11 +04:00
while ( ( addr = va_arg ( ap , const u8 * ) ) )
dt_fixup_mac_address ( index + + , addr ) ;
2007-03-22 09:02:21 +03:00
va_end ( ap ) ;
}
2007-03-27 00:52:24 +04:00
# define MAX_ADDR_CELLS 4
2007-08-20 21:40:02 +04:00
void dt_get_reg_format ( void * node , u32 * naddr , u32 * nsize )
2007-03-27 00:52:24 +04:00
{
if ( getprop ( node , " #address-cells " , naddr , 4 ) ! = 4 )
* naddr = 2 ;
if ( getprop ( node , " #size-cells " , nsize , 4 ) ! = 4 )
* nsize = 1 ;
}
static void copy_val ( u32 * dest , u32 * src , int naddr )
{
2007-04-26 21:08:13 +04:00
int pad = MAX_ADDR_CELLS - naddr ;
memset ( dest , 0 , pad * 4 ) ;
memcpy ( dest + pad , src , naddr * 4 ) ;
2007-03-27 00:52:24 +04:00
}
static int sub_reg ( u32 * reg , u32 * sub )
{
int i , borrow = 0 ;
2007-04-26 21:08:13 +04:00
for ( i = MAX_ADDR_CELLS - 1 ; i > = 0 ; i - - ) {
2007-03-27 00:52:24 +04:00
int prev_borrow = borrow ;
borrow = reg [ i ] < sub [ i ] + prev_borrow ;
reg [ i ] - = sub [ i ] + prev_borrow ;
}
return ! borrow ;
}
2007-04-26 21:08:13 +04:00
static int add_reg ( u32 * reg , u32 * add , int naddr )
2007-03-27 00:52:24 +04:00
{
int i , carry = 0 ;
2007-04-26 21:08:13 +04:00
for ( i = MAX_ADDR_CELLS - 1 ; i > = MAX_ADDR_CELLS - naddr ; i - - ) {
2007-03-27 00:52:24 +04:00
u64 tmp = ( u64 ) reg [ i ] + add [ i ] + carry ;
carry = tmp > > 32 ;
reg [ i ] = ( u32 ) tmp ;
}
return ! carry ;
}
/* It is assumed that if the first byte of reg fits in a
* range , then the whole reg block fits .
*/
static int compare_reg ( u32 * reg , u32 * range , u32 * rangesize )
{
int i ;
u32 end ;
for ( i = 0 ; i < MAX_ADDR_CELLS ; i + + ) {
if ( reg [ i ] < range [ i ] )
return 0 ;
if ( reg [ i ] > range [ i ] )
break ;
}
for ( i = 0 ; i < MAX_ADDR_CELLS ; i + + ) {
end = range [ i ] + rangesize [ i ] ;
if ( reg [ i ] < end )
break ;
if ( reg [ i ] > end )
return 0 ;
}
return reg [ i ] ! = end ;
}
/* reg must be MAX_ADDR_CELLS */
static int find_range ( u32 * reg , u32 * ranges , int nregaddr ,
int naddr , int nsize , int buflen )
{
int nrange = nregaddr + naddr + nsize ;
int i ;
for ( i = 0 ; i + nrange < = buflen ; i + = nrange ) {
u32 range_addr [ MAX_ADDR_CELLS ] ;
u32 range_size [ MAX_ADDR_CELLS ] ;
copy_val ( range_addr , ranges + i , naddr ) ;
copy_val ( range_size , ranges + i + nregaddr + naddr , nsize ) ;
if ( compare_reg ( reg , range_addr , range_size ) )
return i ;
}
return - 1 ;
}
/* Currently only generic buses without special encodings are supported.
* In particular , PCI is not supported . Also , only the beginning of the
* reg block is tracked ; size is ignored except in ranges .
*/
2007-08-20 21:39:48 +04:00
static u32 prop_buf [ MAX_PROP_LEN / 4 ] ;
2007-04-28 00:48:24 +04:00
static int dt_xlate ( void * node , int res , int reglen , unsigned long * addr ,
unsigned long * size )
2007-03-27 00:52:24 +04:00
{
u32 last_addr [ MAX_ADDR_CELLS ] ;
u32 this_addr [ MAX_ADDR_CELLS ] ;
void * parent ;
u64 ret_addr , ret_size ;
2007-08-20 21:39:46 +04:00
u32 naddr , nsize , prev_naddr , prev_nsize ;
2007-03-27 00:52:24 +04:00
int buflen , offset ;
parent = get_parent ( node ) ;
if ( ! parent )
return 0 ;
2007-08-20 21:40:02 +04:00
dt_get_reg_format ( parent , & naddr , & nsize ) ;
2007-03-27 00:52:24 +04:00
if ( nsize > 2 )
return 0 ;
offset = ( naddr + nsize ) * res ;
2007-04-28 00:48:24 +04:00
if ( reglen < offset + naddr + nsize | |
2007-08-20 21:39:48 +04:00
MAX_PROP_LEN < ( offset + naddr + nsize ) * 4 )
2007-03-27 00:52:24 +04:00
return 0 ;
2007-08-20 21:39:48 +04:00
copy_val ( last_addr , prop_buf + offset , naddr ) ;
2007-03-27 00:52:24 +04:00
2007-08-20 21:39:48 +04:00
ret_size = prop_buf [ offset + naddr ] ;
2007-03-27 00:52:24 +04:00
if ( nsize = = 2 ) {
ret_size < < = 32 ;
2007-08-20 21:39:48 +04:00
ret_size | = prop_buf [ offset + naddr + 1 ] ;
2007-03-27 00:52:24 +04:00
}
2007-08-20 21:39:46 +04:00
for ( ; ; ) {
2007-03-27 00:52:24 +04:00
prev_naddr = naddr ;
2007-08-20 21:39:46 +04:00
prev_nsize = nsize ;
node = parent ;
2007-03-27 00:52:24 +04:00
2007-08-20 21:39:46 +04:00
parent = get_parent ( node ) ;
if ( ! parent )
break ;
2007-08-20 21:40:02 +04:00
dt_get_reg_format ( parent , & naddr , & nsize ) ;
2007-03-27 00:52:24 +04:00
2007-08-20 21:39:48 +04:00
buflen = getprop ( node , " ranges " , prop_buf ,
sizeof ( prop_buf ) ) ;
2007-08-20 21:39:46 +04:00
if ( buflen = = 0 )
2007-03-27 00:52:24 +04:00
continue ;
2007-08-20 21:39:48 +04:00
if ( buflen < 0 | | buflen > sizeof ( prop_buf ) )
2007-03-27 00:52:24 +04:00
return 0 ;
2007-08-20 21:39:48 +04:00
offset = find_range ( last_addr , prop_buf , prev_naddr ,
2007-08-20 21:39:46 +04:00
naddr , prev_nsize , buflen / 4 ) ;
2007-03-27 00:52:24 +04:00
if ( offset < 0 )
return 0 ;
2007-08-20 21:39:48 +04:00
copy_val ( this_addr , prop_buf + offset , prev_naddr ) ;
2007-03-27 00:52:24 +04:00
if ( ! sub_reg ( last_addr , this_addr ) )
return 0 ;
2007-08-20 21:39:48 +04:00
copy_val ( this_addr , prop_buf + offset + prev_naddr , naddr ) ;
2007-03-27 00:52:24 +04:00
2007-04-26 21:08:13 +04:00
if ( ! add_reg ( last_addr , this_addr , naddr ) )
2007-03-27 00:52:24 +04:00
return 0 ;
}
if ( naddr > 2 )
return 0 ;
2007-04-26 21:08:13 +04:00
ret_addr = ( ( u64 ) last_addr [ 2 ] < < 32 ) | last_addr [ 3 ] ;
2007-03-27 00:52:24 +04:00
if ( sizeof ( void * ) = = 4 & &
( ret_addr > = 0x100000000ULL | | ret_size > 0x100000000ULL | |
ret_addr + ret_size > 0x100000000ULL ) )
return 0 ;
* addr = ret_addr ;
if ( size )
* size = ret_size ;
return 1 ;
}
2007-04-28 00:48:24 +04:00
int dt_xlate_reg ( void * node , int res , unsigned long * addr , unsigned long * size )
{
int reglen ;
2007-08-20 21:39:48 +04:00
reglen = getprop ( node , " reg " , prop_buf , sizeof ( prop_buf ) ) / 4 ;
2007-04-28 00:48:24 +04:00
return dt_xlate ( node , res , reglen , addr , size ) ;
}
int dt_xlate_addr ( void * node , u32 * buf , int buflen , unsigned long * xlated_addr )
{
2007-08-20 21:39:48 +04:00
if ( buflen > sizeof ( prop_buf ) )
2007-04-28 00:48:24 +04:00
return 0 ;
2007-08-20 21:39:48 +04:00
memcpy ( prop_buf , buf , buflen ) ;
2007-04-28 00:48:24 +04:00
return dt_xlate ( node , 0 , buflen / 4 , xlated_addr , NULL ) ;
}
2007-08-20 21:39:48 +04:00
int dt_is_compatible ( void * node , const char * compat )
{
char * buf = ( char * ) prop_buf ;
int len , pos ;
len = getprop ( node , " compatible " , buf , MAX_PROP_LEN ) ;
if ( len < 0 )
return 0 ;
for ( pos = 0 ; pos < len ; pos + + ) {
if ( ! strcmp ( buf + pos , compat ) )
return 1 ;
pos + = strnlen ( & buf [ pos ] , len - pos ) ;
}
return 0 ;
}