--- /dev/null
+/*!***************************************************************************\r
+*!\r
+*! FILE NAME : i2c.c\r
+*!\r
+*!\r
+*! ---------------------------------------------------------------------------\r
+*!\r
+*! ( C ) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN\r
+*!\r
+*!***************************************************************************/\r
+/* $Id: i2c.c,v 1.16 2005/09/29 13:33:35 bjarne Exp $ */\r
+\r
+#define DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+//#undef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+\r
+/******************** INCLUDE FILES SECTION ****************************/\r
+\r
+#include <linux/module.h>\r
+#include <linux/fs.h>\r
+\r
+/**GVC**/\r
+#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+#include <linux/types.h> /* for dev_t */\r
+#include <linux/cdev.h> /* for struct cdev */\r
+#endif\r
+/**END GVC**/\r
+\r
+#include "etraxi2c.h"\r
+\r
+/**GVC**/\r
+#include "i2c_errno.h"\r
+/**END GVC**/\r
+\r
+#include <asm/io.h>\r
+#include <asm/delay.h>\r
+#include <asm/arch/io_interface_mux.h>\r
+#include <asm/uaccess.h>\r
+\r
+#include "i2c_gvc.h"\r
+\r
+MODULE_DESCRIPTION( "I2C Device Driver - 1.1" );\r
+\r
+/*!*********************************************************************\r
+ *!History I2C driver Geert Vancompernolle\r
+ *!---------------------------------------\r
+ *!\r
+ *! - v1.0:\r
+ *! First official version.\r
+ *!\r
+ *! - v1.1:\r
+ *! Changes to remove unwanted spikes at ACK/NACK time.\r
+ *!\r
+ *!*********************************************************************/\r
+ \r
+MODULE_LICENSE( "GPL" );\r
+\r
+/****************** MACRO's **********************/\r
+\r
+#define D( x )\r
+\r
+/**GVC**/\r
+#ifndef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+/**END GVC**/\r
+#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */\r
+/**GVC**/\r
+#endif\r
+/**END GVC**/\r
+\r
+/**GVC**/\r
+#define WAITONEUS 1\r
+/* Following are abbreviations taken from Philips I2C standard */\r
+/* Values are representing time in us and are rounded to next whole number, if relevant */ \r
+#define THDSTA 4 /* Hold delay time for (repeated) START condition */\r
+#define TLOW 5 /* LOW period of the SCL clock */\r
+#define THDDAT 1 /* Hold delay time for DATA: value of 0 is allowed but 1 taken to be sure */\r
+#define TSUDAT 1 /* Set-up time for DATA */\r
+#define THIGH 4 /* HIGH period of the SCL clock */\r
+#define TSUSTA 5 /* Set-up time for a repeated START condition */\r
+#define TSUSTO 4 /* Set-up time for STOP condition */\r
+#define TBUF 5 /* Bus-free time between STOP and START condition */\r
+\r
+#define MAXBUSFREERETRIES 5\r
+#define MAXRETRIES 3\r
+#define WRITEADDRESS_MASK ( 0xFE )\r
+#define READADDRESS_MASK ( 0x01 )\r
+/**END GVC**/\r
+\r
+#define SCL_HIGH 1\r
+#define SCL_LOW 0\r
+#define SDA_HIGH 1\r
+#define SDA_LOW 0\r
+\r
+#ifdef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C\r
+/* Use PB and not PB_I2C */\r
+#ifndef CONFIG_ETRAX_I2C_DATA_PORT\r
+#define CONFIG_ETRAX_I2C_DATA_PORT 0\r
+#endif\r
+#ifndef CONFIG_ETRAX_I2C_CLK_PORT\r
+#define CONFIG_ETRAX_I2C_CLK_PORT 1\r
+#endif\r
+\r
+#define SDABIT CONFIG_ETRAX_I2C_DATA_PORT\r
+#define SCLBIT CONFIG_ETRAX_I2C_CLK_PORT\r
+#define i2c_enable() \r
+#define i2c_disable() \r
+\r
+/* enable or disable output-enable, to select output or input on the i2c bus */\r
+#define i2c_sda_dir_out() \\r
+ REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 1 )\r
+#define i2c_sda_dir_in() \\r
+ REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 0 )\r
+\r
+/* control the i2c clock and data signals */\r
+#define i2c_set_scl( x ) \\r
+ REG_SHADOW_SET( R_PORT_PB_DATA, port_pb_data_shadow, SCLBIT, x )\r
+#define i2c_set_sda( x ) \\r
+ REG_SHADOW_SET( R_PORT_PB_DATA, port_pb_data_shadow, SDABIT, x )\r
+\r
+/* read status of SDA bit from the i2c interface */\r
+#define i2c_sda_is_high() ( ( ( *R_PORT_PB_READ & ( 1 << SDABIT ) ) ) >> SDABIT )\r
+\r
+/**GVC**/\r
+/* read status of SCL bit from the i2c interface */\r
+#define i2c_scl_is_high() ( ( ( *R_PORT_PB_READ & ( 1 << SCLBIT ) ) ) >> SCLBIT )\r
+/**END GVC**/\r
+\r
+#else\r
+/* enable or disable the i2c interface */\r
+#define i2c_enable() *R_PORT_PB_I2C = ( port_pb_i2c_shadow |= IO_MASK( R_PORT_PB_I2C, i2c_en ) )\r
+#define i2c_disable() *R_PORT_PB_I2C = ( port_pb_i2c_shadow &= ~IO_MASK( R_PORT_PB_I2C, i2c_en ) )\r
+\r
+/* enable or disable output-enable, to select output or input on the i2c bus */\r
+#define i2c_sda_dir_out() \\r
+ *R_PORT_PB_I2C = ( port_pb_i2c_shadow &= ~IO_MASK( R_PORT_PB_I2C, i2c_oe_ ) ); \\r
+ REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, 0, 1 ); \r
+#define i2c_sda_dir_in() \\r
+ *R_PORT_PB_I2C = ( port_pb_i2c_shadow |= IO_MASK( R_PORT_PB_I2C, i2c_oe_ ) ); \\r
+ REG_SHADOW_SET( R_PORT_PB_DIR, port_pb_dir_shadow, 0, 0 );\r
+\r
+/* control the i2c clock and data signals */\r
+#define i2c_set_scl( x ) \\r
+ *R_PORT_PB_I2C = ( port_pb_i2c_shadow = ( port_pb_i2c_shadow & \\r
+ ~IO_MASK( R_PORT_PB_I2C, i2c_set_scl ) ) | IO_FIELD( R_PORT_PB_I2C, i2c_set_scl, ( x ) ) ); \\r
+ REG_SHADOW_SET( R_PORT_PB_DATA, port_pb_data_shadow, 1, x );\r
+\r
+#define i2c_set_sda( x ) \\r
+ *R_PORT_PB_I2C = ( port_pb_i2c_shadow = ( port_pb_i2c_shadow & \\r
+ ~IO_MASK( R_PORT_PB_I2C, i2c_d ) ) | IO_FIELD( R_PORT_PB_I2C, i2c_d, ( x ) ) ); \\r
+ REG_SHADOW_SET( R_PORT_PB_DATA, port_pb_data_shadow, 0, x );\r
+\r
+/* read a bit from the i2c interface */\r
+#define i2c_sda_is_high() ( *R_PORT_PB_READ & 0x1 )\r
+#endif\r
+\r
+/* use the kernels delay routine */\r
+#define i2c_delay( usecs ) udelay( usecs )\r
+\r
+\r
+/****************** TYPEDEF's **********************/\r
+\r
+\r
+/****************** STATIC (file scope) VARIABLES **********************/\r
+static DEFINE_SPINLOCK( i2c_lock ); /* Protect directions etc */\r
+/**GVC**/\r
+#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+static const char i2c_name[] = "i2cgvc";\r
+#else\r
+static const char i2c_name[] = "i2c";\r
+#endif\r
+/**END GVC**/\r
+\r
+\r
+/****************** PROTOTYPING SECTION *************************/\r
+static int i2c_open( struct inode *inode, struct file *filp );\r
+static int i2c_release( struct inode *inode, struct file *filp );\r
+/**GVC**/\r
+static int i2c_command( unsigned char slave\r
+ , unsigned char* wbuf\r
+ , unsigned char wlen\r
+ , unsigned char* rbuf\r
+ , unsigned char rlen\r
+ );\r
+static int i2c_bus_free_check( unsigned char maxretries );\r
+static void i2c_finalise( const char* text, unsigned long irqflags );\r
+/**END GVC**/\r
+ \r
+\r
+/************************************************************************/\r
+/****************** AUXILIARIES *************************/\r
+/************************************************************************/\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_open\r
+ *#\r
+ *# DESCRIPTION : opens an I2C device\r
+ *#\r
+ *# PARAMETERS : *inode: reference to inode\r
+ *# *filp : reference to file pointer \r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+static int i2c_open( struct inode *inode, struct file *filp )\r
+{\r
+ return 0;\r
+} /* i2c_open */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_release\r
+ *#\r
+ *# DESCRIPTION : Releases the I2C device\r
+ *#\r
+ *# PARAMETERS : *inode: reference to inode\r
+ *# *filp : reference to file pointer \r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+static int i2c_release( struct inode *inode, struct file *filp )\r
+{\r
+ return 0;\r
+} /* i2c_release */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_ioctl\r
+ *#\r
+ *# DESCRIPTION : Main device API: ioctl's to write/read \r
+ *# to/from i2c registers\r
+ *#\r
+ *# PARAMETERS : *inode: reference to inode\r
+ *# *filp : reference to file pointer \r
+ *# cmd : command to be executed during the ioctl call \r
+ *# arg : pointer to a structure with the data??? \r
+ *#\r
+ *# RETURN : result of the ioctl call\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+static int i2c_ioctl( struct inode *inode\r
+ , struct file *file\r
+ , unsigned int cmd\r
+ , unsigned long arg\r
+ )\r
+{\r
+ /* the acme ioctls */\r
+ I2C_DATA i2cdata;\r
+ int RetVal = EI2CNOERRORS;\r
+ \r
+ if ( _IOC_TYPE( cmd ) != ETRAXI2C_IOCTYPE ) \r
+ {\r
+ return ( -EINVAL );\r
+ }\r
+ \r
+ switch ( _IOC_NR( cmd ) ) \r
+ {\r
+ case I2C_WRITEREG:\r
+ /* write to an i2c slave */\r
+ RetVal = i2c_writereg( I2C_ARGSLAVE( arg )\r
+ , I2C_ARGREG( arg )\r
+ , I2C_ARGVALUE( arg )\r
+ );\r
+ break; \r
+\r
+ case I2C_READREG:\r
+ RetVal = i2c_readreg( I2C_ARGSLAVE( arg ), I2C_ARGREG( arg ) );\r
+ break;\r
+ \r
+/**GVC**/\r
+ /* New functions added by GVC */ \r
+ case I2C_READ:\r
+ copy_from_user( (char*)&i2cdata, (char*)arg, sizeof( I2C_DATA ) );\r
+ {\r
+ int RetryCntr = MAXRETRIES;\r
+ \r
+ do\r
+ {\r
+ RetVal = i2c_command( i2cdata.slave\r
+ , NULL\r
+ , 0 \r
+ , i2cdata.rbuf\r
+ , i2cdata.rlen\r
+ );\r
+ } while ( ( EI2CNOERRORS != RetVal )\r
+ &&( --RetryCntr )\r
+ );\r
+ }\r
+ copy_to_user( (char*)arg, (char*)&i2cdata, sizeof( I2C_DATA ) );\r
+ break;\r
+\r
+ case I2C_WRITE:\r
+ copy_from_user( (char*)&i2cdata, (char*)arg, sizeof( I2C_DATA ) );\r
+ {\r
+ int RetryCntr = MAXRETRIES;\r
+ \r
+ do\r
+ {\r
+ RetVal = i2c_command( i2cdata.slave\r
+ , i2cdata.wbuf\r
+ , i2cdata.wlen\r
+ , NULL\r
+ , 0 \r
+ );\r
+ } while ( ( EI2CNOERRORS != RetVal )\r
+ &&( --RetryCntr )\r
+ );\r
+ }\r
+ break;\r
+ \r
+ case I2C_WRITEREAD:\r
+ copy_from_user( (char*)&i2cdata, (char*)arg, sizeof( I2C_DATA ) );\r
+ {\r
+ int RetryCntr = MAXRETRIES;\r
+ \r
+ do\r
+ {\r
+ RetVal = i2c_command( i2cdata.slave\r
+ , i2cdata.wbuf\r
+ , i2cdata.wlen\r
+ , i2cdata.rbuf\r
+ , i2cdata.rlen \r
+ );\r
+ } while ( ( EI2CNOERRORS != RetVal )\r
+ &&( --RetryCntr )\r
+ );\r
+ }\r
+ copy_to_user( (char*)arg, (char*)&i2cdata, sizeof( I2C_DATA ) );\r
+ break;\r
+/**END GVC**/ \r
+ \r
+ default:\r
+ RetVal = -EINVAL;\r
+ }\r
+ \r
+ return ( -RetVal );\r
+} /* i2c_ioctl */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_command\r
+ *#\r
+ *# DESCRIPTION : general routine to read/write bytes from an I2C device\r
+ *# \r
+ *# 'i2c_command()' sends wlen bytes to the I2c bus and receives\r
+ *# rlen bytes from the I2c bus. \r
+ *# The data to be send must be placed in wbuf[ 0 ] upto wbuf[ wlen - 1 ).\r
+ *# The data to be received is assembled in rbuf[ 0 ] upto rbuf[ rlen - 1 ].\r
+ *# \r
+ *# If no data is to be sent or received, put appropriate buffer parameter\r
+ *# to "NULL" and appropriate length parameter to "0".\r
+ *# \r
+ *# PARAMETERS : slave = slave address of the I2C device\r
+ *# wbuf = address of first element of write buffer (wbuf)\r
+ *# wlen = number of bytes to be written to slave\r
+ *# rbuf = address of first element of read buffer (rbuf)\r
+ *# rlen = number of bytes to be read from slave\r
+ *#\r
+ *# RETURN : \r
+ *# EI2CNOERRORS: I2C communication went fine\r
+ *# EI2CBUSNFREE: I2C bus is not free\r
+ *# EI2CWADDRESS: I2C write address failed\r
+ *# EI2CRADDRESS: I2C read address failed\r
+ *# EI2CSENDDATA: I2C send data failed\r
+ *# EI2CRECVDATA: I2C receive data failed\r
+ *# EI2CSTRTCOND: I2C start condition failed\r
+ *# EI2CRSTACOND: I2C repeated start condition failed\r
+ *# EI2CSTOPCOND: I2C stop condition failed\r
+ *# EI2CNOSNDBYT: I2C no bytes to be sent\r
+ *# EI2CNOSNDBUF: I2C no send buffer defined\r
+ *# EI2CNORCVBYT: I2C no bytes to be received\r
+ *# EI2CNORCVBUF: I2C no receive buffer defined\r
+ *# EI2CNOACKNLD: I2C no acknowledge received\r
+ *#\r
+ *# REMARK :\r
+ *# First, the send part is completed. \r
+ *# In the send routine, there is no stop generated. This is because maybe\r
+ *# a repeated start condition must be generated.\r
+ *# This happens when we want to receive some data from the I2c bus. If not,\r
+ *# at the end of the general I2c loop the stopcondition is generated.\r
+ *# If, on the contrary, there are a number of bytes to be received, a new\r
+ *# startcondition is generated in the 'if' part of the main I2c routine, \r
+ *# which controls the receiving part. \r
+ *# Only when the receiving of data is finished, a final stopcondition is \r
+ *# generated.\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+static int i2c_command( unsigned char slave\r
+ , unsigned char* wbuf\r
+ , unsigned char wlen\r
+ , unsigned char* rbuf\r
+ , unsigned char rlen\r
+ )\r
+{\r
+ /* Check arguments and report error if relevant... */\r
+ if ( ( wlen > 0 ) && ( wbuf == NULL ) )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CNOSNDBUF\n" );\r
+ return ( EI2CNOSNDBUF );\r
+ }\r
+ else if ( ( wlen == 0 ) && ( wbuf != NULL ) )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CNOSNDBYT\n" );\r
+ return ( EI2CNOSNDBYT );\r
+ }\r
+ else if ( ( rlen > 0 ) && ( rbuf == NULL ) )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CNORCVBUF\n" );\r
+ return ( EI2CNORCVBUF );\r
+ }\r
+ else if ( ( rlen == 0 ) && ( rbuf != NULL ) )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CNORCVBYT\n" );\r
+ return ( EI2CNORCVBYT );\r
+ }\r
+ else if ( EI2CBUSNFREE == i2c_bus_free_check( MAXBUSFREERETRIES ) )\r
+ {\r
+ /* There's no need to try more, since we weren't even\r
+ * able to start the I2C communication.\r
+ * So, no IRQ flags are stored yet, no changes to any other\r
+ * stuff like START, STOP, SENDBYTES...\r
+ * Result, simply write down the error and return the correct error code.\r
+ */\r
+ printk( KERN_DEBUG "I2C: EI2CBUSNFREE\n" );\r
+ return ( EI2CBUSNFREE );\r
+ }\r
+ else\r
+ {\r
+ /* Finally... We made it... */\r
+ unsigned long irqflags = 0;\r
+\r
+ /* we don't like to be interrupted */\r
+ local_irq_save( irqflags );\r
+\r
+ /* Check if there are bytes to be send, \r
+ * or if you immediately want to receive data.\r
+ */\r
+ if ( 0 < wlen )\r
+ {\r
+ /* start I2C communication */ \r
+ if ( EI2CNOERRORS != i2c_start() )\r
+ {\r
+ return ( i2c_finalise( "I2C: EI2CSTRTCOND\n", irqflags )\r
+ , EI2CSTRTCOND \r
+ );\r
+ }\r
+\r
+ /* send slave address: xxxxxxx0B (last bit must be zero) */\r
+ if ( EI2CNOERRORS != i2c_outbyte( slave & WRITEADDRESS_MASK ) )\r
+ {\r
+ return ( i2c_finalise( "I2C: EI2CWADDRESS\n", irqflags )\r
+ , EI2CWADDRESS \r
+ );\r
+ }\r
+\r
+ while ( wlen-- )\r
+ { \r
+ /* send register data */\r
+ if ( EI2CNOERRORS != i2c_outbyte( *wbuf ) )\r
+ {\r
+ return ( i2c_finalise( "I2C: EI2CSENDDATA\n", irqflags )\r
+ , EI2CSENDDATA \r
+ );\r
+ }\r
+ \r
+ wbuf++;\r
+ };\r
+ \r
+ i2c_delay( TLOW );\r
+ }\r
+\r
+ /*\r
+ * Receiving data from I2c_bus\r
+ * If there are bytes to be received, a new start condition is\r
+ * generated => Repeated Startcondition.\r
+ * A final stopcondition is generated at the end of the main I2c\r
+ * routine.\r
+ */\r
+ if ( 0 < rlen )\r
+ {\r
+ /*\r
+ * Generate start condition if wlen == 0 \r
+ * or repeated start condition if wlen != 0...\r
+ */\r
+ if ( EI2CNOERRORS != i2c_start() )\r
+ {\r
+ return ( i2c_finalise( ( ( 0 < wlen ) \r
+ ? "I2C: EI2CRSTACOND\n"\r
+ : "I2C: EI2CSTRTCOND\n"\r
+ )\r
+ , irqflags\r
+ )\r
+ , ( ( 0 < wlen ) ? EI2CRSTACOND : EI2CSTRTCOND )\r
+ );\r
+ }\r
+\r
+ /* Send ReadAddress: xxxxxxx1B (last bit must be one) */\r
+ if ( EI2CNOERRORS != i2c_outbyte( slave | READADDRESS_MASK ) )\r
+ {\r
+ return ( i2c_finalise( "I2C: EI2CRADDRESS\n", irqflags )\r
+ , EI2CRADDRESS\r
+ );\r
+ }\r
+ \r
+ while ( rlen-- )\r
+ {\r
+ /* fetch register */\r
+ *rbuf = i2c_inbyte();\r
+ rbuf++;\r
+ \r
+ /* last received byte needs to be NACK-ed instead of ACK-ed */\r
+ if ( rlen )\r
+ {\r
+ i2c_sendack();\r
+ }\r
+ else \r
+ {\r
+ i2c_sendnack(); \r
+ }\r
+ };\r
+ }\r
+\r
+ /* Generate final stop condition */\r
+ if ( EI2CNOERRORS != i2c_stop() )\r
+ {\r
+ return ( i2c_finalise( "I2C: EI2CSTOPCOND\n", irqflags )\r
+ , EI2CSTOPCOND\r
+ );\r
+ } \r
+ \r
+ /* enable interrupt again */\r
+ local_irq_restore( irqflags );\r
+ }\r
+ \r
+ return ( EI2CNOERRORS );\r
+} /* i2c_command */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_bus_free_check\r
+ *#\r
+ *# DESCRIPTION : checks if the I2C bus is free before starting\r
+ *# an I2C communication\r
+ *# \r
+ *# PARAMETERS : maxretries, the number of times we will try to release\r
+ *# the I2C bus \r
+ *#\r
+ *# RETURN : I2cStatus_I2cBusNotFreeError in case the bus is not free,\r
+ *# I2cStatus_I2cNoError otherwise \r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+static int i2c_bus_free_check( unsigned char maxretries )\r
+{\r
+ i2c_sda_dir_in(); /* Release SDA line */\r
+ i2c_set_scl( SCL_HIGH ); /* put SCL line high */\r
+ \r
+ i2c_delay( WAITONEUS );\r
+ \r
+ while ( ( !i2c_sda_is_high() || !i2c_scl_is_high() )\r
+ &&( maxretries-- )\r
+ )\r
+ {\r
+ /* Try to release I2C bus by generating STOP conditions */\r
+ i2c_stop();\r
+ }\r
+ \r
+ if ( 0 == maxretries )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CBUSNFREE\n" );\r
+ return ( EI2CBUSNFREE );\r
+ }\r
+ else \r
+ {\r
+ return ( EI2CNOERRORS );\r
+ }\r
+} /* i2c_bus_free_check */\r
+\r
+\r
+static void i2c_finalise( const char* errortxt\r
+ , unsigned long irqflags\r
+ )\r
+{\r
+ printk( KERN_DEBUG "%s", errortxt );\r
+ local_irq_restore( irqflags );\r
+ /* The least we can do when things go terribly wrong,\r
+ * is to try to release the bus.\r
+ * If this fails, well, then I don't know\r
+ * what I can do more for the moment...\r
+ */\r
+ (void)i2c_bus_free_check( MAXBUSFREERETRIES );\r
+} /* i2c_finalise */ \r
+\r
+\r
+static struct file_operations i2c_fops = \r
+{\r
+ .owner = THIS_MODULE\r
+, .ioctl = i2c_ioctl\r
+, .open = i2c_open\r
+, .release = i2c_release\r
+};\r
+\r
+\r
+/***********************************************************************/\r
+/************* EXTERNAL FUNCTION DEFINITION SECTION ********************/\r
+/***********************************************************************/\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_init\r
+ *#\r
+ *# DESCRIPTION : initialises the I2C device driver\r
+ *#\r
+ *# PARAMETERS :\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int __init i2c_init( void )\r
+{\r
+ static int res = 0;\r
+ static int first = 1;\r
+\r
+ if ( !first ) \r
+ {\r
+ return res;\r
+ }\r
+ \r
+ first = 0;\r
+\r
+ /* Setup and enable the Port B I2C interface */\r
+\r
+#ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C\r
+ /* here, we're using the dedicated I2C pins of FoxBoard */\r
+ if ( ( res = cris_request_io_interface( if_i2c, "I2C" ) ) ) \r
+ {\r
+ printk( KERN_CRIT "i2c_init: Failed to get IO interface\n" );\r
+ return res;\r
+ }\r
+\r
+ *R_PORT_PB_I2C = port_pb_i2c_shadow |= \r
+ IO_STATE( R_PORT_PB_I2C, i2c_en, on ) |\r
+ IO_FIELD( R_PORT_PB_I2C, i2c_d, 1 ) |\r
+ IO_FIELD( R_PORT_PB_I2C, i2c_set_scl, 1 ) |\r
+ IO_STATE( R_PORT_PB_I2C, i2c_oe_, enable );\r
+\r
+ port_pb_dir_shadow &= ~IO_MASK( R_PORT_PB_DIR, dir0 );\r
+ port_pb_dir_shadow &= ~IO_MASK( R_PORT_PB_DIR, dir1 );\r
+\r
+ *R_PORT_PB_DIR = ( port_pb_dir_shadow |=\r
+ IO_STATE( R_PORT_PB_DIR, dir0, input ) |\r
+ IO_STATE( R_PORT_PB_DIR, dir1, output ) );\r
+#else\r
+ /* If everything goes fine, res = 0, meaning "if" fails => \r
+ * will do the "else" too and as such initialise the clock port...\r
+ * Clever trick! \r
+ */\r
+ if ( ( res = cris_io_interface_allocate_pins( if_i2c\r
+ , 'b'\r
+ , CONFIG_ETRAX_I2C_DATA_PORT\r
+ , CONFIG_ETRAX_I2C_DATA_PORT \r
+ ) \r
+ ) \r
+ ) \r
+ {\r
+ printk( KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n" );\r
+ return ( res );\r
+ }\r
+ /* Same here...*/ \r
+ else if ( ( res = cris_io_interface_allocate_pins( if_i2c\r
+ , 'b'\r
+ , CONFIG_ETRAX_I2C_CLK_PORT\r
+ , CONFIG_ETRAX_I2C_CLK_PORT \r
+ ) \r
+ ) \r
+ ) \r
+ {\r
+ cris_io_interface_free_pins( if_i2c\r
+ , 'b'\r
+ , CONFIG_ETRAX_I2C_DATA_PORT\r
+ , CONFIG_ETRAX_I2C_DATA_PORT \r
+ );\r
+ printk( KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n" );\r
+ }\r
+#endif\r
+\r
+ return ( res );\r
+} /* i2c_init */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_register\r
+ *#\r
+ *# DESCRIPTION : this registers the i2c driver as a character device\r
+ *#\r
+ *# PARAMETERS :\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+static int __init i2c_register( void )\r
+{\r
+ int res;\r
+/**GVC**/\r
+#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+ dev_t devt;\r
+ struct cdev *my_i2cdev = NULL;\r
+#endif\r
+/**END GVC**/\r
+\r
+ res = i2c_init();\r
+ \r
+ if ( res < 0 )\r
+ {\r
+ return res;\r
+ }\r
+ \r
+/**GVC**/\r
+#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+ res = alloc_chrdev_region( &devt, 0, 1, i2c_name );\r
+ \r
+ if ( res < 0 )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CNOMNUMBR\n" );\r
+ return ( res );\r
+ }\r
+ \r
+ my_i2cdev = cdev_alloc();\r
+ my_i2cdev->ops = &i2c_fops;\r
+ my_i2cdev->owner = THIS_MODULE;\r
+ \r
+ /* make device "alive" */ \r
+ res = cdev_add( my_i2cdev, devt, 1 );\r
+ \r
+ if ( res < 0 )\r
+ { \r
+ printk( KERN_DEBUG "I2C: EI2CDADDFAIL\n" );\r
+ return ( res );\r
+ }\r
+#else\r
+/**END GVC**/\r
+ res = register_chrdev( I2C_MAJOR, i2c_name, &i2c_fops );\r
+ \r
+ if ( res < 0 ) \r
+ {\r
+ printk( KERN_ERR "i2c: couldn't get a major number.\n" );\r
+ return res;\r
+ }\r
+/**GVC**/\r
+#endif\r
+/**END GVC**/\r
+\r
+ printk( KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n" );\r
+\r
+/**GVC**/\r
+ printk( KERN_INFO " ==> Improvements done by Geert Vancompernolle - December 2006\n" );\r
+\r
+#ifdef DYNAMIC_MAJOR_I2CDEV_NUMBER_ALLOC\r
+ printk( KERN_INFO "I2C Major: %d / I2C Name: %s\n", MAJOR( devt ), i2c_name );\r
+#else\r
+/**END GVC**/\r
+ printk( KERN_INFO "I2C Major: %d / I2C Name: %s\n", I2C_MAJOR, i2c_name );\r
+/**GVC**/\r
+#endif \r
+/**END GVC**/\r
+ \r
+ return ( 0 );\r
+} /* i2c_register */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_start\r
+ *#\r
+ *# DESCRIPTION : generate i2c start condition\r
+ *#\r
+ *# PARAMETERS : none\r
+ *#\r
+ *# RETURN : EI2CNOERRORS if OK, EI2CSTRTCOND otherwise\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_start( void )\r
+{\r
+ /* Set SCL=1, SDA=1 */\r
+ i2c_sda_dir_out();\r
+ i2c_set_sda( SDA_HIGH );\r
+ i2c_delay( WAITONEUS );\r
+ i2c_set_scl( SCL_HIGH );\r
+ i2c_delay( WAITONEUS );\r
+ \r
+ /* Set SCL=1, SDA=0 */\r
+ i2c_set_sda( SDA_LOW );\r
+ i2c_delay( THDSTA );\r
+ \r
+ /* Set SCL=0, SDA=0 */\r
+ i2c_set_scl( SCL_LOW );\r
+ /* We can take 1 us less than defined in spec (5 us), since the next action\r
+ * will be to set the dataline high or low and this action is 1 us\r
+ * before the clock is put high, so that makes our 5 us.\r
+ */\r
+ i2c_delay( TLOW - WAITONEUS );\r
+ \r
+ if ( i2c_sda_is_high() || i2c_scl_is_high() )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CSTRTCOND\n" );\r
+ return ( EI2CSTRTCOND );\r
+ }\r
+ \r
+ return ( EI2CNOERRORS );\r
+} /* i2c_start */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_stop\r
+ *#\r
+ *# DESCRIPTION : generate i2c stop condition\r
+ *#\r
+ *# PARAMETERS : none\r
+ *#\r
+ *# RETURN : none\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_stop( void )\r
+{\r
+ i2c_sda_dir_out();\r
+\r
+ /* Set SCL=0, SDA=0 */\r
+ /* Don't change order, otherwise you might generate a start condition! */\r
+ i2c_set_scl( SCL_LOW );\r
+ i2c_delay( WAITONEUS );\r
+ i2c_set_sda( SDA_LOW );\r
+ i2c_delay( WAITONEUS );\r
+ \r
+ /* Set SCL=1, SDA=0 */\r
+ i2c_set_scl( SCL_HIGH );\r
+ i2c_delay( TSUSTO );\r
+ \r
+ /* Set SCL=1, SDA=1 */\r
+ i2c_set_sda( SDA_HIGH );\r
+ i2c_delay( TBUF );\r
+\r
+ i2c_sda_dir_in();\r
+ \r
+ if ( !i2c_sda_is_high() || !i2c_scl_is_high() )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CSTOPCOND\n" );\r
+ return ( EI2CSTOPCOND );\r
+ }\r
+ \r
+ return ( EI2CNOERRORS );\r
+} /* i2c_stop */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_outbyte\r
+ *#\r
+ *# DESCRIPTION : write a byte to the i2c interface\r
+ *#\r
+ *# PARAMETERS : x: byte to be sent on the I2C bus\r
+ *#\r
+ *# RETURN : none\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_outbyte( unsigned char x )\r
+{\r
+ int i;\r
+\r
+ i2c_sda_dir_out();\r
+\r
+ for ( i = 0; i < 8; i++ ) \r
+ {\r
+ if ( x & 0x80 ) \r
+ {\r
+ i2c_set_sda( SDA_HIGH );\r
+ } \r
+ else \r
+ {\r
+ i2c_set_sda( SDA_LOW );\r
+ }\r
+ \r
+ i2c_delay( TSUDAT );\r
+ i2c_set_scl( SCL_HIGH );\r
+ i2c_delay( THIGH );\r
+ i2c_set_scl( SCL_LOW );\r
+ i2c_delay( TSUDAT );\r
+ i2c_set_sda( SDA_LOW );\r
+ /* There should be only 5 us between falling edge and new rising\r
+ * edge of clock pulse.\r
+ * Since we spend already 1 us since clock edge was low, there are\r
+ * only ( TLOW - TSUDAT ) us left.\r
+ * Next to this, since the data line will be set up 1 us before the\r
+ * clock line is set up, we can reduce the delay with another us.\r
+ */\r
+ i2c_delay( TLOW - TSUDAT - WAITONEUS );\r
+ x <<= 1;\r
+ }\r
+ \r
+ /* enable input */\r
+ i2c_sda_dir_in();\r
+ \r
+ if ( !i2c_getack() )\r
+ {\r
+ printk( KERN_DEBUG "I2C: EI2CNOACKNLD\n" );\r
+ return( EI2CNOACKNLD );\r
+ }\r
+ \r
+ return ( EI2CNOERRORS );\r
+} /* i2c_outbyte */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_inbyte\r
+ *#\r
+ *# DESCRIPTION : read a byte from the i2c interface\r
+ *#\r
+ *# PARAMETERS : none\r
+ *#\r
+ *# RETURN : returns the byte read from the I2C device\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+unsigned char i2c_inbyte( void )\r
+{\r
+ unsigned char aBitByte = 0;\r
+ unsigned char Mask = 0x80; /* !!! ATTENTION: do NOT use 'char', otherwise shifting is wrong!!! */\r
+ /* Must be UNSIGNED, not SIGNED! */\r
+\r
+\r
+ /* Switch off I2C to get bit */\r
+ i2c_disable();\r
+ i2c_sda_dir_in();\r
+\r
+ while ( Mask != 0 )\r
+ {\r
+ i2c_set_scl( SCL_HIGH );\r
+ i2c_delay( THIGH );\r
+\r
+ if ( i2c_sda_is_high() )\r
+ {\r
+ aBitByte |= Mask;\r
+ }\r
+\r
+ i2c_set_scl( SCL_LOW );\r
+\r
+ Mask >>= 1;\r
+\r
+ i2c_delay( TLOW );\r
+ }\r
+\r
+ /*\r
+ * we leave the clock low, getbyte is usually followed\r
+ * by sendack/nack, they assume the clock to be low\r
+ */\r
+ return ( aBitByte );\r
+} /* i2c_inbyte */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_getack\r
+ *#\r
+ *# DESCRIPTION : checks if ack was received from ic2\r
+ *#\r
+ *# PARAMETERS : none\r
+ *#\r
+ *# RETURN : returns the ack state of the I2C device\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_getack( void )\r
+{\r
+ int ack = 1;\r
+\r
+ /* generate ACK clock pulse */\r
+ i2c_set_scl( SCL_HIGH );\r
+ \r
+ /* switch off I2C */\r
+ i2c_disable();\r
+\r
+ /* now wait for ack */\r
+ i2c_delay( THIGH );\r
+ /* check for ack: if SDA is high, then NACK, else ACK */\r
+ if ( i2c_sda_is_high() )\r
+ {\r
+ ack = 0;\r
+ }\r
+ else\r
+ {\r
+ ack = 1;\r
+ }\r
+ \r
+ /* end clock pulse */\r
+ i2c_enable();\r
+ i2c_set_scl( SCL_LOW );\r
+ i2c_sda_dir_out();\r
+ i2c_set_sda( SDA_LOW );\r
+\r
+ /* Since we "lost" already THDDAT time, we can subtract it here... */\r
+ i2c_delay( TLOW - THDDAT );\r
+ \r
+ return ( ack );\r
+} /* i2c_getack */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_sendack\r
+ *#\r
+ *# DESCRIPTION : sends ACK on received data\r
+ *#\r
+ *# PARAMETERS : none\r
+ *#\r
+ *# RETURN : none\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+void i2c_sendack( void )\r
+{\r
+ /* enable output */\r
+ /* Clock has been set to TLOW already at end of i2c_inbyte()\r
+ * and i2c_outbyte(), so no need to do it again.\r
+ */\r
+ i2c_sda_dir_out();\r
+ /* set ack pulse low */\r
+ i2c_set_sda( SDA_LOW );\r
+ /* generate clock pulse */\r
+ i2c_delay( TSUDAT );\r
+ i2c_set_scl( SCL_HIGH );\r
+ i2c_delay( THIGH );\r
+ i2c_set_scl( SCL_LOW );\r
+ i2c_delay( THDDAT );\r
+ /* reset data out */\r
+ i2c_set_sda( SDA_HIGH );\r
+ /* Subtract time spend already when waited to put SDA high */\r
+ i2c_delay( TLOW - THDDAT );\r
+\r
+ /* release the SDA line */\r
+ i2c_sda_dir_in();\r
+} /* i2c_sendack */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_sendnack\r
+ *#\r
+ *# DESCRIPTION : sends NACK on received data\r
+ *#\r
+ *# PARAMETERS : none\r
+ *#\r
+ *# RETURN : none\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+void i2c_sendnack( void )\r
+{\r
+ /* make sure the SDA line is set high prior to activation of the output.\r
+ * this way, you avoid an unnecessary peak to ground when a NACK has to\r
+ * be created.\r
+ */\r
+ /* set data high */\r
+ i2c_set_sda( SDA_HIGH );\r
+ /* enable output */\r
+ i2c_sda_dir_out();\r
+\r
+ /* generate clock pulse */\r
+ i2c_delay( TSUDAT );\r
+ i2c_set_scl( SCL_HIGH );\r
+ i2c_delay( THIGH );\r
+ i2c_set_scl( SCL_LOW );\r
+ i2c_delay( TSUDAT );\r
+ i2c_set_sda( SDA_LOW );\r
+ i2c_delay( TLOW - TSUDAT );\r
+ \r
+ /* There's no need to change the direction of SDA to "in" again,\r
+ * since a NACK is always followed by a stop condition.\r
+ * A STOP condition will put the direction of SDA back to "out"\r
+ * resulting in a useless SDA "dip" on the line...\r
+ */\r
+ /* i2c_sda_dir_in(); */\r
+} /* i2c_sendnack */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_writereg\r
+ *#\r
+ *# DESCRIPTION : writes a value to a register of an I2C device\r
+ *#\r
+ *# PARAMETERS : theSlave = slave address of the I2C device\r
+ *# theReg = register of the I2C device that needs to be written\r
+ *# theValue = value to be written to the register\r
+ *#\r
+ *# RETURN : returns OR-ed result of the write action:\r
+ *# 0 = Ok\r
+ *# 1 = Slave_NoAck\r
+ *# 2 = Reg_NoAck\r
+ *# 4 = Val_NoAck\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_writereg( unsigned char theSlave\r
+ , unsigned char theReg\r
+ , unsigned char theValue \r
+ )\r
+{\r
+ int error, cntr = 3;\r
+ unsigned long flags;\r
+\r
+ spin_lock( &i2c_lock );\r
+\r
+ do \r
+ {\r
+ error = 0;\r
+ /* we don't like to be interrupted */\r
+ local_irq_save( flags );\r
+\r
+ i2c_start();\r
+ /* send slave address */\r
+ if ( EI2CNOACKNLD == i2c_outbyte( theSlave & 0xfe ) )\r
+ {\r
+ error = 1;\r
+ }\r
+ \r
+ /* now select register */\r
+ if ( EI2CNOACKNLD == i2c_outbyte( theReg ) )\r
+ {\r
+ error |= 2;\r
+ }\r
+ \r
+ /* send register register data */\r
+ if ( EI2CNOACKNLD == i2c_outbyte( theValue ) )\r
+ {\r
+ error |= 4;\r
+ }\r
+ \r
+ /* end byte stream */\r
+ i2c_stop();\r
+ /* enable interrupt again */\r
+ local_irq_restore( flags );\r
+ \r
+ } while ( error && cntr-- );\r
+\r
+ i2c_delay( TLOW );\r
+\r
+ spin_unlock( &i2c_lock );\r
+\r
+ return ( -error );\r
+} /* i2c_writereg */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_readreg\r
+ *#\r
+ *# DESCRIPTION : reads the value from a certain register of an I2C device.\r
+ *# Function first writes the register that it wants to read\r
+ *# later on.\r
+ *# \r
+ *# PARAMETERS : theSlave = slave address of the I2C device\r
+ *# theReg = register of the I2C device that needs to be written\r
+ *#\r
+ *# RETURN : returns OR-ed result of the write action:\r
+ *# 0 = Ok\r
+ *# 1 = Slave_NoAck\r
+ *# 2 = Reg_NoAck\r
+ *# 4 = Val_NoAck\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+unsigned char i2c_readreg( unsigned char theSlave\r
+ , unsigned char theReg \r
+ )\r
+{\r
+ unsigned char b = 0;\r
+ int error, cntr = 3;\r
+ unsigned long flags;\r
+\r
+ spin_lock( &i2c_lock );\r
+\r
+ do \r
+ {\r
+ error = 0;\r
+ \r
+ /* we don't like to be interrupted */\r
+ local_irq_save( flags );\r
+ \r
+ /* generate start condition */\r
+ i2c_start();\r
+ \r
+ /* send slave address */\r
+ if ( EI2CNOACKNLD == i2c_outbyte( theSlave & 0xfe ) )\r
+ {\r
+ error = 1;\r
+ }\r
+\r
+ /* now select register */\r
+ i2c_sda_dir_out();\r
+\r
+ if ( EI2CNOACKNLD == i2c_outbyte( theReg ) )\r
+ {\r
+ error |= 2;\r
+ } \r
+\r
+ /* repeat start condition */\r
+ i2c_delay( TLOW );\r
+ i2c_start();\r
+ \r
+ /* send slave address */\r
+ if ( EI2CNOACKNLD == i2c_outbyte( theSlave | 0x01 ) )\r
+ {\r
+ error |= 1;\r
+ }\r
+ \r
+ /* fetch register */\r
+ b = i2c_inbyte();\r
+ /*\r
+ * last received byte needs to be nacked\r
+ * instead of acked\r
+ */\r
+ i2c_sendnack();\r
+ \r
+ /* end sequence */\r
+ i2c_stop();\r
+ \r
+ /* enable interrupt again */\r
+ local_irq_restore( flags );\r
+ \r
+ } while ( error && cntr-- );\r
+\r
+ spin_unlock( &i2c_lock );\r
+\r
+ return ( b );\r
+} /* i2c_readreg */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_read\r
+ *#\r
+ *# DESCRIPTION :\r
+ *# \r
+ *# PARAMETERS :\r
+ *#\r
+ *# RETURN :\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_read( unsigned char slave, unsigned char* rbuf, unsigned char rlen )\r
+{\r
+ return ( i2c_command( slave, NULL, 0, rbuf, rlen ) );\r
+} /* i2c_read */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_write\r
+ *#\r
+ *# DESCRIPTION :\r
+ *# \r
+ *# PARAMETERS :\r
+ *#\r
+ *# RETURN :\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_write( unsigned char slave, unsigned char* wbuf, unsigned char wlen )\r
+{\r
+ return ( i2c_command( slave, wbuf, wlen, NULL, 0 ) );\r
+} /* i2c_write */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: i2c_writeread\r
+ *#\r
+ *# DESCRIPTION :\r
+ *# \r
+ *# PARAMETERS :\r
+ *#\r
+ *# RETURN :\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+int i2c_writeread( unsigned char slave\r
+ , unsigned char* wbuf\r
+ , unsigned char wlen\r
+ , unsigned char* rbuf\r
+ , unsigned char rlen\r
+ )\r
+{\r
+ return ( i2c_command( slave, wbuf, wlen, rbuf, rlen ) );\r
+} /* i2c_writeread */\r
+\r
+\r
+/*#---------------------------------------------------------------------------\r
+ *#\r
+ *# FUNCTION NAME: module_init\r
+ *#\r
+ *# DESCRIPTION : this makes sure that i2c_register is called during boot\r
+ *#\r
+ *# PARAMETERS :\r
+ *#\r
+ *#---------------------------------------------------------------------------\r
+ */\r
+module_init( i2c_register );\r
+\r
+/****************** END OF FILE i2c.c ********************************/\r