2005-04-16 15:20:36 -07:00
/*!***************************************************************************
* !
* ! FILE NAME : i2c . c
* !
* ! DESCRIPTION : implements an interface for IIC / I2C , both directly from other
* ! kernel modules ( i2c_writereg / readreg ) and from userspace using
* ! ioctl ( ) ' s
* !
2008-01-17 14:50:47 +01:00
* ! ( C ) Copyright 1999 - 2007 Axis Communications AB , LUND , SWEDEN
2005-04-16 15:20:36 -07:00
* !
* ! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/****************** INCLUDE FILES SECTION ***********************************/
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/slab.h>
2008-05-18 15:32:43 -06:00
# include <linux/smp_lock.h>
2005-04-16 15:20:36 -07:00
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/string.h>
# include <linux/init.h>
# include <asm/etraxi2c.h>
# include <asm/system.h>
2008-10-21 17:45:58 +02:00
# include <arch/svinto.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/delay.h>
2008-10-21 17:45:58 +02:00
# include <arch/io_interface_mux.h>
2005-04-16 15:20:36 -07:00
# include "i2c.h"
/****************** I2C DEFINITION SECTION *************************/
# define D(x)
# define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */
static const char i2c_name [ ] = " i2c " ;
# define CLOCK_LOW_TIME 8
# define CLOCK_HIGH_TIME 8
# define START_CONDITION_HOLD_TIME 8
# define STOP_CONDITION_HOLD_TIME 8
# define ENABLE_OUTPUT 0x01
# define ENABLE_INPUT 0x00
# define I2C_CLOCK_HIGH 1
# define I2C_CLOCK_LOW 0
# define I2C_DATA_HIGH 1
# define I2C_DATA_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_dir_out() \
REG_SHADOW_SET ( R_PORT_PB_DIR , port_pb_dir_shadow , SDABIT , 1 )
# define i2c_dir_in() \
REG_SHADOW_SET ( R_PORT_PB_DIR , port_pb_dir_shadow , SDABIT , 0 )
/* control the i2c clock and data signals */
# define i2c_clk(x) \
REG_SHADOW_SET ( R_PORT_PB_DATA , port_pb_data_shadow , SCLBIT , x )
# define i2c_data(x) \
REG_SHADOW_SET ( R_PORT_PB_DATA , port_pb_data_shadow , SDABIT , x )
/* read a bit from the i2c interface */
# define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)
# 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_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_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_clk(x) \
* R_PORT_PB_I2C = ( port_pb_i2c_shadow = ( port_pb_i2c_shadow & \
~ IO_MASK ( R_PORT_PB_I2C , i2c_clk ) ) | IO_FIELD ( R_PORT_PB_I2C , i2c_clk , ( x ) ) ) ; \
REG_SHADOW_SET ( R_PORT_PB_DATA , port_pb_data_shadow , 1 , x ) ;
# define i2c_data(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_getbit() (*R_PORT_PB_READ & 0x1)
# endif
/* use the kernels delay routine */
# define i2c_delay(usecs) udelay(usecs)
2005-07-27 11:44:34 -07:00
static DEFINE_SPINLOCK ( i2c_lock ) ; /* Protect directions etc */
2005-04-16 15:20:36 -07:00
/****************** FUNCTION DEFINITION SECTION *************************/
/* generate i2c start condition */
void
i2c_start ( void )
{
/*
* SCL = 1 SDA = 1
*/
i2c_dir_out ( ) ;
i2c_delay ( CLOCK_HIGH_TIME / 6 ) ;
i2c_data ( I2C_DATA_HIGH ) ;
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME ) ;
/*
* SCL = 1 SDA = 0
*/
i2c_data ( I2C_DATA_LOW ) ;
i2c_delay ( START_CONDITION_HOLD_TIME ) ;
/*
* SCL = 0 SDA = 0
*/
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME ) ;
}
/* generate i2c stop condition */
void
i2c_stop ( void )
{
i2c_dir_out ( ) ;
/*
* SCL = 0 SDA = 0
*/
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_data ( I2C_DATA_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME * 2 ) ;
/*
* SCL = 1 SDA = 0
*/
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME * 2 ) ;
/*
* SCL = 1 SDA = 1
*/
i2c_data ( I2C_DATA_HIGH ) ;
i2c_delay ( STOP_CONDITION_HOLD_TIME ) ;
i2c_dir_in ( ) ;
}
/* write a byte to the i2c interface */
void
i2c_outbyte ( unsigned char x )
{
int i ;
i2c_dir_out ( ) ;
for ( i = 0 ; i < 8 ; i + + ) {
if ( x & 0x80 ) {
i2c_data ( I2C_DATA_HIGH ) ;
} else {
i2c_data ( I2C_DATA_LOW ) ;
}
i2c_delay ( CLOCK_LOW_TIME / 2 ) ;
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME ) ;
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME / 2 ) ;
x < < = 1 ;
}
i2c_data ( I2C_DATA_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME / 2 ) ;
/*
* enable input
*/
i2c_dir_in ( ) ;
}
/* read a byte from the i2c interface */
unsigned char
i2c_inbyte ( void )
{
unsigned char aBitByte = 0 ;
int i ;
/* Switch off I2C to get bit */
i2c_disable ( ) ;
i2c_dir_in ( ) ;
i2c_delay ( CLOCK_HIGH_TIME / 2 ) ;
/* Get bit */
aBitByte | = i2c_getbit ( ) ;
/* Enable I2C */
i2c_enable ( ) ;
i2c_delay ( CLOCK_LOW_TIME / 2 ) ;
for ( i = 1 ; i < 8 ; i + + ) {
aBitByte < < = 1 ;
/* Clock pulse */
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME ) ;
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME ) ;
/* Switch off I2C to get bit */
i2c_disable ( ) ;
i2c_dir_in ( ) ;
i2c_delay ( CLOCK_HIGH_TIME / 2 ) ;
/* Get bit */
aBitByte | = i2c_getbit ( ) ;
/* Enable I2C */
i2c_enable ( ) ;
i2c_delay ( CLOCK_LOW_TIME / 2 ) ;
}
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME ) ;
/*
* we leave the clock low , getbyte is usually followed
* by sendack / nack , they assume the clock to be low
*/
i2c_clk ( I2C_CLOCK_LOW ) ;
return aBitByte ;
}
/*#---------------------------------------------------------------------------
* #
* # FUNCTION NAME : i2c_getack
* #
* # DESCRIPTION : checks if ack was received from ic2
* #
* # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int
i2c_getack ( void )
{
int ack = 1 ;
/*
* enable output
*/
i2c_dir_out ( ) ;
/*
* Release data bus by setting
* data high
*/
i2c_data ( I2C_DATA_HIGH ) ;
/*
* enable input
*/
i2c_dir_in ( ) ;
i2c_delay ( CLOCK_HIGH_TIME / 4 ) ;
/*
* generate ACK clock pulse
*/
i2c_clk ( I2C_CLOCK_HIGH ) ;
/*
* Use PORT PB instead of I2C
* for input . ( I2C not working )
*/
i2c_clk ( 1 ) ;
i2c_data ( 1 ) ;
/*
* switch off I2C
*/
i2c_data ( 1 ) ;
i2c_disable ( ) ;
i2c_dir_in ( ) ;
/*
* now wait for ack
*/
i2c_delay ( CLOCK_HIGH_TIME / 2 ) ;
/*
* check for ack
*/
if ( i2c_getbit ( ) )
ack = 0 ;
i2c_delay ( CLOCK_HIGH_TIME / 2 ) ;
if ( ! ack ) {
if ( ! i2c_getbit ( ) ) /* receiver pulld SDA low */
ack = 1 ;
i2c_delay ( CLOCK_HIGH_TIME / 2 ) ;
}
/*
* our clock is high now , make sure data is low
* before we enable our output . If we keep data high
* and enable output , we would generate a stop condition .
*/
i2c_data ( I2C_DATA_LOW ) ;
/*
* end clock pulse
*/
i2c_enable ( ) ;
i2c_dir_out ( ) ;
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_delay ( CLOCK_HIGH_TIME / 4 ) ;
/*
* enable output
*/
i2c_dir_out ( ) ;
/*
* remove ACK clock pulse
*/
i2c_data ( I2C_DATA_HIGH ) ;
i2c_delay ( CLOCK_LOW_TIME / 2 ) ;
return ack ;
}
/*#---------------------------------------------------------------------------
* #
* # FUNCTION NAME : I2C : : sendAck
* #
* # DESCRIPTION : Send ACK on received data
* #
* # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void
i2c_sendack ( void )
{
/*
* enable output
*/
i2c_delay ( CLOCK_LOW_TIME ) ;
i2c_dir_out ( ) ;
/*
* set ack pulse high
*/
i2c_data ( I2C_DATA_LOW ) ;
/*
* generate clock pulse
*/
i2c_delay ( CLOCK_HIGH_TIME / 6 ) ;
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME ) ;
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME / 6 ) ;
/*
* reset data out
*/
i2c_data ( I2C_DATA_HIGH ) ;
i2c_delay ( CLOCK_LOW_TIME ) ;
i2c_dir_in ( ) ;
}
/*#---------------------------------------------------------------------------
* #
* # FUNCTION NAME : i2c_sendnack
* #
* # DESCRIPTION : Sends NACK on received data
* #
* # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void
i2c_sendnack ( void )
{
/*
* enable output
*/
i2c_delay ( CLOCK_LOW_TIME ) ;
i2c_dir_out ( ) ;
/*
* set data high
*/
i2c_data ( I2C_DATA_HIGH ) ;
/*
* generate clock pulse
*/
i2c_delay ( CLOCK_HIGH_TIME / 6 ) ;
i2c_clk ( I2C_CLOCK_HIGH ) ;
i2c_delay ( CLOCK_HIGH_TIME ) ;
i2c_clk ( I2C_CLOCK_LOW ) ;
i2c_delay ( CLOCK_LOW_TIME ) ;
i2c_dir_in ( ) ;
}
/*#---------------------------------------------------------------------------
* #
* # FUNCTION NAME : i2c_writereg
* #
* # DESCRIPTION : Writes a value to an I2C device
* #
* # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int
i2c_writereg ( unsigned char theSlave , unsigned char theReg ,
unsigned char theValue )
{
int error , cntr = 3 ;
unsigned long flags ;
2005-07-27 11:44:34 -07:00
spin_lock ( & i2c_lock ) ;
2005-04-16 15:20:36 -07:00
do {
error = 0 ;
/*
* we don ' t like to be interrupted
*/
local_irq_save ( flags ) ;
i2c_start ( ) ;
/*
* send slave address
*/
i2c_outbyte ( ( theSlave & 0xfe ) ) ;
/*
* wait for ack
*/
if ( ! i2c_getack ( ) )
error = 1 ;
/*
* now select register
*/
i2c_dir_out ( ) ;
i2c_outbyte ( theReg ) ;
/*
* now it ' s time to wait for ack
*/
if ( ! i2c_getack ( ) )
error | = 2 ;
/*
* send register register data
*/
i2c_outbyte ( theValue ) ;
/*
* now it ' s time to wait for ack
*/
if ( ! i2c_getack ( ) )
error | = 4 ;
/*
* end byte stream
*/
i2c_stop ( ) ;
/*
* enable interrupt again
*/
local_irq_restore ( flags ) ;
} while ( error & & cntr - - ) ;
i2c_delay ( CLOCK_LOW_TIME ) ;
2005-07-27 11:44:34 -07:00
spin_unlock ( & i2c_lock ) ;
2005-04-16 15:20:36 -07:00
return - error ;
}
/*#---------------------------------------------------------------------------
* #
* # FUNCTION NAME : i2c_readreg
* #
* # DESCRIPTION : Reads a value from the decoder registers .
* #
* # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
unsigned char
i2c_readreg ( unsigned char theSlave , unsigned char theReg )
{
unsigned char b = 0 ;
int error , cntr = 3 ;
unsigned long flags ;
2005-07-27 11:44:34 -07:00
spin_lock ( & i2c_lock ) ;
2005-04-16 15:20:36 -07:00
do {
error = 0 ;
/*
* we don ' t like to be interrupted
*/
local_irq_save ( flags ) ;
/*
* generate start condition
*/
i2c_start ( ) ;
/*
* send slave address
*/
i2c_outbyte ( ( theSlave & 0xfe ) ) ;
/*
* wait for ack
*/
if ( ! i2c_getack ( ) )
error = 1 ;
/*
* now select register
*/
i2c_dir_out ( ) ;
i2c_outbyte ( theReg ) ;
/*
* now it ' s time to wait for ack
*/
if ( ! i2c_getack ( ) )
error = 1 ;
/*
* repeat start condition
*/
i2c_delay ( CLOCK_LOW_TIME ) ;
i2c_start ( ) ;
/*
* send slave address
*/
i2c_outbyte ( theSlave | 0x01 ) ;
/*
* wait for ack
*/
if ( ! i2c_getack ( ) )
error = 1 ;
/*
* fetch register
*/
b = i2c_inbyte ( ) ;
/*
* last received byte needs to be nacked
* instead of acked
*/
2008-01-17 14:50:47 +01:00
i2c_sendnack ( ) ;
2005-04-16 15:20:36 -07:00
/*
* end sequence
*/
i2c_stop ( ) ;
/*
* enable interrupt again
*/
local_irq_restore ( flags ) ;
} while ( error & & cntr - - ) ;
2005-07-27 11:44:34 -07:00
spin_unlock ( & i2c_lock ) ;
2005-04-16 15:20:36 -07:00
return b ;
}
static int
i2c_open ( struct inode * inode , struct file * filp )
{
2008-05-18 15:32:43 -06:00
cycle_kernel_lock ( ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int
i2c_release ( struct inode * inode , struct file * filp )
{
return 0 ;
}
/* Main device API. ioctl's to write or read to/from i2c registers.
*/
static int
i2c_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
if ( _IOC_TYPE ( cmd ) ! = ETRAXI2C_IOCTYPE ) {
return - EINVAL ;
}
switch ( _IOC_NR ( cmd ) ) {
case I2C_WRITEREG :
/* write to an i2c slave */
D ( printk ( " i2cw %d %d %d \n " ,
I2C_ARGSLAVE ( arg ) ,
I2C_ARGREG ( arg ) ,
I2C_ARGVALUE ( arg ) ) ) ;
return i2c_writereg ( I2C_ARGSLAVE ( arg ) ,
I2C_ARGREG ( arg ) ,
I2C_ARGVALUE ( arg ) ) ;
case I2C_READREG :
{
unsigned char val ;
/* read from an i2c slave */
D ( printk ( " i2cr %d %d " ,
I2C_ARGSLAVE ( arg ) ,
I2C_ARGREG ( arg ) ) ) ;
val = i2c_readreg ( I2C_ARGSLAVE ( arg ) , I2C_ARGREG ( arg ) ) ;
D ( printk ( " = %d \n " , val ) ) ;
return val ;
}
default :
return - EINVAL ;
}
return 0 ;
}
2007-02-12 00:55:31 -08:00
static const struct file_operations i2c_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. ioctl = i2c_ioctl ,
. open = i2c_open ,
. release = i2c_release ,
} ;
int __init
i2c_init ( void )
{
2005-07-27 11:44:34 -07:00
static int res = 0 ;
static int first = 1 ;
if ( ! first ) {
return res ;
}
2008-01-17 14:50:47 +01:00
first = 0 ;
2005-07-27 11:44:34 -07:00
2005-04-16 15:20:36 -07:00
/* Setup and enable the Port B I2C interface */
# ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
2005-07-27 11:44:34 -07:00
if ( ( res = cris_request_io_interface ( if_i2c , " I2C " ) ) ) {
printk ( KERN_CRIT " i2c_init: Failed to get IO interface \n " ) ;
return res ;
}
2005-04-16 15:20:36 -07:00
* 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_clk , 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 ) ) ;
2005-07-27 11:44:34 -07:00
# else
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 ;
} 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
2005-04-16 15:20:36 -07:00
2005-07-27 11:44:34 -07:00
return res ;
2005-04-16 15:20:36 -07:00
}
static int __init
i2c_register ( void )
{
int res ;
2005-07-27 11:44:34 -07:00
res = i2c_init ( ) ;
if ( res < 0 )
return res ;
2005-04-16 15:20:36 -07:00
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 ;
}
2005-07-27 11:44:34 -07:00
printk ( KERN_INFO " I2C driver v2.2, (c) 1999-2004 Axis Communications AB \n " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* this makes sure that i2c_register is called during boot */
module_init ( i2c_register ) ;
/****************** END OF FILE i2c.c ********************************/