Improve i2c driver slave delay, thanks to Fabrizio Sciarra that provide it * support for master/slave delay (provided patch) * remove some printk that spam logs * introduce new symbols ETRAX_I2C_DYN_ALLOC and ETRAX_I2C_SLAVE_DELAY * cleanup a bit the driver * dump release number

SVN-Revision: 17954
owl
Claudio Mignanti 2009-10-06 09:20:30 +00:00
parent 3c5feca158
commit 4b724681e0
2 changed files with 317 additions and 211 deletions

View File

@ -9,26 +9,21 @@
*!
*!***************************************************************************/
#define DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
//#undef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
/******************** INCLUDE FILES SECTION ****************************/
#include <linux/module.h>
#include <linux/fs.h>
/**GVC**/
#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
#ifdef CONFIG_ETRAX_I2C_DYN_ALLOC
#include <linux/types.h> /* for dev_t */
#include <linux/cdev.h> /* for struct cdev */
#endif
/**END GVC**/
#include <linux/device.h>
#include "etraxi2c.h"
/**GVC**/
#include "i2c_errno.h"
/**END GVC**/
#include <asm/io.h>
#include <asm/delay.h>
@ -37,7 +32,7 @@
#include "i2c_gvc.h"
MODULE_DESCRIPTION( "I2C Device Driver - 1.1" );
MODULE_DESCRIPTION( "I2C Device Driver - 2.3" );
/*!*********************************************************************
*!History I2C driver Geert Vancompernolle
@ -57,15 +52,10 @@ MODULE_LICENSE( "GPL" );
#define D( x )
/**GVC**/
#ifndef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
/**END GVC**/
#ifndef CONFIG_ETRAX_I2C_DYN_ALLOC
#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */
/**GVC**/
#endif
/**END GVC**/
/**GVC**/
#define WAITONEUS 1
/* Following are abbreviations taken from Philips I2C standard */
/* Values are representing time in us and are rounded to next whole number, if relevant */
@ -78,11 +68,14 @@ MODULE_LICENSE( "GPL" );
#define TSUSTO 4 /* Set-up time for STOP condition */
#define TBUF 5 /* Bus-free time between STOP and START condition */
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
#define MAXSCLRETRIES 100
#endif
#define MAXBUSFREERETRIES 5
#define MAXRETRIES 3
#define WRITEADDRESS_MASK ( 0xFE )
#define READADDRESS_MASK ( 0x01 )
/**END GVC**/
#define SCL_HIGH 1
#define SCL_LOW 0
@ -109,6 +102,13 @@ MODULE_LICENSE( "GPL" );
#define i2c_sda_dir_in() \
REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 0 )
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
#define i2c_scl_dir_out() \
REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, SCLBIT, 1 )
#define i2c_scl_dir_in() \
REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, SCLBIT, 0 )
#endif
/* control the i2c clock and data signals */
#define i2c_set_scl( x ) \
REG_SHADOW_SET( R_PORT_PB_DATA, port_pb_data_shadow, SCLBIT, x )
@ -118,10 +118,8 @@ MODULE_LICENSE( "GPL" );
/* read status of SDA bit from the i2c interface */
#define i2c_sda_is_high() ( ( ( *R_PORT_PB_READ & ( 1 << SDABIT ) ) ) >> SDABIT )
/**GVC**/
/* read status of SCL bit from the i2c interface */
#define i2c_scl_is_high() ( ( ( *R_PORT_PB_READ & ( 1 << SCLBIT ) ) ) >> SCLBIT )
/**END GVC**/
#else
/* enable or disable the i2c interface */
@ -160,19 +158,13 @@ MODULE_LICENSE( "GPL" );
/****************** STATIC (file scope) VARIABLES **********************/
static DEFINE_SPINLOCK( i2c_lock ); /* Protect directions etc */
/**GVC**/
#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
static const char i2c_name[] = "i2cgvc";
#else
static const char i2c_name[] = "i2c";
#endif
/**END GVC**/
/****************** PROTOTYPING SECTION *************************/
static int i2c_open( struct inode *inode, struct file *filp );
static int i2c_release( struct inode *inode, struct file *filp );
/**GVC**/
static int i2c_command( unsigned char slave
, unsigned char* wbuf
, unsigned char wlen
@ -181,7 +173,6 @@ static int i2c_command( unsigned char slave
);
static int i2c_bus_free_check( unsigned char maxretries );
static void i2c_finalise( const char* text, unsigned long irqflags );
/**END GVC**/
/************************************************************************/
@ -267,7 +258,6 @@ static int i2c_ioctl( struct inode *inode
RetVal = i2c_readreg( I2C_ARGSLAVE( arg ), I2C_ARGREG( arg ) );
break;
/**GVC**/
/* New functions added by GVC */
case I2C_READ:
copy_from_user( (char*)&i2cdata, (char*)arg, sizeof( I2C_DATA ) );
@ -327,7 +317,6 @@ static int i2c_ioctl( struct inode *inode
}
copy_to_user( (char*)arg, (char*)&i2cdata, sizeof( I2C_DATA ) );
break;
/**END GVC**/
default:
RetVal = -EINVAL;
@ -458,7 +447,7 @@ static int i2c_command( unsigned char slave
while ( wlen-- )
{
/* send register data */
if ( EI2CNOERRORS != i2c_outbyte( *wbuf ) )
if ( EI2CNOERRORS != i2c_outbyte( *wbuf ) && wlen )
{
return ( i2c_finalise( "I2C: EI2CSENDDATA\n", irqflags )
, EI2CSENDDATA
@ -525,7 +514,7 @@ static int i2c_command( unsigned char slave
/* Generate final stop condition */
if ( EI2CNOERRORS != i2c_stop() )
{
return ( i2c_finalise( "I2C: EI2CSTOPCOND\n", irqflags )
return ( i2c_finalise( "I2C CMD: EI2CSTOPCOND\n", irqflags )
, EI2CSTOPCOND
);
}
@ -700,15 +689,16 @@ int __init i2c_init( void )
*#
*#---------------------------------------------------------------------------
*/
static struct class *i2c_class;
static int __init i2c_register( void )
{
int res;
/**GVC**/
#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
#ifdef CONFIG_ETRAX_I2C_DYN_ALLOC
dev_t devt;
struct cdev *my_i2cdev = NULL;
#endif
/**END GVC**/
res = i2c_init();
@ -717,8 +707,7 @@ static int __init i2c_register( void )
return res;
}
/**GVC**/
#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
#ifdef CONFIG_ETRAX_I2C_DYN_ALLOC
res = alloc_chrdev_region( &devt, 0, 1, i2c_name );
if ( res < 0 )
@ -739,8 +728,9 @@ static int __init i2c_register( void )
printk( KERN_DEBUG "I2C: EI2CDADDFAIL\n" );
return ( res );
}
int i2c_major = MAJOR( devt );
#else
/**END GVC**/
res = register_chrdev( I2C_MAJOR, i2c_name, &i2c_fops );
if ( res < 0 )
@ -748,23 +738,20 @@ static int __init i2c_register( void )
printk( KERN_ERR "i2c: couldn't get a major number.\n" );
return res;
}
/**GVC**/
int i2c_major = I2C_MAJOR;
#endif
/**END GVC**/
printk( KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n" );
printk( KERN_INFO "I2C: driver v2.3, (c) 1999-2004 Axis Communications AB\n" );
printk( KERN_INFO "I2C: Improvements by Geert Vancompernolle, Positive Going, BK srl\n" );
/**GVC**/
printk( KERN_INFO " ==> Improvements done by Geert Vancompernolle - December 2006\n" );
#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC
printk( KERN_INFO "I2C Major: %d / I2C Name: %s\n", MAJOR( devt ), i2c_name );
#else
/**END GVC**/
printk( KERN_INFO "I2C Major: %d / I2C Name: %s\n", I2C_MAJOR, i2c_name );
/**GVC**/
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
printk( KERN_INFO "I2C: with master/slave delay patch\n" );
#endif
/**END GVC**/
i2c_class = class_create (THIS_MODULE, "i2c_etrax");
device_create (i2c_class, NULL,
MKDEV(i2c_major,0), NULL, i2c_name);
return ( 0 );
} /* i2c_register */
@ -827,6 +814,9 @@ int i2c_start( void )
*/
int i2c_stop( void )
{
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
int n=MAXSCLRETRIES;
#endif
i2c_sda_dir_out();
/* Set SCL=0, SDA=0 */
@ -837,7 +827,21 @@ int i2c_stop( void )
i2c_delay( WAITONEUS );
/* Set SCL=1, SDA=0 */
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
i2c_set_scl( SCL_HIGH );
i2c_scl_dir_in();
for( ; n>0; n-- )
{
if( i2c_scl_is_high() )
break;
i2c_delay( TSUSTO );
}
i2c_scl_dir_out();
#else
i2c_set_scl( SCL_HIGH );
#endif
i2c_delay( TSUSTO );
/* Set SCL=1, SDA=1 */
@ -848,7 +852,6 @@ int i2c_stop( void )
if ( !i2c_sda_is_high() || !i2c_scl_is_high() )
{
printk( KERN_DEBUG "I2C: EI2CSTOPCOND\n" );
return ( EI2CSTOPCOND );
}
@ -907,7 +910,6 @@ int i2c_outbyte( unsigned char x )
if ( !i2c_getack() )
{
printk( KERN_DEBUG "I2C: EI2CNOACKNLD\n" );
return( EI2CNOACKNLD );
}
@ -929,6 +931,9 @@ int i2c_outbyte( unsigned char x )
*/
unsigned char i2c_inbyte( void )
{
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
int n=MAXSCLRETRIES;
#endif
unsigned char aBitByte = 0;
unsigned char Mask = 0x80; /* !!! ATTENTION: do NOT use 'char', otherwise shifting is wrong!!! */
/* Must be UNSIGNED, not SIGNED! */
@ -940,7 +945,20 @@ unsigned char i2c_inbyte( void )
while ( Mask != 0 )
{
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
i2c_scl_dir_in();
for( ; n>0; n-- )
{
if( i2c_scl_is_high() )
break;
i2c_delay( THIGH );
}
i2c_set_scl( SCL_HIGH );
i2c_scl_dir_out();
#else
i2c_set_scl( SCL_HIGH );
#endif
i2c_delay( THIGH );
if ( i2c_sda_is_high() )
@ -978,6 +996,9 @@ unsigned char i2c_inbyte( void )
int i2c_getack( void )
{
int ack = 1;
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
int n=MAXSCLRETRIES;
#endif
/* generate ACK clock pulse */
i2c_set_scl( SCL_HIGH );
@ -985,8 +1006,34 @@ int i2c_getack( void )
/* switch off I2C */
i2c_disable();
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
/* set clock low */
i2c_set_scl( SCL_LOW );
/* now wait for ack */
i2c_delay( THIGH );
/* set clock as input */
i2c_scl_dir_in();
/* wait for clock to rise (n=MAXSCLRETRIES) */
for( ; n>0; n-- )
{
if( i2c_scl_is_high() )
break;
i2c_delay( THIGH );
}
i2c_set_scl( SCL_HIGH );
i2c_scl_dir_out();
i2c_delay( THIGH );
#else
/* now wait for ack */
i2c_delay( THIGH );
#endif
/* check for ack: if SDA is high, then NACK, else ACK */
if ( i2c_sda_is_high() )
{
@ -1024,6 +1071,10 @@ int i2c_getack( void )
*/
void i2c_sendack( void )
{
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
int n=MAXSCLRETRIES;
#endif
/* enable output */
/* Clock has been set to TLOW already at end of i2c_inbyte()
* and i2c_outbyte(), so no need to do it again.
@ -1033,8 +1084,25 @@ void i2c_sendack( void )
i2c_set_sda( SDA_LOW );
/* generate clock pulse */
i2c_delay( TSUDAT );
i2c_set_scl( SCL_HIGH );
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
i2c_scl_dir_in();
/* wait for clock to rise (n=MAXSCLRETRIES) */
for( ; n>0; n-- )
{
if( i2c_scl_is_high() )
break;
i2c_delay( THIGH );
}
i2c_set_scl( SCL_HIGH );
i2c_scl_dir_out();
i2c_delay( THIGH );
#else
i2c_set_scl( SCL_HIGH );
i2c_delay( THIGH );
#endif
i2c_set_scl( SCL_LOW );
i2c_delay( THDDAT );
/* reset data out */
@ -1061,6 +1129,10 @@ void i2c_sendack( void )
*/
void i2c_sendnack( void )
{
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
int n=MAXSCLRETRIES;
#endif
/* make sure the SDA line is set high prior to activation of the output.
* this way, you avoid an unnecessary peak to ground when a NACK has to
* be created.
@ -1072,8 +1144,25 @@ void i2c_sendnack( void )
/* generate clock pulse */
i2c_delay( TSUDAT );
i2c_set_scl( SCL_HIGH );
#ifdef CONFIG_ETRAX_I2C_SLAVE_DELAY
i2c_scl_dir_in();
/* wait for clock to rise (n=MAXSCLRETRIES) */
for( ; n>0; n-- )
{
if( i2c_scl_is_high() )
break;
i2c_delay( THIGH );
}
i2c_set_scl( SCL_HIGH );
i2c_scl_dir_out();
i2c_delay( THIGH );
#else
i2c_set_scl( SCL_HIGH );
i2c_delay( THIGH );
#endif
i2c_set_scl( SCL_LOW );
i2c_delay( TSUDAT );
i2c_set_sda( SDA_LOW );

View File

@ -1,6 +1,8 @@
--- a/arch/cris/arch-v10/drivers/Kconfig
+++ b/arch/cris/arch-v10/drivers/Kconfig
@@ -450,11 +450,18 @@ config ETRAX_I2C
Index: linux-2.6.30.8/arch/cris/arch-v10/drivers/Kconfig
===================================================================
--- linux-2.6.30.8.orig/arch/cris/arch-v10/drivers/Kconfig 2009-10-02 11:31:49.000000000 +0200
+++ linux-2.6.30.8/arch/cris/arch-v10/drivers/Kconfig 2009-10-06 10:36:23.000000000 +0200
@@ -450,11 +450,31 @@
i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
@ -10,6 +12,19 @@
+ select ETRAX_I2C_USES_PB_NOT_PB_I2C
+ help
+ Enables an I2C driver with Geert Vancompernolle improvement.
+
+config ETRAX_I2C_SLAVE_DELAY
+ bool "I2C Slave delay support"
+ depends on ETRAX_I2C_GVC && EXPERIMENTAL
+ help
+ Enable this to enhanced master/slave dialog
+ Improvement by Positive Going (www.positivegoing.it) and BK srl (www.b-k.it)
+
+config ETRAX_I2C_DYN_ALLOC
+ bool "I2C major device dynamic alloc"
+ depends on ETRAX_I2C_GVC && EXPERIMENTAL
+ help
+ Enable this to dynamicaly alloc major i2c device number
+
# this is true for most products since PB-I2C seems to be somewhat
# flawed..
@ -20,7 +35,7 @@
help
Select whether to use the special I2C mode in the PB I/O register or
not. This option needs to be selected in order to use some drivers
@@ -478,7 +485,7 @@ config ETRAX_I2C_CLK_PORT
@@ -478,7 +498,7 @@
config ETRAX_I2C_EEPROM
bool "I2C EEPROM (non-volatile RAM) support"
@ -29,8 +44,10 @@
help
Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C
driver. Select size option: Probed, 2k, 8k, 16k.
--- a/arch/cris/arch-v10/drivers/Makefile
+++ b/arch/cris/arch-v10/drivers/Makefile
Index: linux-2.6.30.8/arch/cris/arch-v10/drivers/Makefile
===================================================================
--- linux-2.6.30.8.orig/arch/cris/arch-v10/drivers/Makefile 2009-10-02 11:31:14.000000000 +0200
+++ linux-2.6.30.8/arch/cris/arch-v10/drivers/Makefile 2009-10-02 11:31:50.000000000 +0200
@@ -4,6 +4,7 @@
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o