2008-11-18 17:48:22 +08:00
/*
* arch / blackfin / kernel / cplbinfo . c - display CPLB status
*
* Copyright 2004 - 2008 Analog Devices Inc .
2009-09-24 14:11:24 +00:00
*
2008-11-18 17:48:22 +08:00
* Licensed under the GPL - 2 or later .
*/
2009-01-07 23:14:39 +08:00
# include <linux/ctype.h>
2008-11-18 17:48:22 +08:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/proc_fs.h>
2009-01-07 23:14:39 +08:00
# include <linux/seq_file.h>
2008-11-18 17:48:22 +08:00
# include <linux/uaccess.h>
# include <asm/cplbinit.h>
# include <asm/blackfin.h>
2009-01-07 23:14:39 +08:00
static char const page_strtbl [ ] [ 3 ] = { " 1K " , " 4K " , " 1M " , " 4M " } ;
2008-11-18 17:48:22 +08:00
# define page(flags) (((flags) & 0x30000) >> 16)
# define strpage(flags) page_strtbl[page(flags)]
2009-01-07 23:14:39 +08:00
struct cplbinfo_data {
loff_t pos ;
char cplb_type ;
u32 mem_control ;
2008-11-18 17:48:22 +08:00
struct cplb_entry * tbl ;
int switched ;
2009-01-07 23:14:39 +08:00
} ;
2008-11-18 17:48:22 +08:00
2009-01-07 23:14:39 +08:00
static void cplbinfo_print_header ( struct seq_file * m )
{
seq_printf ( m , " Index \t Address \t \t Data \t Size \t U/RD \t U/WR \t S/WR \t Switch \n " ) ;
}
2008-11-18 17:48:22 +08:00
2009-01-07 23:14:39 +08:00
static int cplbinfo_nomore ( struct cplbinfo_data * cdata )
{
return cdata - > pos > = MAX_CPLBS ;
}
static int cplbinfo_show ( struct seq_file * m , void * p )
{
struct cplbinfo_data * cdata ;
unsigned long data , addr ;
loff_t pos ;
cdata = p ;
pos = cdata - > pos ;
addr = cdata - > tbl [ pos ] . addr ;
data = cdata - > tbl [ pos ] . data ;
seq_printf ( m ,
" %d \t 0x%08lx \t %05lx \t %s \t %c \t %c \t %c \t %c \n " ,
( int ) pos , addr , data , strpage ( data ) ,
( data & CPLB_USER_RD ) ? ' Y ' : ' N ' ,
( data & CPLB_USER_WR ) ? ' Y ' : ' N ' ,
( data & CPLB_SUPV_WR ) ? ' Y ' : ' N ' ,
pos < cdata - > switched ? ' N ' : ' Y ' ) ;
2008-11-18 17:48:22 +08:00
2009-01-07 23:14:39 +08:00
return 0 ;
}
static void cplbinfo_seq_init ( struct cplbinfo_data * cdata , unsigned int cpu )
{
if ( cdata - > cplb_type = = ' I ' ) {
cdata - > mem_control = bfin_read_IMEM_CONTROL ( ) ;
cdata - > tbl = icplb_tbl [ cpu ] ;
cdata - > switched = first_switched_icplb ;
} else {
cdata - > mem_control = bfin_read_DMEM_CONTROL ( ) ;
cdata - > tbl = dcplb_tbl [ cpu ] ;
cdata - > switched = first_switched_dcplb ;
}
2008-11-18 17:48:22 +08:00
}
2009-01-07 23:14:39 +08:00
static void * cplbinfo_start ( struct seq_file * m , loff_t * pos )
{
struct cplbinfo_data * cdata = m - > private ;
if ( ! * pos ) {
seq_printf ( m , " %cCPLBs are %sabled: 0x%x \n " , cdata - > cplb_type ,
( cdata - > mem_control & ENDCPLB ? " en " : " dis " ) ,
cdata - > mem_control ) ;
cplbinfo_print_header ( m ) ;
} else if ( cplbinfo_nomore ( cdata ) )
return NULL ;
get_cpu ( ) ;
return cdata ;
2008-11-18 17:48:22 +08:00
}
2009-01-07 23:14:39 +08:00
static void * cplbinfo_next ( struct seq_file * m , void * p , loff_t * pos )
{
struct cplbinfo_data * cdata = p ;
cdata - > pos = + + ( * pos ) ;
if ( cplbinfo_nomore ( cdata ) )
return NULL ;
else
return cdata ;
}
2008-11-18 17:48:22 +08:00
2009-01-07 23:14:39 +08:00
static void cplbinfo_stop ( struct seq_file * m , void * p )
2008-11-18 17:48:22 +08:00
{
2009-01-07 23:14:39 +08:00
put_cpu ( ) ;
2008-11-18 17:48:22 +08:00
}
2009-01-07 23:14:39 +08:00
static const struct seq_operations cplbinfo_sops = {
. start = cplbinfo_start ,
. next = cplbinfo_next ,
. stop = cplbinfo_stop ,
. show = cplbinfo_show ,
} ;
2009-09-21 16:12:05 +00:00
# define CPLBINFO_DCPLB_FLAG 0x80000000
2009-01-07 23:14:39 +08:00
static int cplbinfo_open ( struct inode * inode , struct file * file )
2008-11-18 17:48:22 +08:00
{
2009-09-21 16:12:05 +00:00
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
char cplb_type ;
2009-01-07 23:14:39 +08:00
unsigned int cpu ;
int ret ;
struct seq_file * m ;
struct cplbinfo_data * cdata ;
2009-09-21 16:12:05 +00:00
cpu = ( unsigned int ) pde - > data ;
cplb_type = cpu & CPLBINFO_DCPLB_FLAG ? ' D ' : ' I ' ;
cpu & = ~ CPLBINFO_DCPLB_FLAG ;
2009-01-07 23:14:39 +08:00
if ( ! cpu_online ( cpu ) )
return - ENODEV ;
ret = seq_open_private ( file , & cplbinfo_sops , sizeof ( * cdata ) ) ;
if ( ret )
return ret ;
m = file - > private_data ;
cdata = m - > private ;
cdata - > pos = 0 ;
2009-09-21 16:12:05 +00:00
cdata - > cplb_type = cplb_type ;
2009-01-07 23:14:39 +08:00
cplbinfo_seq_init ( cdata , cpu ) ;
return 0 ;
2008-11-18 17:48:22 +08:00
}
2009-01-07 23:14:39 +08:00
static const struct file_operations cplbinfo_fops = {
. open = cplbinfo_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release_private ,
} ;
2008-11-18 17:48:22 +08:00
static int __init cplbinfo_init ( void )
{
2009-01-07 23:14:39 +08:00
struct proc_dir_entry * cplb_dir , * cpu_dir ;
char buf [ 10 ] ;
2008-11-18 17:48:22 +08:00
unsigned int cpu ;
2009-01-07 23:14:39 +08:00
cplb_dir = proc_mkdir ( " cplbinfo " , NULL ) ;
if ( ! cplb_dir )
2008-11-18 17:48:22 +08:00
return - ENOMEM ;
2009-01-07 23:14:39 +08:00
for_each_possible_cpu ( cpu ) {
sprintf ( buf , " cpu%i " , cpu ) ;
cpu_dir = proc_mkdir ( buf , cplb_dir ) ;
if ( ! cpu_dir )
2008-11-18 17:48:22 +08:00
return - ENOMEM ;
2009-09-21 16:12:05 +00:00
proc_create_data ( " icplb " , S_IRUGO , cpu_dir , & cplbinfo_fops ,
( void * ) cpu ) ;
proc_create_data ( " dcplb " , S_IRUGO , cpu_dir , & cplbinfo_fops ,
( void * ) ( cpu | CPLBINFO_DCPLB_FLAG ) ) ;
2008-11-18 17:48:22 +08:00
}
return 0 ;
}
late_initcall ( cplbinfo_init ) ;