2005-04-17 02:20:36 +04:00
/*
* PPC System library functions
*
2005-11-14 03:06:30 +03:00
* Maintainer : Kumar Gala < galak @ kernel . crashing . org >
2005-04-17 02:20:36 +04:00
*
* Copyright 2005 Freescale Semiconductor Inc .
2005-09-04 02:55:46 +04:00
* Copyright 2005 MontaVista , Inc . by Vitaly Bordug < vbordug @ ru . mvista . com >
2005-04-17 02:20:36 +04:00
*
* 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 .
*/
2005-11-07 11:59:43 +03:00
# include <linux/string.h>
2006-01-20 22:22:34 +03:00
# include <linux/bootmem.h>
2005-04-17 02:20:36 +04:00
# include <asm/ppc_sys.h>
int ( * ppc_sys_device_fixup ) ( struct platform_device * pdev ) ;
static int ppc_sys_inited ;
2006-01-20 22:22:34 +03:00
static int ppc_sys_func_inited ;
static const char * ppc_sys_func_names [ ] = {
[ PPC_SYS_FUNC_DUMMY ] = " dummy " ,
[ PPC_SYS_FUNC_ETH ] = " eth " ,
[ PPC_SYS_FUNC_UART ] = " uart " ,
[ PPC_SYS_FUNC_HLDC ] = " hldc " ,
[ PPC_SYS_FUNC_USB ] = " usb " ,
[ PPC_SYS_FUNC_IRDA ] = " irda " ,
} ;
2005-04-17 02:20:36 +04:00
void __init identify_ppc_sys_by_id ( u32 id )
{
unsigned int i = 0 ;
while ( 1 ) {
if ( ( ppc_sys_specs [ i ] . mask & id ) = = ppc_sys_specs [ i ] . value )
break ;
i + + ;
}
cur_ppc_sys_spec = & ppc_sys_specs [ i ] ;
return ;
}
void __init identify_ppc_sys_by_name ( char * name )
{
2005-09-04 02:55:46 +04:00
unsigned int i = 0 ;
2006-01-20 22:22:34 +03:00
while ( ppc_sys_specs [ i ] . ppc_sys_name [ 0 ] ) {
2005-09-04 02:55:46 +04:00
if ( ! strcmp ( ppc_sys_specs [ i ] . ppc_sys_name , name ) )
break ;
i + + ;
}
cur_ppc_sys_spec = & ppc_sys_specs [ i ] ;
2006-01-20 22:22:34 +03:00
2005-04-17 02:20:36 +04:00
return ;
}
2005-09-04 02:55:46 +04:00
static int __init count_sys_specs ( void )
{
int i = 0 ;
while ( ppc_sys_specs [ i ] . ppc_sys_name [ 0 ] )
i + + ;
return i ;
}
static int __init find_chip_by_name_and_id ( char * name , u32 id )
{
int ret = - 1 ;
unsigned int i = 0 ;
unsigned int j = 0 ;
unsigned int dups = 0 ;
unsigned char matched [ count_sys_specs ( ) ] ;
while ( ppc_sys_specs [ i ] . ppc_sys_name [ 0 ] ) {
if ( ! strcmp ( ppc_sys_specs [ i ] . ppc_sys_name , name ) )
matched [ j + + ] = i ;
i + + ;
}
2005-10-29 04:46:28 +04:00
ret = i ;
2005-09-04 02:55:46 +04:00
if ( j ! = 0 ) {
for ( i = 0 ; i < j ; i + + ) {
if ( ( ppc_sys_specs [ matched [ i ] ] . mask & id ) = =
ppc_sys_specs [ matched [ i ] ] . value ) {
ret = matched [ i ] ;
dups + + ;
}
}
ret = ( dups = = 1 ) ? ret : ( - 1 * dups ) ;
}
return ret ;
}
void __init identify_ppc_sys_by_name_and_id ( char * name , u32 id )
{
int i = find_chip_by_name_and_id ( name , id ) ;
BUG_ON ( i < 0 ) ;
cur_ppc_sys_spec = & ppc_sys_specs [ i ] ;
}
2005-04-17 02:20:36 +04:00
/* Update all memory resources by paddr, call before platform_device_register */
void __init
ppc_sys_fixup_mem_resource ( struct platform_device * pdev , phys_addr_t paddr )
{
int i ;
for ( i = 0 ; i < pdev - > num_resources ; i + + ) {
struct resource * r = & pdev - > resource [ i ] ;
2006-04-25 20:26:33 +04:00
if ( ( ( r - > flags & IORESOURCE_MEM ) = = IORESOURCE_MEM ) & &
( ( r - > flags & PPC_SYS_IORESOURCE_FIXUPPED ) ! = PPC_SYS_IORESOURCE_FIXUPPED ) ) {
2005-04-17 02:20:36 +04:00
r - > start + = paddr ;
r - > end + = paddr ;
2006-04-25 20:26:33 +04:00
r - > flags | = PPC_SYS_IORESOURCE_FIXUPPED ;
2005-04-17 02:20:36 +04:00
}
}
}
/* Get platform_data pointer out of platform device, call before platform_device_register */
void * __init ppc_sys_get_pdata ( enum ppc_sys_devices dev )
{
return ppc_sys_platform_devices [ dev ] . dev . platform_data ;
}
void ppc_sys_device_remove ( enum ppc_sys_devices dev )
{
unsigned int i ;
if ( ppc_sys_inited ) {
platform_device_unregister ( & ppc_sys_platform_devices [ dev ] ) ;
} else {
if ( cur_ppc_sys_spec = = NULL )
return ;
for ( i = 0 ; i < cur_ppc_sys_spec - > num_devices ; i + + )
if ( cur_ppc_sys_spec - > device_list [ i ] = = dev )
cur_ppc_sys_spec - > device_list [ i ] = - 1 ;
}
}
2006-01-20 22:22:34 +03:00
/* Platform-notify mapping
* Helper function for BSP code to assign board - specific platfom - divice bits
*/
void platform_notify_map ( const struct platform_notify_dev_map * map ,
struct device * dev )
{
struct platform_device * pdev ;
int len , idx ;
const char * s ;
/* do nothing if no device or no bus_id */
if ( ! dev | | ! dev - > bus_id )
return ;
/* call per device map */
while ( map - > bus_id ! = NULL ) {
idx = - 1 ;
s = strrchr ( dev - > bus_id , ' . ' ) ;
2006-04-03 15:26:32 +04:00
if ( s ! = NULL ) {
2006-01-20 22:22:34 +03:00
idx = ( int ) simple_strtol ( s + 1 , NULL , 10 ) ;
2006-04-03 15:26:32 +04:00
len = s - dev - > bus_id ;
} else {
2006-01-20 22:22:34 +03:00
s = dev - > bus_id ;
2006-04-03 15:26:32 +04:00
len = strlen ( dev - > bus_id ) ;
}
2006-01-20 22:22:34 +03:00
if ( ! strncmp ( dev - > bus_id , map - > bus_id , len ) ) {
pdev = container_of ( dev , struct platform_device , dev ) ;
map - > rtn ( pdev , idx ) ;
}
map + + ;
}
}
/*
Function assignment stuff .
Intended to work as follows :
the device name defined in foo_devices . c will be concatenated with : " func " ,
where func is string map of respective function from platfom_device_func enum
The PPC_SYS_FUNC_DUMMY function is intended to remove all assignments , making the device to appear
in platform bus with unmodified name .
*/
/*
Here we ' ll replace . name pointers with fixed - lenght strings
Hereby , this should be called * before * any func stuff triggeded .
*/
void ppc_sys_device_initfunc ( void )
{
int i ;
const char * name ;
static char new_names [ NUM_PPC_SYS_DEVS ] [ BUS_ID_SIZE ] ;
enum ppc_sys_devices cur_dev ;
/* If inited yet, do nothing */
if ( ppc_sys_func_inited )
return ;
for ( i = 0 ; i < cur_ppc_sys_spec - > num_devices ; i + + ) {
if ( ( cur_dev = cur_ppc_sys_spec - > device_list [ i ] ) < 0 )
continue ;
if ( ppc_sys_platform_devices [ cur_dev ] . name ) {
/*backup name */
name = ppc_sys_platform_devices [ cur_dev ] . name ;
strlcpy ( new_names [ i ] , name , BUS_ID_SIZE ) ;
ppc_sys_platform_devices [ cur_dev ] . name = new_names [ i ] ;
}
}
ppc_sys_func_inited = 1 ;
}
/*The "engine" of the func stuff. Here we either concat specified function string description
to the name , or remove it if PPC_SYS_FUNC_DUMMY parameter is passed here */
void ppc_sys_device_setfunc ( enum ppc_sys_devices dev ,
enum platform_device_func func )
{
char * s ;
char * name = ( char * ) ppc_sys_platform_devices [ dev ] . name ;
char tmp [ BUS_ID_SIZE ] ;
if ( ! ppc_sys_func_inited ) {
printk ( KERN_ERR " Unable to alter function - not inited! \n " ) ;
return ;
}
if ( ppc_sys_inited ) {
platform_device_unregister ( & ppc_sys_platform_devices [ dev ] ) ;
}
if ( ( s = ( char * ) strchr ( name , ' : ' ) ) ! = NULL ) { /* reassign */
/* Either change the name after ':' or remove func modifications */
if ( func ! = PPC_SYS_FUNC_DUMMY )
strlcpy ( s + 1 , ppc_sys_func_names [ func ] , BUS_ID_SIZE ) ;
else
* s = 0 ;
} else if ( func ! = PPC_SYS_FUNC_DUMMY ) {
/* do assignment if it is not just "clear" request */
sprintf ( tmp , " %s:%s " , name , ppc_sys_func_names [ func ] ) ;
strlcpy ( name , tmp , BUS_ID_SIZE ) ;
}
if ( ppc_sys_inited ) {
platform_device_register ( & ppc_sys_platform_devices [ dev ] ) ;
}
}
void ppc_sys_device_disable ( enum ppc_sys_devices dev )
{
BUG_ON ( cur_ppc_sys_spec = = NULL ) ;
/*Check if it is enabled*/
if ( ! ( cur_ppc_sys_spec - > config [ dev ] & PPC_SYS_CONFIG_DISABLED ) ) {
if ( ppc_sys_inited ) {
platform_device_unregister ( & ppc_sys_platform_devices [ dev ] ) ;
}
cur_ppc_sys_spec - > config [ dev ] | = PPC_SYS_CONFIG_DISABLED ;
}
}
void ppc_sys_device_enable ( enum ppc_sys_devices dev )
{
BUG_ON ( cur_ppc_sys_spec = = NULL ) ;
/*Check if it is disabled*/
if ( cur_ppc_sys_spec - > config [ dev ] & PPC_SYS_CONFIG_DISABLED ) {
if ( ppc_sys_inited ) {
platform_device_register ( & ppc_sys_platform_devices [ dev ] ) ;
}
cur_ppc_sys_spec - > config [ dev ] & = ~ PPC_SYS_CONFIG_DISABLED ;
}
}
void ppc_sys_device_enable_all ( void )
{
enum ppc_sys_devices cur_dev ;
int i ;
for ( i = 0 ; i < cur_ppc_sys_spec - > num_devices ; i + + ) {
cur_dev = cur_ppc_sys_spec - > device_list [ i ] ;
ppc_sys_device_enable ( cur_dev ) ;
}
}
void ppc_sys_device_disable_all ( void )
{
enum ppc_sys_devices cur_dev ;
int i ;
for ( i = 0 ; i < cur_ppc_sys_spec - > num_devices ; i + + ) {
cur_dev = cur_ppc_sys_spec - > device_list [ i ] ;
ppc_sys_device_disable ( cur_dev ) ;
}
}
2005-04-17 02:20:36 +04:00
static int __init ppc_sys_init ( void )
{
unsigned int i , dev_id , ret = 0 ;
BUG_ON ( cur_ppc_sys_spec = = NULL ) ;
for ( i = 0 ; i < cur_ppc_sys_spec - > num_devices ; i + + ) {
dev_id = cur_ppc_sys_spec - > device_list [ i ] ;
2006-01-20 22:22:34 +03:00
if ( ( dev_id ! = - 1 ) & &
! ( cur_ppc_sys_spec - > config [ dev_id ] & PPC_SYS_CONFIG_DISABLED ) ) {
2005-04-17 02:20:36 +04:00
if ( ppc_sys_device_fixup ! = NULL )
ppc_sys_device_fixup ( & ppc_sys_platform_devices
[ dev_id ] ) ;
if ( platform_device_register
( & ppc_sys_platform_devices [ dev_id ] ) ) {
ret = 1 ;
printk ( KERN_ERR
" unable to register device %d \n " ,
dev_id ) ;
}
}
}
ppc_sys_inited = 1 ;
return ret ;
}
subsys_initcall ( ppc_sys_init ) ;