2005-04-17 02:20:36 +04:00
/*
2008-01-27 20:14:45 +03:00
i2c - stub . c - I2C / SMBus chip emulator
2005-04-17 02:20:36 +04:00
Copyright ( c ) 2004 Mark M . Hoffman < mhoffman @ lightlink . com >
2014-01-29 23:40:08 +04:00
Copyright ( C ) 2007 , 2012 Jean Delvare < jdelvare @ suse . de >
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 .
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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# define DEBUG 1
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
2007-10-14 01:56:31 +04:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/i2c.h>
2007-10-14 01:56:31 +04:00
# define MAX_CHIPS 10
2010-05-21 20:40:56 +04:00
# define STUB_FUNC (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
I2C_FUNC_SMBUS_I2C_BLOCK )
2006-08-14 01:46:44 +04:00
2007-10-14 01:56:31 +04:00
static unsigned short chip_addr [ MAX_CHIPS ] ;
module_param_array ( chip_addr , ushort , NULL , S_IRUGO ) ;
MODULE_PARM_DESC ( chip_addr ,
2008-04-30 01:11:37 +04:00
" Chip addresses (up to 10, between 0x03 and 0x77) " ) ;
2007-10-14 01:56:31 +04:00
2010-05-21 20:40:56 +04:00
static unsigned long functionality = STUB_FUNC ;
2009-12-06 19:06:29 +03:00
module_param ( functionality , ulong , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( functionality , " Override functionality bitfield " ) ;
2007-10-14 01:56:31 +04:00
struct stub_chip {
u8 pointer ;
2008-01-27 20:14:45 +03:00
u16 words [ 256 ] ; /* Byte operations use the LSB as per SMBus
specification */
2007-10-14 01:56:31 +04:00
} ;
static struct stub_chip * stub_chips ;
2005-04-17 02:20:36 +04:00
2008-07-15 00:38:25 +04:00
/* Return negative errno on error. */
2012-10-29 00:37:00 +04:00
static s32 stub_xfer ( struct i2c_adapter * adap , u16 addr , unsigned short flags ,
char read_write , u8 command , int size , union i2c_smbus_data * data )
2005-04-17 02:20:36 +04:00
{
s32 ret ;
2009-12-06 19:06:28 +03:00
int i , len ;
2007-10-14 01:56:31 +04:00
struct stub_chip * chip = NULL ;
/* Search for the right chip */
for ( i = 0 ; i < MAX_CHIPS & & chip_addr [ i ] ; i + + ) {
if ( addr = = chip_addr [ i ] ) {
chip = stub_chips + i ;
break ;
}
}
if ( ! chip )
2006-08-14 01:46:44 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
switch ( size ) {
case I2C_SMBUS_QUICK :
dev_dbg ( & adap - > dev , " smbus quick - addr 0x%02x \n " , addr ) ;
ret = 0 ;
break ;
case I2C_SMBUS_BYTE :
if ( read_write = = I2C_SMBUS_WRITE ) {
2007-10-14 01:56:31 +04:00
chip - > pointer = command ;
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" smbus byte - addr 0x%02x, wrote 0x%02x. \n " ,
addr , command ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-27 20:14:45 +03:00
data - > byte = chip - > words [ chip - > pointer + + ] & 0xff ;
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" smbus byte - addr 0x%02x, read 0x%02x. \n " ,
addr , data - > byte ) ;
2005-04-17 02:20:36 +04:00
}
ret = 0 ;
break ;
case I2C_SMBUS_BYTE_DATA :
if ( read_write = = I2C_SMBUS_WRITE ) {
2008-01-27 20:14:45 +03:00
chip - > words [ command ] & = 0xff00 ;
chip - > words [ command ] | = data - > byte ;
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x. \n " ,
addr , data - > byte , command ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-01-27 20:14:45 +03:00
data - > byte = chip - > words [ command ] & 0xff ;
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x. \n " ,
addr , data - > byte , command ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-14 01:56:31 +04:00
chip - > pointer = command + 1 ;
2005-04-17 02:20:36 +04:00
ret = 0 ;
break ;
case I2C_SMBUS_WORD_DATA :
if ( read_write = = I2C_SMBUS_WRITE ) {
2007-10-14 01:56:31 +04:00
chip - > words [ command ] = data - > word ;
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x. \n " ,
addr , data - > word , command ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-10-14 01:56:31 +04:00
data - > word = chip - > words [ command ] ;
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" smbus word data - addr 0x%02x, read 0x%04x at 0x%02x. \n " ,
addr , data - > word , command ) ;
2005-04-17 02:20:36 +04:00
}
ret = 0 ;
break ;
2009-12-06 19:06:28 +03:00
case I2C_SMBUS_I2C_BLOCK_DATA :
len = data - > block [ 0 ] ;
if ( read_write = = I2C_SMBUS_WRITE ) {
for ( i = 0 ; i < len ; i + + ) {
chip - > words [ command + i ] & = 0xff00 ;
chip - > words [ command + i ] | = data - > block [ 1 + i ] ;
}
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x. \n " ,
addr , len , command ) ;
2009-12-06 19:06:28 +03:00
} else {
for ( i = 0 ; i < len ; i + + ) {
data - > block [ 1 + i ] =
chip - > words [ command + i ] & 0xff ;
}
2012-10-29 00:37:00 +04:00
dev_dbg ( & adap - > dev ,
" i2c block data - addr 0x%02x, read %d bytes at 0x%02x. \n " ,
addr , len , command ) ;
2009-12-06 19:06:28 +03:00
}
ret = 0 ;
break ;
2005-04-17 02:20:36 +04:00
default :
dev_dbg ( & adap - > dev , " Unsupported I2C/SMBus command \n " ) ;
2008-07-15 00:38:25 +04:00
ret = - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
break ;
} /* switch (size) */
return ret ;
}
static u32 stub_func ( struct i2c_adapter * adapter )
{
2010-05-21 20:40:56 +04:00
return STUB_FUNC & functionality ;
2005-04-17 02:20:36 +04:00
}
2006-09-04 00:39:46 +04:00
static const struct i2c_algorithm smbus_algorithm = {
2005-04-17 02:20:36 +04:00
. functionality = stub_func ,
. smbus_xfer = stub_xfer ,
} ;
static struct i2c_adapter stub_adapter = {
. owner = THIS_MODULE ,
2008-07-15 00:38:29 +04:00
. class = I2C_CLASS_HWMON | I2C_CLASS_SPD ,
2005-04-17 02:20:36 +04:00
. algo = & smbus_algorithm ,
. name = " SMBus stub driver " ,
} ;
static int __init i2c_stub_init ( void )
{
2007-10-14 01:56:31 +04:00
int i , ret ;
if ( ! chip_addr [ 0 ] ) {
2012-10-29 00:37:00 +04:00
pr_err ( " i2c-stub: Please specify a chip address \n " ) ;
2006-08-14 01:46:44 +04:00
return - ENODEV ;
}
2007-10-14 01:56:31 +04:00
for ( i = 0 ; i < MAX_CHIPS & & chip_addr [ i ] ; i + + ) {
if ( chip_addr [ i ] < 0x03 | | chip_addr [ i ] > 0x77 ) {
2012-10-29 00:37:00 +04:00
pr_err ( " i2c-stub: Invalid chip address 0x%02x \n " ,
chip_addr [ i ] ) ;
2007-10-14 01:56:31 +04:00
return - EINVAL ;
}
2012-10-29 00:37:00 +04:00
pr_info ( " i2c-stub: Virtual chip at 0x%02x \n " , chip_addr [ i ] ) ;
2006-08-14 01:46:44 +04:00
}
2007-10-14 01:56:31 +04:00
/* Allocate memory for all chips at once */
stub_chips = kzalloc ( i * sizeof ( struct stub_chip ) , GFP_KERNEL ) ;
if ( ! stub_chips ) {
2012-10-29 00:37:00 +04:00
pr_err ( " i2c-stub: Out of memory \n " ) ;
2007-10-14 01:56:31 +04:00
return - ENOMEM ;
}
ret = i2c_add_adapter ( & stub_adapter ) ;
if ( ret )
kfree ( stub_chips ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
static void __exit i2c_stub_exit ( void )
{
i2c_del_adapter ( & stub_adapter ) ;
2007-10-14 01:56:31 +04:00
kfree ( stub_chips ) ;
2005-04-17 02:20:36 +04:00
}
MODULE_AUTHOR ( " Mark M. Hoffman <mhoffman@lightlink.com> " ) ;
MODULE_DESCRIPTION ( " I2C stub driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( i2c_stub_init ) ;
module_exit ( i2c_stub_exit ) ;