2010-11-17 06:09:52 +00:00
/*
* OLPC - specific OFW device tree support code .
*
* 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 sparc by David S . Miller davem @ davemloft . net
* Adapted for x86 / OLPC by Andres Salomon < dilinger @ queued . 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/bootmem.h>
# include <linux/of.h>
2011-03-13 15:10:17 +00:00
# include <linux/of_platform.h>
2010-11-17 06:09:52 +00:00
# include <linux/of_pdt.h>
2011-03-13 15:10:17 +00:00
# include <asm/olpc.h>
2010-11-17 06:09:52 +00:00
# include <asm/olpc_ofw.h>
static phandle __init olpc_dt_getsibling ( phandle node )
{
const void * args [ ] = { ( void * ) node } ;
void * res [ ] = { & node } ;
if ( ( s32 ) node = = - 1 )
return 0 ;
if ( olpc_ofw ( " peer " , args , res ) | | ( s32 ) node = = - 1 )
return 0 ;
return node ;
}
static phandle __init olpc_dt_getchild ( phandle node )
{
const void * args [ ] = { ( void * ) node } ;
void * res [ ] = { & node } ;
if ( ( s32 ) node = = - 1 )
return 0 ;
if ( olpc_ofw ( " child " , args , res ) | | ( s32 ) node = = - 1 ) {
pr_err ( " PROM: %s: fetching child failed! \n " , __func__ ) ;
return 0 ;
}
return node ;
}
static int __init olpc_dt_getproplen ( phandle node , const char * prop )
{
const void * args [ ] = { ( void * ) node , prop } ;
int len ;
void * res [ ] = { & len } ;
if ( ( s32 ) node = = - 1 )
return - 1 ;
if ( olpc_ofw ( " getproplen " , args , res ) ) {
pr_err ( " PROM: %s: getproplen failed! \n " , __func__ ) ;
return - 1 ;
}
return len ;
}
static int __init olpc_dt_getproperty ( phandle node , const char * prop ,
char * buf , int bufsize )
{
int plen ;
plen = olpc_dt_getproplen ( node , prop ) ;
if ( plen > bufsize | | plen < 1 ) {
return - 1 ;
} else {
const void * args [ ] = { ( void * ) node , prop , buf , ( void * ) plen } ;
void * res [ ] = { & plen } ;
if ( olpc_ofw ( " getprop " , args , res ) ) {
pr_err ( " PROM: %s: getprop failed! \n " , __func__ ) ;
return - 1 ;
}
}
return plen ;
}
static int __init olpc_dt_nextprop ( phandle node , char * prev , char * buf )
{
const void * args [ ] = { ( void * ) node , prev , buf } ;
int success ;
void * res [ ] = { & success } ;
buf [ 0 ] = ' \0 ' ;
if ( ( s32 ) node = = - 1 )
return - 1 ;
if ( olpc_ofw ( " nextprop " , args , res ) | | success ! = 1 )
return - 1 ;
return 0 ;
}
static int __init olpc_dt_pkg2path ( phandle node , char * buf ,
const int buflen , int * len )
{
const void * args [ ] = { ( void * ) node , buf , ( void * ) buflen } ;
void * res [ ] = { len } ;
if ( ( s32 ) node = = - 1 )
return - 1 ;
if ( olpc_ofw ( " package-to-path " , args , res ) | | * len < 1 )
return - 1 ;
return 0 ;
}
static unsigned int prom_early_allocated __initdata ;
void * __init prom_early_alloc ( unsigned long size )
{
2010-11-29 23:39:51 +00:00
static u8 * mem ;
static size_t free_mem ;
2010-11-17 06:09:52 +00:00
void * res ;
2010-11-29 23:39:51 +00:00
if ( free_mem < size ) {
const size_t chunk_size = max ( PAGE_SIZE , size ) ;
/*
* To mimimize the number of allocations , grab at least
* PAGE_SIZE of memory ( that ' s an arbitrary choice that ' s
* fast enough on the platforms we care about while minimizing
* wasted bootmem ) and hand off chunks of it to callers .
*/
res = alloc_bootmem ( chunk_size ) ;
2011-02-24 20:06:31 -08:00
BUG_ON ( ! res ) ;
2010-11-29 23:39:51 +00:00
prom_early_allocated + = chunk_size ;
memset ( res , 0 , chunk_size ) ;
free_mem = chunk_size ;
mem = res ;
}
2010-11-17 06:09:52 +00:00
2010-11-29 23:39:51 +00:00
/* allocate from the local cache */
free_mem - = size ;
res = mem ;
mem + = size ;
2010-11-17 06:09:52 +00:00
return res ;
}
static struct of_pdt_ops prom_olpc_ops __initdata = {
. nextprop = olpc_dt_nextprop ,
. getproplen = olpc_dt_getproplen ,
. getproperty = olpc_dt_getproperty ,
. getchild = olpc_dt_getchild ,
. getsibling = olpc_dt_getsibling ,
. pkg2path = olpc_dt_pkg2path ,
} ;
2011-06-25 17:34:08 +01:00
static phandle __init olpc_dt_finddevice ( const char * path )
{
phandle node ;
const void * args [ ] = { path } ;
void * res [ ] = { & node } ;
if ( olpc_ofw ( " finddevice " , args , res ) ) {
pr_err ( " olpc_dt: finddevice failed! \n " ) ;
return 0 ;
}
if ( ( s32 ) node = = - 1 )
return 0 ;
return node ;
}
static int __init olpc_dt_interpret ( const char * words )
{
int result ;
const void * args [ ] = { words } ;
void * res [ ] = { & result } ;
if ( olpc_ofw ( " interpret " , args , res ) ) {
pr_err ( " olpc_dt: interpret failed! \n " ) ;
return - 1 ;
}
return result ;
}
/*
* Extract board revision directly from OFW device tree .
* We can ' t use olpc_platform_info because that hasn ' t been set up yet .
*/
static u32 __init olpc_dt_get_board_revision ( void )
{
phandle node ;
__be32 rev ;
int r ;
node = olpc_dt_finddevice ( " / " ) ;
if ( ! node )
return 0 ;
r = olpc_dt_getproperty ( node , " board-revision-int " ,
( char * ) & rev , sizeof ( rev ) ) ;
if ( r < 0 )
return 0 ;
return be32_to_cpu ( rev ) ;
}
void __init olpc_dt_fixup ( void )
{
int r ;
char buf [ 64 ] ;
phandle node ;
u32 board_rev ;
node = olpc_dt_finddevice ( " /battery@0 " ) ;
if ( ! node )
return ;
/*
* If the battery node has a compatible property , we are running a new
* enough firmware and don ' t have fixups to make .
*/
r = olpc_dt_getproperty ( node , " compatible " , buf , sizeof ( buf ) ) ;
if ( r > 0 )
return ;
pr_info ( " PROM DT: Old firmware detected, applying fixes \n " ) ;
/* Add olpc,xo1-battery compatible marker to battery node */
olpc_dt_interpret ( " \" /battery@0 \" find-device "
" \" olpc,xo1-battery \" +compatible "
" device-end " ) ;
board_rev = olpc_dt_get_board_revision ( ) ;
if ( ! board_rev )
return ;
if ( board_rev > = olpc_board_pre ( 0xd0 ) ) {
/* XO-1.5: add dcon device */
olpc_dt_interpret ( " \" /pci/display@1 \" find-device "
" new-device "
" \" dcon \" device-name \" olpc,xo1-dcon \" +compatible "
" finish-device device-end " ) ;
} else {
/* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */
olpc_dt_interpret ( " \" /pci/display@1,1 \" find-device "
" new-device "
" \" dcon \" device-name \" olpc,xo1-dcon \" +compatible "
" finish-device device-end "
" \" /rtc \" find-device "
" \" olpc,xo1-rtc \" +compatible "
" device-end " ) ;
}
}
2010-11-17 06:09:52 +00:00
void __init olpc_dt_build_devicetree ( void )
{
phandle root ;
if ( ! olpc_ofw_is_installed ( ) )
return ;
2011-06-25 17:34:08 +01:00
olpc_dt_fixup ( ) ;
2010-11-17 06:09:52 +00:00
root = olpc_dt_getsibling ( 0 ) ;
if ( ! root ) {
pr_err ( " PROM: unable to get root node from OFW! \n " ) ;
return ;
}
of_pdt_build_devicetree ( root , & prom_olpc_ops ) ;
pr_info ( " PROM DT: Built device tree with %u bytes of memory. \n " ,
prom_early_allocated ) ;
}
2011-03-13 15:10:17 +00:00
/* A list of DT node/bus matches that we want to expose as platform devices */
static struct of_device_id __initdata of_ids [ ] = {
{ . compatible = " olpc,xo1-battery " } ,
{ . compatible = " olpc,xo1-dcon " } ,
{ . compatible = " olpc,xo1-rtc " } ,
{ } ,
} ;
static int __init olpc_create_platform_devices ( void )
{
if ( machine_is_olpc ( ) )
return of_platform_bus_probe ( NULL , of_ids , NULL ) ;
else
return 0 ;
}
device_initcall ( olpc_create_platform_devices ) ;