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