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

View file

@ -1,6 +1,8 @@
--- a/arch/cris/arch-v10/drivers/Kconfig Index: linux-2.6.30.8/arch/cris/arch-v10/drivers/Kconfig
+++ b/arch/cris/arch-v10/drivers/Kconfig ===================================================================
@@ -450,11 +450,18 @@ config ETRAX_I2C --- 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); i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg); val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
@ -10,6 +12,19 @@
+ select ETRAX_I2C_USES_PB_NOT_PB_I2C + select ETRAX_I2C_USES_PB_NOT_PB_I2C
+ help + help
+ Enables an I2C driver with Geert Vancompernolle improvement. + 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 # this is true for most products since PB-I2C seems to be somewhat
# flawed.. # flawed..
@ -20,7 +35,7 @@
help help
Select whether to use the special I2C mode in the PB I/O register or 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 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 config ETRAX_I2C_EEPROM
bool "I2C EEPROM (non-volatile RAM) support" bool "I2C EEPROM (non-volatile RAM) support"
@ -29,8 +44,10 @@
help help
Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C
driver. Select size option: Probed, 2k, 8k, 16k. driver. Select size option: Probed, 2k, 8k, 16k.
--- a/arch/cris/arch-v10/drivers/Makefile Index: linux-2.6.30.8/arch/cris/arch-v10/drivers/Makefile
+++ b/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 @@ @@ -4,6 +4,7 @@
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o