2011-04-15 02:31:58 +04:00
/*
* Copyright 2010 Benjamin Herrenschmidt , IBM Corp
* < benh @ kernel . crashing . org >
* and David Gibson , IBM Corporation .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
# include <linux/debugfs.h>
# include <linux/slab.h>
2011-05-27 18:46:24 +04:00
# include <linux/export.h>
2011-04-15 02:31:58 +04:00
# include <asm/prom.h>
# include <asm/scom.h>
const struct scom_controller * scom_controller ;
EXPORT_SYMBOL_GPL ( scom_controller ) ;
struct device_node * scom_find_parent ( struct device_node * node )
{
struct device_node * par , * tmp ;
const u32 * p ;
for ( par = of_node_get ( node ) ; par ; ) {
if ( of_get_property ( par , " scom-controller " , NULL ) )
break ;
p = of_get_property ( par , " scom-parent " , NULL ) ;
tmp = par ;
if ( p = = NULL )
par = of_get_parent ( par ) ;
else
par = of_find_node_by_phandle ( * p ) ;
of_node_put ( tmp ) ;
}
return par ;
}
EXPORT_SYMBOL_GPL ( scom_find_parent ) ;
scom_map_t scom_map_device ( struct device_node * dev , int index )
{
struct device_node * parent ;
unsigned int cells , size ;
const u32 * prop ;
u64 reg , cnt ;
scom_map_t ret ;
parent = scom_find_parent ( dev ) ;
if ( parent = = NULL )
return 0 ;
prop = of_get_property ( parent , " #scom-cells " , NULL ) ;
cells = prop ? * prop : 1 ;
prop = of_get_property ( dev , " scom-reg " , & size ) ;
if ( ! prop )
return 0 ;
size > > = 2 ;
if ( index > = ( size / ( 2 * cells ) ) )
return 0 ;
reg = of_read_number ( & prop [ index * cells * 2 ] , cells ) ;
cnt = of_read_number ( & prop [ index * cells * 2 + cells ] , cells ) ;
ret = scom_map ( parent , reg , cnt ) ;
of_node_put ( parent ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( scom_map_device ) ;
# ifdef CONFIG_SCOM_DEBUGFS
struct scom_debug_entry {
struct device_node * dn ;
unsigned long addr ;
scom_map_t map ;
spinlock_t lock ;
char name [ 8 ] ;
struct debugfs_blob_wrapper blob ;
} ;
static int scom_addr_set ( void * data , u64 val )
{
struct scom_debug_entry * ent = data ;
ent - > addr = 0 ;
scom_unmap ( ent - > map ) ;
ent - > map = scom_map ( ent - > dn , val , 1 ) ;
if ( scom_map_ok ( ent - > map ) )
ent - > addr = val ;
else
return - EFAULT ;
return 0 ;
}
static int scom_addr_get ( void * data , u64 * val )
{
struct scom_debug_entry * ent = data ;
* val = ent - > addr ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( scom_addr_fops , scom_addr_get , scom_addr_set ,
" 0x%llx \n " ) ;
static int scom_val_set ( void * data , u64 val )
{
struct scom_debug_entry * ent = data ;
if ( ! scom_map_ok ( ent - > map ) )
return - EFAULT ;
scom_write ( ent - > map , 0 , val ) ;
return 0 ;
}
static int scom_val_get ( void * data , u64 * val )
{
struct scom_debug_entry * ent = data ;
if ( ! scom_map_ok ( ent - > map ) )
return - EFAULT ;
* val = scom_read ( ent - > map , 0 ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( scom_val_fops , scom_val_get , scom_val_set ,
" 0x%llx \n " ) ;
static int scom_debug_init_one ( struct dentry * root , struct device_node * dn ,
int i )
{
struct scom_debug_entry * ent ;
struct dentry * dir ;
ent = kzalloc ( sizeof ( * ent ) , GFP_KERNEL ) ;
if ( ! ent )
return - ENOMEM ;
ent - > dn = of_node_get ( dn ) ;
ent - > map = SCOM_MAP_INVALID ;
spin_lock_init ( & ent - > lock ) ;
snprintf ( ent - > name , 8 , " scom%d " , i ) ;
ent - > blob . data = dn - > full_name ;
ent - > blob . size = strlen ( dn - > full_name ) ;
dir = debugfs_create_dir ( ent - > name , root ) ;
if ( ! dir ) {
of_node_put ( dn ) ;
kfree ( ent ) ;
return - 1 ;
}
debugfs_create_file ( " addr " , 0600 , dir , ent , & scom_addr_fops ) ;
debugfs_create_file ( " value " , 0600 , dir , ent , & scom_val_fops ) ;
debugfs_create_blob ( " path " , 0400 , dir , & ent - > blob ) ;
return 0 ;
}
static int scom_debug_init ( void )
{
struct device_node * dn ;
struct dentry * root ;
int i , rc ;
root = debugfs_create_dir ( " scom " , powerpc_debugfs_root ) ;
if ( ! root )
return - 1 ;
i = rc = 0 ;
for_each_node_with_property ( dn , " scom-controller " )
rc | = scom_debug_init_one ( root , dn , i + + ) ;
return rc ;
}
device_initcall ( scom_debug_init ) ;
# endif /* CONFIG_SCOM_DEBUGFS */