@ -2,6 +2,8 @@
* ( C ) Copyright 2007
* Gerald Van Baren , Custom IDEAS , vanbaren @ cideas . com
*
* Copyright 2010 Freescale Semiconductor , Inc .
*
* See file CREDITS for list of people who contributed to this
* project .
*
@ -861,3 +863,265 @@ void fdt_del_node_and_alias(void *blob, const char *alias)
off = fdt_path_offset ( blob , " /aliases " ) ;
fdt_delprop ( blob , off , alias ) ;
}
/* Helper to read a big number; size is in cells (not bytes) */
static inline u64 of_read_number ( const __be32 * cell , int size )
{
u64 r = 0 ;
while ( size - - )
r = ( r < < 32 ) | be32_to_cpu ( * ( cell + + ) ) ;
return r ;
}
static int of_n_cells ( const void * blob , int nodeoffset , const char * name )
{
int np ;
const int * ip ;
do {
np = fdt_parent_offset ( blob , nodeoffset ) ;
if ( np > = 0 )
nodeoffset = np ;
ip = ( int * ) fdt_getprop ( blob , nodeoffset , name , NULL ) ;
if ( ip )
return be32_to_cpup ( ip ) ;
} while ( np > = 0 ) ;
/* No #<NAME>-cells property for the root node */
return 1 ;
}
int of_n_addr_cells ( const void * blob , int nodeoffset )
{
return of_n_cells ( blob , nodeoffset , " #address-cells " ) ;
}
int of_n_size_cells ( const void * blob , int nodeoffset )
{
return of_n_cells ( blob , nodeoffset , " #size-cells " ) ;
}
# define PRu64 "%llx"
/* Max address size we deal with */
# define OF_MAX_ADDR_CELLS 4
# define OF_BAD_ADDR ((u64)-1)
# define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
( ns ) > 0 )
/* Debug utility */
# ifdef DEBUG
static void of_dump_addr ( const char * s , const u32 * addr , int na )
{
printf ( " %s " , s ) ;
while ( na - - )
printf ( " %08x " , * ( addr + + ) ) ;
printf ( " \n " ) ;
}
# else
static void of_dump_addr ( const char * s , const u32 * addr , int na ) { }
# endif
/* Callbacks for bus specific translators */
struct of_bus {
const char * name ;
const char * addresses ;
void ( * count_cells ) ( void * blob , int offset ,
int * addrc , int * sizec ) ;
u64 ( * map ) ( u32 * addr , const u32 * range ,
int na , int ns , int pna ) ;
int ( * translate ) ( u32 * addr , u64 offset , int na ) ;
} ;
/* Default translator (generic bus) */
static void of_bus_default_count_cells ( void * blob , int offset ,
int * addrc , int * sizec )
{
if ( addrc )
* addrc = of_n_addr_cells ( blob , offset ) ;
if ( sizec )
* sizec = of_n_size_cells ( blob , offset ) ;
}
static u64 of_bus_default_map ( u32 * addr , const u32 * range ,
int na , int ns , int pna )
{
u64 cp , s , da ;
cp = of_read_number ( range , na ) ;
s = of_read_number ( range + na + pna , ns ) ;
da = of_read_number ( addr , na ) ;
debug ( " OF: default map, cp= " PRu64 " , s= " PRu64 " , da= " PRu64 " \n " ,
cp , s , da ) ;
if ( da < cp | | da > = ( cp + s ) )
return OF_BAD_ADDR ;
return da - cp ;
}
static int of_bus_default_translate ( u32 * addr , u64 offset , int na )
{
u64 a = of_read_number ( addr , na ) ;
memset ( addr , 0 , na * 4 ) ;
a + = offset ;
if ( na > 1 )
addr [ na - 2 ] = a > > 32 ;
addr [ na - 1 ] = a & 0xffffffffu ;
return 0 ;
}
/* Array of bus specific translators */
static struct of_bus of_busses [ ] = {
/* Default */
{
. name = " default " ,
. addresses = " reg " ,
. count_cells = of_bus_default_count_cells ,
. map = of_bus_default_map ,
. translate = of_bus_default_translate ,
} ,
} ;
static int of_translate_one ( void * blob , int parent , struct of_bus * bus ,
struct of_bus * pbus , u32 * addr ,
int na , int ns , int pna , const char * rprop )
{
const u32 * ranges ;
int rlen ;
int rone ;
u64 offset = OF_BAD_ADDR ;
/* Normally, an absence of a "ranges" property means we are
* crossing a non - translatable boundary , and thus the addresses
* below the current not cannot be converted to CPU physical ones .
* Unfortunately , while this is very clear in the spec , it ' s not
* what Apple understood , and they do have things like / uni - n or
* / ht nodes with no " ranges " property and a lot of perfectly
* useable mapped devices below them . Thus we treat the absence of
* " ranges " as equivalent to an empty " ranges " property which means
* a 1 : 1 translation at that level . It ' s up to the caller not to try
* to translate addresses that aren ' t supposed to be translated in
* the first place . - - BenH .
*/
ranges = ( u32 * ) fdt_getprop ( blob , parent , rprop , & rlen ) ;
if ( ranges = = NULL | | rlen = = 0 ) {
offset = of_read_number ( addr , na ) ;
memset ( addr , 0 , pna * 4 ) ;
debug ( " OF: no ranges, 1:1 translation \n " ) ;
goto finish ;
}
debug ( " OF: walking ranges... \n " ) ;
/* Now walk through the ranges */
rlen / = 4 ;
rone = na + pna + ns ;
for ( ; rlen > = rone ; rlen - = rone , ranges + = rone ) {
offset = bus - > map ( addr , ranges , na , ns , pna ) ;
if ( offset ! = OF_BAD_ADDR )
break ;
}
if ( offset = = OF_BAD_ADDR ) {
debug ( " OF: not found ! \n " ) ;
return 1 ;
}
memcpy ( addr , ranges + na , 4 * pna ) ;
finish :
of_dump_addr ( " OF: parent translation for: " , addr , pna ) ;
debug ( " OF: with offset: " PRu64 " \n " , offset ) ;
/* Translate it into parent bus space */
return pbus - > translate ( addr , offset , pna ) ;
}
/*
* Translate an address from the device - tree into a CPU physical address ,
* this walks up the tree and applies the various bus mappings on the
* way .
*
* Note : We consider that crossing any level with # size - cells = = 0 to mean
* that translation is impossible ( that is we are not dealing with a value
* that can be mapped to a cpu physical address ) . This is not really specified
* that way , but this is traditionally the way IBM at least do things
*/
u64 __of_translate_address ( void * blob , int node_offset , const u32 * in_addr ,
const char * rprop )
{
int parent ;
struct of_bus * bus , * pbus ;
u32 addr [ OF_MAX_ADDR_CELLS ] ;
int na , ns , pna , pns ;
u64 result = OF_BAD_ADDR ;
debug ( " OF: ** translation for device %s ** \n " ,
fdt_get_name ( blob , node_offset , NULL ) ) ;
/* Get parent & match bus type */
parent = fdt_parent_offset ( blob , node_offset ) ;
if ( parent < 0 )
goto bail ;
bus = & of_busses [ 0 ] ;
/* Cound address cells & copy address locally */
bus - > count_cells ( blob , node_offset , & na , & ns ) ;
if ( ! OF_CHECK_COUNTS ( na , ns ) ) {
printf ( " %s: Bad cell count for %s \n " , __FUNCTION__ ,
fdt_get_name ( blob , node_offset , NULL ) ) ;
goto bail ;
}
memcpy ( addr , in_addr , na * 4 ) ;
debug ( " OF: bus is %s (na=%d, ns=%d) on %s \n " ,
bus - > name , na , ns , fdt_get_name ( blob , parent , NULL ) ) ;
of_dump_addr ( " OF: translating address: " , addr , na ) ;
/* Translate */
for ( ; ; ) {
/* Switch to parent bus */
node_offset = parent ;
parent = fdt_parent_offset ( blob , node_offset ) ;
/* If root, we have finished */
if ( parent < 0 ) {
debug ( " OF: reached root node \n " ) ;
result = of_read_number ( addr , na ) ;
break ;
}
/* Get new parent bus and counts */
pbus = & of_busses [ 0 ] ;
pbus - > count_cells ( blob , node_offset , & pna , & pns ) ;
if ( ! OF_CHECK_COUNTS ( pna , pns ) ) {
printf ( " %s: Bad cell count for %s \n " , __FUNCTION__ ,
fdt_get_name ( blob , node_offset , NULL ) ) ;
break ;
}
debug ( " OF: parent bus is %s (na=%d, ns=%d) on %s \n " ,
pbus - > name , pna , pns , fdt_get_name ( blob , parent , NULL ) ) ;
/* Apply bus translation */
if ( of_translate_one ( blob , node_offset , bus , pbus ,
addr , na , ns , pna , rprop ) )
break ;
/* Complete the move up one level */
na = pna ;
ns = pns ;
bus = pbus ;
of_dump_addr ( " OF: one level translation: " , addr , na ) ;
}
bail :
return result ;
}
u64 fdt_translate_address ( void * blob , int node_offset , const u32 * in_addr )
{
return __of_translate_address ( blob , node_offset , in_addr , " ranges " ) ;
}