2010-09-13 18:05:04 +04:00
/*
* Copyright ( C ) ST - Ericsson SA 2010
*
* Author : Mattias Wallin < mattias . wallin @ stericsson . com > for ST - Ericsson .
* License Terms : GNU General Public License v2
*/
# include <linux/seq_file.h>
# include <linux/uaccess.h>
# include <linux/fs.h>
2011-07-03 23:13:27 +04:00
# include <linux/module.h>
2010-09-13 18:05:04 +04:00
# include <linux/debugfs.h>
# include <linux/platform_device.h>
# include <linux/mfd/abx500.h>
2011-12-02 17:16:33 +04:00
# include <linux/mfd/abx500/ab8500.h>
2010-09-13 18:05:04 +04:00
static u32 debug_bank ;
static u32 debug_address ;
/**
* struct ab8500_reg_range
* @ first : the first address of the range
* @ last : the last address of the range
* @ perm : access permissions for the range
*/
struct ab8500_reg_range {
2010-11-26 15:06:39 +03:00
u8 first ;
u8 last ;
u8 perm ;
2010-09-13 18:05:04 +04:00
} ;
/**
2012-06-20 16:56:38 +04:00
* struct ab8500_prcmu_ranges
2010-09-13 18:05:04 +04:00
* @ num_ranges : the number of ranges in the list
* @ bankid : bank identifier
* @ range : the list of register ranges
*/
2012-06-20 16:56:38 +04:00
struct ab8500_prcmu_ranges {
2010-11-26 15:06:39 +03:00
u8 num_ranges ;
u8 bankid ;
const struct ab8500_reg_range * range ;
2010-09-13 18:05:04 +04:00
} ;
# define AB8500_NAME_STRING "ab8500"
# define AB8500_NUM_BANKS 22
# define AB8500_REV_REG 0x80
2012-06-20 16:56:38 +04:00
static struct ab8500_prcmu_ranges debug_ranges [ AB8500_NUM_BANKS ] = {
2010-11-26 15:06:39 +03:00
[ 0x0 ] = {
. num_ranges = 0 ,
. range = 0 ,
} ,
[ AB8500_SYS_CTRL1_BLOCK ] = {
. num_ranges = 3 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x02 ,
} ,
{
. first = 0x42 ,
. last = 0x42 ,
} ,
{
. first = 0x80 ,
. last = 0x81 ,
} ,
} ,
} ,
[ AB8500_SYS_CTRL2_BLOCK ] = {
. num_ranges = 4 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x0D ,
} ,
{
. first = 0x0F ,
. last = 0x17 ,
} ,
{
. first = 0x30 ,
. last = 0x30 ,
} ,
{
. first = 0x32 ,
. last = 0x33 ,
} ,
} ,
} ,
[ AB8500_REGU_CTRL1 ] = {
. num_ranges = 3 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x00 ,
} ,
{
. first = 0x03 ,
. last = 0x10 ,
} ,
{
. first = 0x80 ,
. last = 0x84 ,
} ,
} ,
} ,
[ AB8500_REGU_CTRL2 ] = {
. num_ranges = 5 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x15 ,
} ,
{
. first = 0x17 ,
. last = 0x19 ,
} ,
{
. first = 0x1B ,
. last = 0x1D ,
} ,
{
. first = 0x1F ,
. last = 0x22 ,
} ,
{
. first = 0x40 ,
. last = 0x44 ,
} ,
/* 0x80-0x8B is SIM registers and should
* not be accessed from here */
} ,
} ,
[ AB8500_USB ] = {
. num_ranges = 2 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x80 ,
. last = 0x83 ,
} ,
{
. first = 0x87 ,
. last = 0x8A ,
} ,
} ,
} ,
[ AB8500_TVOUT ] = {
. num_ranges = 9 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x12 ,
} ,
{
. first = 0x15 ,
. last = 0x17 ,
} ,
{
. first = 0x19 ,
. last = 0x21 ,
} ,
{
. first = 0x27 ,
. last = 0x2C ,
} ,
{
. first = 0x41 ,
. last = 0x41 ,
} ,
{
. first = 0x45 ,
. last = 0x5B ,
} ,
{
. first = 0x5D ,
. last = 0x5D ,
} ,
{
. first = 0x69 ,
. last = 0x69 ,
} ,
{
. first = 0x80 ,
. last = 0x81 ,
} ,
} ,
} ,
[ AB8500_DBI ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ AB8500_ECI_AV_ACC ] = {
. num_ranges = 1 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x80 ,
. last = 0x82 ,
} ,
} ,
} ,
[ 0x9 ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ AB8500_GPADC ] = {
. num_ranges = 1 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x08 ,
} ,
} ,
} ,
[ AB8500_CHARGER ] = {
. num_ranges = 8 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x03 ,
} ,
{
. first = 0x05 ,
. last = 0x05 ,
} ,
{
. first = 0x40 ,
. last = 0x40 ,
} ,
{
. first = 0x42 ,
. last = 0x42 ,
} ,
{
. first = 0x44 ,
. last = 0x44 ,
} ,
{
. first = 0x50 ,
. last = 0x55 ,
} ,
{
. first = 0x80 ,
. last = 0x82 ,
} ,
{
. first = 0xC0 ,
. last = 0xC2 ,
} ,
} ,
} ,
[ AB8500_GAS_GAUGE ] = {
. num_ranges = 3 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x00 ,
} ,
{
. first = 0x07 ,
. last = 0x0A ,
} ,
{
. first = 0x10 ,
. last = 0x14 ,
} ,
} ,
} ,
[ AB8500_AUDIO ] = {
. num_ranges = 1 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x6F ,
} ,
} ,
} ,
[ AB8500_INTERRUPT ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ AB8500_RTC ] = {
. num_ranges = 1 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x0F ,
} ,
} ,
} ,
[ AB8500_MISC ] = {
. num_ranges = 8 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x05 ,
} ,
{
. first = 0x10 ,
. last = 0x15 ,
} ,
{
. first = 0x20 ,
. last = 0x25 ,
} ,
{
. first = 0x30 ,
. last = 0x35 ,
} ,
{
. first = 0x40 ,
. last = 0x45 ,
} ,
{
. first = 0x50 ,
. last = 0x50 ,
} ,
{
. first = 0x60 ,
. last = 0x67 ,
} ,
{
. first = 0x80 ,
. last = 0x80 ,
} ,
} ,
} ,
[ 0x11 ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ 0x12 ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ 0x13 ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ 0x14 ] = {
. num_ranges = 0 ,
2010-12-13 17:06:47 +03:00
. range = NULL ,
2010-11-26 15:06:39 +03:00
} ,
[ AB8500_OTP_EMUL ] = {
. num_ranges = 1 ,
. range = ( struct ab8500_reg_range [ ] ) {
{
. first = 0x01 ,
. last = 0x0F ,
} ,
} ,
} ,
2010-09-13 18:05:04 +04:00
} ;
static int ab8500_registers_print ( struct seq_file * s , void * p )
{
2010-11-26 15:06:39 +03:00
struct device * dev = s - > private ;
unsigned int i ;
u32 bank = debug_bank ;
seq_printf ( s , AB8500_NAME_STRING " register values: \n " ) ;
seq_printf ( s , " bank %u: \n " , bank ) ;
for ( i = 0 ; i < debug_ranges [ bank ] . num_ranges ; i + + ) {
u32 reg ;
for ( reg = debug_ranges [ bank ] . range [ i ] . first ;
reg < = debug_ranges [ bank ] . range [ i ] . last ;
reg + + ) {
u8 value ;
int err ;
err = abx500_get_register_interruptible ( dev ,
( u8 ) bank , ( u8 ) reg , & value ) ;
if ( err < 0 ) {
dev_err ( dev , " ab->read fail %d \n " , err ) ;
return err ;
}
err = seq_printf ( s , " [%u/0x%02X]: 0x%02X \n " , bank ,
reg , value ) ;
if ( err < 0 ) {
dev_err ( dev , " seq_printf overflow \n " ) ;
/* Error is not returned here since
* the output is wanted in any case */
return 0 ;
}
}
}
return 0 ;
2010-09-13 18:05:04 +04:00
}
static int ab8500_registers_open ( struct inode * inode , struct file * file )
{
2010-11-26 15:06:39 +03:00
return single_open ( file , ab8500_registers_print , inode - > i_private ) ;
2010-09-13 18:05:04 +04:00
}
static const struct file_operations ab8500_registers_fops = {
2010-11-26 15:06:39 +03:00
. open = ab8500_registers_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. owner = THIS_MODULE ,
2010-09-13 18:05:04 +04:00
} ;
static int ab8500_bank_print ( struct seq_file * s , void * p )
{
2010-11-26 15:06:39 +03:00
return seq_printf ( s , " %d \n " , debug_bank ) ;
2010-09-13 18:05:04 +04:00
}
static int ab8500_bank_open ( struct inode * inode , struct file * file )
{
2010-11-26 15:06:39 +03:00
return single_open ( file , ab8500_bank_print , inode - > i_private ) ;
2010-09-13 18:05:04 +04:00
}
static ssize_t ab8500_bank_write ( struct file * file ,
2010-11-26 15:06:39 +03:00
const char __user * user_buf ,
size_t count , loff_t * ppos )
2010-09-13 18:05:04 +04:00
{
2010-11-26 15:06:39 +03:00
struct device * dev = ( ( struct seq_file * ) ( file - > private_data ) ) - > private ;
unsigned long user_bank ;
int err ;
/* Get userspace string and assure termination */
2011-06-07 00:43:32 +04:00
err = kstrtoul_from_user ( user_buf , count , 0 , & user_bank ) ;
2010-11-26 15:06:39 +03:00
if ( err )
2011-06-07 00:43:32 +04:00
return err ;
2010-11-26 15:06:39 +03:00
if ( user_bank > = AB8500_NUM_BANKS ) {
dev_err ( dev , " debugfs error input > number of banks \n " ) ;
return - EINVAL ;
}
debug_bank = user_bank ;
2011-06-07 00:43:32 +04:00
return count ;
2010-09-13 18:05:04 +04:00
}
static int ab8500_address_print ( struct seq_file * s , void * p )
{
2010-11-26 15:06:39 +03:00
return seq_printf ( s , " 0x%02X \n " , debug_address ) ;
2010-09-13 18:05:04 +04:00
}
static int ab8500_address_open ( struct inode * inode , struct file * file )
{
2010-11-26 15:06:39 +03:00
return single_open ( file , ab8500_address_print , inode - > i_private ) ;
2010-09-13 18:05:04 +04:00
}
static ssize_t ab8500_address_write ( struct file * file ,
2010-11-26 15:06:39 +03:00
const char __user * user_buf ,
size_t count , loff_t * ppos )
2010-09-13 18:05:04 +04:00
{
2010-11-26 15:06:39 +03:00
struct device * dev = ( ( struct seq_file * ) ( file - > private_data ) ) - > private ;
unsigned long user_address ;
int err ;
/* Get userspace string and assure termination */
2011-06-07 00:43:32 +04:00
err = kstrtoul_from_user ( user_buf , count , 0 , & user_address ) ;
2010-11-26 15:06:39 +03:00
if ( err )
2011-06-07 00:43:32 +04:00
return err ;
2010-11-26 15:06:39 +03:00
if ( user_address > 0xff ) {
dev_err ( dev , " debugfs error input > 0xff \n " ) ;
return - EINVAL ;
}
debug_address = user_address ;
2011-06-07 00:43:32 +04:00
return count ;
2010-09-13 18:05:04 +04:00
}
static int ab8500_val_print ( struct seq_file * s , void * p )
{
2010-11-26 15:06:39 +03:00
struct device * dev = s - > private ;
int ret ;
u8 regvalue ;
ret = abx500_get_register_interruptible ( dev ,
( u8 ) debug_bank , ( u8 ) debug_address , & regvalue ) ;
if ( ret < 0 ) {
dev_err ( dev , " abx500_get_reg fail %d, %d \n " ,
ret , __LINE__ ) ;
return - EINVAL ;
}
seq_printf ( s , " 0x%02X \n " , regvalue ) ;
return 0 ;
2010-09-13 18:05:04 +04:00
}
static int ab8500_val_open ( struct inode * inode , struct file * file )
{
2010-11-26 15:06:39 +03:00
return single_open ( file , ab8500_val_print , inode - > i_private ) ;
2010-09-13 18:05:04 +04:00
}
static ssize_t ab8500_val_write ( struct file * file ,
2010-11-26 15:06:39 +03:00
const char __user * user_buf ,
size_t count , loff_t * ppos )
2010-09-13 18:05:04 +04:00
{
2010-11-26 15:06:39 +03:00
struct device * dev = ( ( struct seq_file * ) ( file - > private_data ) ) - > private ;
unsigned long user_val ;
int err ;
/* Get userspace string and assure termination */
2011-06-07 00:43:32 +04:00
err = kstrtoul_from_user ( user_buf , count , 0 , & user_val ) ;
2010-11-26 15:06:39 +03:00
if ( err )
2011-06-07 00:43:32 +04:00
return err ;
2010-11-26 15:06:39 +03:00
if ( user_val > 0xff ) {
dev_err ( dev , " debugfs error input > 0xff \n " ) ;
return - EINVAL ;
}
err = abx500_set_register_interruptible ( dev ,
( u8 ) debug_bank , debug_address , ( u8 ) user_val ) ;
if ( err < 0 ) {
printk ( KERN_ERR " abx500_set_reg failed %d, %d " , err , __LINE__ ) ;
return - EINVAL ;
}
2011-06-07 00:43:32 +04:00
return count ;
2010-09-13 18:05:04 +04:00
}
static const struct file_operations ab8500_bank_fops = {
2010-11-26 15:06:39 +03:00
. open = ab8500_bank_open ,
. write = ab8500_bank_write ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. owner = THIS_MODULE ,
2010-09-13 18:05:04 +04:00
} ;
static const struct file_operations ab8500_address_fops = {
2010-11-26 15:06:39 +03:00
. open = ab8500_address_open ,
. write = ab8500_address_write ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. owner = THIS_MODULE ,
2010-09-13 18:05:04 +04:00
} ;
static const struct file_operations ab8500_val_fops = {
2010-11-26 15:06:39 +03:00
. open = ab8500_val_open ,
. write = ab8500_val_write ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. owner = THIS_MODULE ,
2010-09-13 18:05:04 +04:00
} ;
static struct dentry * ab8500_dir ;
static struct dentry * ab8500_reg_file ;
static struct dentry * ab8500_bank_file ;
static struct dentry * ab8500_address_file ;
static struct dentry * ab8500_val_file ;
2012-11-19 22:23:04 +04:00
static int ab8500_debug_probe ( struct platform_device * plf )
2010-09-13 18:05:04 +04:00
{
2010-11-26 15:06:39 +03:00
debug_bank = AB8500_MISC ;
debug_address = AB8500_REV_REG & 0x00FF ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
ab8500_dir = debugfs_create_dir ( AB8500_NAME_STRING , NULL ) ;
if ( ! ab8500_dir )
goto exit_no_debugfs ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
ab8500_reg_file = debugfs_create_file ( " all-bank-registers " ,
S_IRUGO , ab8500_dir , & plf - > dev , & ab8500_registers_fops ) ;
if ( ! ab8500_reg_file )
goto exit_destroy_dir ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
ab8500_bank_file = debugfs_create_file ( " register-bank " ,
2011-02-04 15:23:43 +03:00
( S_IRUGO | S_IWUSR ) , ab8500_dir , & plf - > dev , & ab8500_bank_fops ) ;
2010-11-26 15:06:39 +03:00
if ( ! ab8500_bank_file )
goto exit_destroy_reg ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
ab8500_address_file = debugfs_create_file ( " register-address " ,
2011-02-04 15:23:43 +03:00
( S_IRUGO | S_IWUSR ) , ab8500_dir , & plf - > dev ,
2010-11-26 15:06:39 +03:00
& ab8500_address_fops ) ;
if ( ! ab8500_address_file )
goto exit_destroy_bank ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
ab8500_val_file = debugfs_create_file ( " register-value " ,
2011-02-04 15:23:43 +03:00
( S_IRUGO | S_IWUSR ) , ab8500_dir , & plf - > dev , & ab8500_val_fops ) ;
2010-11-26 15:06:39 +03:00
if ( ! ab8500_val_file )
goto exit_destroy_address ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
return 0 ;
2010-09-13 18:05:04 +04:00
exit_destroy_address :
2010-11-26 15:06:39 +03:00
debugfs_remove ( ab8500_address_file ) ;
2010-09-13 18:05:04 +04:00
exit_destroy_bank :
2010-11-26 15:06:39 +03:00
debugfs_remove ( ab8500_bank_file ) ;
2010-09-13 18:05:04 +04:00
exit_destroy_reg :
2010-11-26 15:06:39 +03:00
debugfs_remove ( ab8500_reg_file ) ;
2010-09-13 18:05:04 +04:00
exit_destroy_dir :
2010-11-26 15:06:39 +03:00
debugfs_remove ( ab8500_dir ) ;
2010-09-13 18:05:04 +04:00
exit_no_debugfs :
2010-11-26 15:06:39 +03:00
dev_err ( & plf - > dev , " failed to create debugfs entries. \n " ) ;
return - ENOMEM ;
2010-09-13 18:05:04 +04:00
}
2012-11-19 22:26:01 +04:00
static int ab8500_debug_remove ( struct platform_device * plf )
2010-09-13 18:05:04 +04:00
{
2010-11-26 15:06:39 +03:00
debugfs_remove ( ab8500_val_file ) ;
debugfs_remove ( ab8500_address_file ) ;
debugfs_remove ( ab8500_bank_file ) ;
debugfs_remove ( ab8500_reg_file ) ;
debugfs_remove ( ab8500_dir ) ;
2010-09-13 18:05:04 +04:00
2010-11-26 15:06:39 +03:00
return 0 ;
2010-09-13 18:05:04 +04:00
}
static struct platform_driver ab8500_debug_driver = {
2010-11-26 15:06:39 +03:00
. driver = {
. name = " ab8500-debug " ,
. owner = THIS_MODULE ,
} ,
. probe = ab8500_debug_probe ,
2012-11-19 22:20:24 +04:00
. remove = ab8500_debug_remove
2010-09-13 18:05:04 +04:00
} ;
static int __init ab8500_debug_init ( void )
{
2010-11-26 15:06:39 +03:00
return platform_driver_register ( & ab8500_debug_driver ) ;
2010-09-13 18:05:04 +04:00
}
static void __exit ab8500_debug_exit ( void )
{
2010-11-26 15:06:39 +03:00
platform_driver_unregister ( & ab8500_debug_driver ) ;
2010-09-13 18:05:04 +04:00
}
subsys_initcall ( ab8500_debug_init ) ;
module_exit ( ab8500_debug_exit ) ;
MODULE_AUTHOR ( " Mattias WALLIN <mattias.wallin@stericsson.com " ) ;
MODULE_DESCRIPTION ( " AB8500 DEBUG " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;