2007-05-02 01:26:32 +04:00
/*
2008-04-23 00:16:48 +04:00
* Blackfin On - Chip Two Wire Interface Driver
2007-05-02 01:26:32 +04:00
*
2008-04-23 00:16:48 +04:00
* Copyright 2005 - 2007 Analog Devices Inc .
2007-05-02 01:26:32 +04:00
*
2008-04-23 00:16:48 +04:00
* Enter bugs at http : //blackfin.uclinux.org/
2007-05-02 01:26:32 +04:00
*
2008-04-23 00:16:48 +04:00
* Licensed under the GPL - 2 or later .
2007-05-02 01:26:32 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/i2c.h>
2009-06-14 09:55:37 +04:00
# include <linux/io.h>
2007-05-02 01:26:32 +04:00
# include <linux/mm.h>
# include <linux/timer.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <asm/blackfin.h>
2008-04-23 00:16:48 +04:00
# include <asm/portmux.h>
2007-05-02 01:26:32 +04:00
# include <asm/irq.h>
# define POLL_TIMEOUT (2 * HZ)
/* SMBus mode*/
2008-04-23 00:16:47 +04:00
# define TWI_I2C_MODE_STANDARD 1
# define TWI_I2C_MODE_STANDARDSUB 2
# define TWI_I2C_MODE_COMBINED 3
# define TWI_I2C_MODE_REPEAT 4
2007-05-02 01:26:32 +04:00
struct bfin_twi_iface {
int irq ;
spinlock_t lock ;
char read_write ;
u8 command ;
u8 * transPtr ;
int readNum ;
int writeNum ;
int cur_mode ;
int manual_stop ;
int result ;
int timeout_count ;
struct timer_list timeout_timer ;
struct i2c_adapter adap ;
struct completion complete ;
2008-04-23 00:16:47 +04:00
struct i2c_msg * pmsg ;
int msg_num ;
int cur_msg ;
2008-07-27 10:41:54 +04:00
u16 saved_clkdiv ;
u16 saved_control ;
2008-04-23 00:16:48 +04:00
void __iomem * regs_base ;
2007-05-02 01:26:32 +04:00
} ;
2008-04-23 00:16:48 +04:00
# define DEFINE_TWI_REG(reg, off) \
static inline u16 read_ # # reg ( struct bfin_twi_iface * iface ) \
{ return bfin_read16 ( iface - > regs_base + ( off ) ) ; } \
static inline void write_ # # reg ( struct bfin_twi_iface * iface , u16 v ) \
{ bfin_write16 ( iface - > regs_base + ( off ) , v ) ; }
DEFINE_TWI_REG ( CLKDIV , 0x00 )
DEFINE_TWI_REG ( CONTROL , 0x04 )
DEFINE_TWI_REG ( SLAVE_CTL , 0x08 )
DEFINE_TWI_REG ( SLAVE_STAT , 0x0C )
DEFINE_TWI_REG ( SLAVE_ADDR , 0x10 )
DEFINE_TWI_REG ( MASTER_CTL , 0x14 )
DEFINE_TWI_REG ( MASTER_STAT , 0x18 )
DEFINE_TWI_REG ( MASTER_ADDR , 0x1C )
DEFINE_TWI_REG ( INT_STAT , 0x20 )
DEFINE_TWI_REG ( INT_MASK , 0x24 )
DEFINE_TWI_REG ( FIFO_CTL , 0x28 )
DEFINE_TWI_REG ( FIFO_STAT , 0x2C )
DEFINE_TWI_REG ( XMT_DATA8 , 0x80 )
DEFINE_TWI_REG ( XMT_DATA16 , 0x84 )
DEFINE_TWI_REG ( RCV_DATA8 , 0x88 )
DEFINE_TWI_REG ( RCV_DATA16 , 0x8C )
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:48 +04:00
static const u16 pin_req [ 2 ] [ 3 ] = {
{ P_TWI0_SCL , P_TWI0_SDA , 0 } ,
{ P_TWI1_SCL , P_TWI1_SDA , 0 } ,
} ;
2007-05-02 01:26:32 +04:00
static void bfin_twi_handle_interrupt ( struct bfin_twi_iface * iface )
{
2008-04-23 00:16:48 +04:00
unsigned short twi_int_status = read_INT_STAT ( iface ) ;
unsigned short mast_stat = read_MASTER_STAT ( iface ) ;
2007-05-02 01:26:32 +04:00
if ( twi_int_status & XMTSERV ) {
/* Transmit next data */
if ( iface - > writeNum > 0 ) {
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface , * ( iface - > transPtr + + ) ) ;
2007-05-02 01:26:32 +04:00
iface - > writeNum - - ;
}
/* start receive immediately after complete sending in
* combine mode .
*/
2008-04-23 00:16:47 +04:00
else if ( iface - > cur_mode = = TWI_I2C_MODE_COMBINED )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) | MDIR | RSTART ) ;
2008-04-23 00:16:47 +04:00
else if ( iface - > manual_stop )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) | STOP ) ;
2008-04-23 00:16:47 +04:00
else if ( iface - > cur_mode = = TWI_I2C_MODE_REPEAT & &
2009-05-19 15:23:49 +04:00
iface - > cur_msg + 1 < iface - > msg_num ) {
if ( iface - > pmsg [ iface - > cur_msg + 1 ] . flags & I2C_M_RD )
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) | RSTART | MDIR ) ;
else
write_MASTER_CTL ( iface ,
( read_MASTER_CTL ( iface ) | RSTART ) & ~ MDIR ) ;
}
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
/* Clear status */
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , XMTSERV ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
}
if ( twi_int_status & RCVSERV ) {
if ( iface - > readNum > 0 ) {
/* Receive next data */
2008-04-23 00:16:48 +04:00
* ( iface - > transPtr ) = read_RCV_DATA8 ( iface ) ;
2007-05-02 01:26:32 +04:00
if ( iface - > cur_mode = = TWI_I2C_MODE_COMBINED ) {
/* Change combine mode into sub mode after
* read first data .
*/
iface - > cur_mode = TWI_I2C_MODE_STANDARDSUB ;
/* Get read number from first byte in block
* combine mode .
*/
if ( iface - > readNum = = 1 & & iface - > manual_stop )
iface - > readNum = * iface - > transPtr + 1 ;
}
iface - > transPtr + + ;
iface - > readNum - - ;
} else if ( iface - > manual_stop ) {
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) | STOP ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
2008-04-23 00:16:47 +04:00
} else if ( iface - > cur_mode = = TWI_I2C_MODE_REPEAT & &
2009-05-19 15:23:49 +04:00
iface - > cur_msg + 1 < iface - > msg_num ) {
if ( iface - > pmsg [ iface - > cur_msg + 1 ] . flags & I2C_M_RD )
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) | RSTART | MDIR ) ;
else
write_MASTER_CTL ( iface ,
( read_MASTER_CTL ( iface ) | RSTART ) & ~ MDIR ) ;
2008-04-23 00:16:47 +04:00
SSYNC ( ) ;
2007-05-02 01:26:32 +04:00
}
/* Clear interrupt source */
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , RCVSERV ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
}
if ( twi_int_status & MERR ) {
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , MERR ) ;
write_INT_MASK ( iface , 0 ) ;
write_MASTER_STAT ( iface , 0x3e ) ;
write_MASTER_CTL ( iface , 0 ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
2008-04-23 00:16:47 +04:00
iface - > result = - EIO ;
2007-05-02 01:26:32 +04:00
/* if both err and complete int stats are set, return proper
* results .
*/
if ( twi_int_status & MCOMP ) {
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , MCOMP ) ;
write_INT_MASK ( iface , 0 ) ;
write_MASTER_CTL ( iface , 0 ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
/* If it is a quick transfer, only address bug no data,
* not an err , return 1.
*/
if ( iface - > writeNum = = 0 & & ( mast_stat & BUFRDERR ) )
iface - > result = 1 ;
/* If address not acknowledged return -1,
* else return 0.
*/
else if ( ! ( mast_stat & ANAK ) )
iface - > result = 0 ;
}
complete ( & iface - > complete ) ;
return ;
}
if ( twi_int_status & MCOMP ) {
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , MCOMP ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
if ( iface - > cur_mode = = TWI_I2C_MODE_COMBINED ) {
if ( iface - > readNum = = 0 ) {
/* set the read number to 1 and ask for manual
* stop in block combine mode
*/
iface - > readNum = 1 ;
iface - > manual_stop = 1 ;
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) | ( 0xff < < 6 ) ) ;
2007-05-02 01:26:32 +04:00
} else {
/* set the readd number in other
* combine mode .
*/
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
( read_MASTER_CTL ( iface ) &
2007-05-02 01:26:32 +04:00
( ~ ( 0xff < < 6 ) ) ) |
2008-04-23 00:16:48 +04:00
( iface - > readNum < < 6 ) ) ;
2007-05-02 01:26:32 +04:00
}
/* remove restart bit and enable master receive */
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) & ~ RSTART ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
2008-04-23 00:16:47 +04:00
} else if ( iface - > cur_mode = = TWI_I2C_MODE_REPEAT & &
iface - > cur_msg + 1 < iface - > msg_num ) {
iface - > cur_msg + + ;
iface - > transPtr = iface - > pmsg [ iface - > cur_msg ] . buf ;
iface - > writeNum = iface - > readNum =
iface - > pmsg [ iface - > cur_msg ] . len ;
/* Set Transmit device address */
2008-04-23 00:16:48 +04:00
write_MASTER_ADDR ( iface ,
2008-04-23 00:16:47 +04:00
iface - > pmsg [ iface - > cur_msg ] . addr ) ;
if ( iface - > pmsg [ iface - > cur_msg ] . flags & I2C_M_RD )
iface - > read_write = I2C_SMBUS_READ ;
else {
iface - > read_write = I2C_SMBUS_WRITE ;
/* Transmit first data */
if ( iface - > writeNum > 0 ) {
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface ,
2008-04-23 00:16:47 +04:00
* ( iface - > transPtr + + ) ) ;
iface - > writeNum - - ;
SSYNC ( ) ;
}
}
if ( iface - > pmsg [ iface - > cur_msg ] . len < = 255 )
2009-05-19 15:21:58 +04:00
write_MASTER_CTL ( iface ,
( read_MASTER_CTL ( iface ) &
( ~ ( 0xff < < 6 ) ) ) |
( iface - > pmsg [ iface - > cur_msg ] . len < < 6 ) ) ;
2008-04-23 00:16:47 +04:00
else {
2009-05-19 15:21:58 +04:00
write_MASTER_CTL ( iface ,
( read_MASTER_CTL ( iface ) |
( 0xff < < 6 ) ) ) ;
2008-04-23 00:16:47 +04:00
iface - > manual_stop = 1 ;
}
/* remove restart bit and enable master receive */
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
read_MASTER_CTL ( iface ) & ~ RSTART ) ;
2008-04-23 00:16:47 +04:00
SSYNC ( ) ;
2007-05-02 01:26:32 +04:00
} else {
iface - > result = 1 ;
2008-04-23 00:16:48 +04:00
write_INT_MASK ( iface , 0 ) ;
write_MASTER_CTL ( iface , 0 ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
complete ( & iface - > complete ) ;
}
}
}
/* Interrupt handler */
static irqreturn_t bfin_twi_interrupt_entry ( int irq , void * dev_id )
{
struct bfin_twi_iface * iface = dev_id ;
unsigned long flags ;
spin_lock_irqsave ( & iface - > lock , flags ) ;
del_timer ( & iface - > timeout_timer ) ;
bfin_twi_handle_interrupt ( iface ) ;
spin_unlock_irqrestore ( & iface - > lock , flags ) ;
return IRQ_HANDLED ;
}
static void bfin_twi_timeout ( unsigned long data )
{
struct bfin_twi_iface * iface = ( struct bfin_twi_iface * ) data ;
unsigned long flags ;
spin_lock_irqsave ( & iface - > lock , flags ) ;
bfin_twi_handle_interrupt ( iface ) ;
if ( iface - > result = = 0 ) {
iface - > timeout_count - - ;
if ( iface - > timeout_count > 0 ) {
iface - > timeout_timer . expires = jiffies + POLL_TIMEOUT ;
add_timer ( & iface - > timeout_timer ) ;
} else {
iface - > result = - 1 ;
complete ( & iface - > complete ) ;
}
}
spin_unlock_irqrestore ( & iface - > lock , flags ) ;
}
/*
* Generic i2c master transfer entrypoint
*/
static int bfin_twi_master_xfer ( struct i2c_adapter * adap ,
struct i2c_msg * msgs , int num )
{
struct bfin_twi_iface * iface = adap - > algo_data ;
struct i2c_msg * pmsg ;
int rc = 0 ;
2008-04-23 00:16:48 +04:00
if ( ! ( read_CONTROL ( iface ) & TWI_ENA ) )
2007-05-02 01:26:32 +04:00
return - ENXIO ;
2008-04-23 00:16:48 +04:00
while ( read_MASTER_STAT ( iface ) & BUSBUSY )
2007-05-02 01:26:32 +04:00
yield ( ) ;
2008-04-23 00:16:47 +04:00
iface - > pmsg = msgs ;
iface - > msg_num = num ;
iface - > cur_msg = 0 ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
pmsg = & msgs [ 0 ] ;
if ( pmsg - > flags & I2C_M_TEN ) {
dev_err ( & adap - > dev , " 10 bits addr not supported! \n " ) ;
return - EINVAL ;
}
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
iface - > cur_mode = TWI_I2C_MODE_REPEAT ;
iface - > manual_stop = 0 ;
iface - > transPtr = pmsg - > buf ;
iface - > writeNum = iface - > readNum = pmsg - > len ;
iface - > result = 0 ;
iface - > timeout_count = 10 ;
2008-04-23 00:16:48 +04:00
init_completion ( & ( iface - > complete ) ) ;
2008-04-23 00:16:47 +04:00
/* Set Transmit device address */
2008-04-23 00:16:48 +04:00
write_MASTER_ADDR ( iface , pmsg - > addr ) ;
2008-04-23 00:16:47 +04:00
/* FIFO Initiation. Data in FIFO should be
* discarded before start a new operation .
*/
2008-04-23 00:16:48 +04:00
write_FIFO_CTL ( iface , 0x3 ) ;
2008-04-23 00:16:47 +04:00
SSYNC ( ) ;
2008-04-23 00:16:48 +04:00
write_FIFO_CTL ( iface , 0 ) ;
2008-04-23 00:16:47 +04:00
SSYNC ( ) ;
if ( pmsg - > flags & I2C_M_RD )
iface - > read_write = I2C_SMBUS_READ ;
else {
iface - > read_write = I2C_SMBUS_WRITE ;
/* Transmit first data */
if ( iface - > writeNum > 0 ) {
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface , * ( iface - > transPtr + + ) ) ;
2008-04-23 00:16:47 +04:00
iface - > writeNum - - ;
SSYNC ( ) ;
2007-05-02 01:26:32 +04:00
}
2008-04-23 00:16:47 +04:00
}
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
/* clear int stat */
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , MERR | MCOMP | XMTSERV | RCVSERV ) ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
/* Interrupt mask . Enable XMT, RCV interrupt */
2008-04-23 00:16:48 +04:00
write_INT_MASK ( iface , MCOMP | MERR | RCVSERV | XMTSERV ) ;
2008-04-23 00:16:47 +04:00
SSYNC ( ) ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
if ( pmsg - > len < = 255 )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , pmsg - > len < < 6 ) ;
2008-04-23 00:16:47 +04:00
else {
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , 0xff < < 6 ) ;
2008-04-23 00:16:47 +04:00
iface - > manual_stop = 1 ;
}
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
iface - > timeout_timer . expires = jiffies + POLL_TIMEOUT ;
add_timer ( & iface - > timeout_timer ) ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
/* Master enable */
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , read_MASTER_CTL ( iface ) | MEN |
2008-04-23 00:16:47 +04:00
( ( iface - > read_write = = I2C_SMBUS_READ ) ? MDIR : 0 ) |
( ( CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100 ) ? FAST : 0 ) ) ;
SSYNC ( ) ;
wait_for_completion ( & iface - > complete ) ;
rc = iface - > result ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:47 +04:00
if ( rc = = 1 )
return num ;
else
return rc ;
2007-05-02 01:26:32 +04:00
}
/*
* SMBus type transfer entrypoint
*/
int bfin_twi_smbus_xfer ( struct i2c_adapter * adap , u16 addr ,
unsigned short flags , char read_write ,
u8 command , int size , union i2c_smbus_data * data )
{
struct bfin_twi_iface * iface = adap - > algo_data ;
int rc = 0 ;
2008-04-23 00:16:48 +04:00
if ( ! ( read_CONTROL ( iface ) & TWI_ENA ) )
2007-05-02 01:26:32 +04:00
return - ENXIO ;
2008-04-23 00:16:48 +04:00
while ( read_MASTER_STAT ( iface ) & BUSBUSY )
2007-05-02 01:26:32 +04:00
yield ( ) ;
iface - > writeNum = 0 ;
iface - > readNum = 0 ;
/* Prepare datas & select mode */
switch ( size ) {
case I2C_SMBUS_QUICK :
iface - > transPtr = NULL ;
iface - > cur_mode = TWI_I2C_MODE_STANDARD ;
break ;
case I2C_SMBUS_BYTE :
if ( data = = NULL )
iface - > transPtr = NULL ;
else {
if ( read_write = = I2C_SMBUS_READ )
iface - > readNum = 1 ;
else
iface - > writeNum = 1 ;
iface - > transPtr = & data - > byte ;
}
iface - > cur_mode = TWI_I2C_MODE_STANDARD ;
break ;
case I2C_SMBUS_BYTE_DATA :
if ( read_write = = I2C_SMBUS_READ ) {
iface - > readNum = 1 ;
iface - > cur_mode = TWI_I2C_MODE_COMBINED ;
} else {
iface - > writeNum = 1 ;
iface - > cur_mode = TWI_I2C_MODE_STANDARDSUB ;
}
iface - > transPtr = & data - > byte ;
break ;
case I2C_SMBUS_WORD_DATA :
if ( read_write = = I2C_SMBUS_READ ) {
iface - > readNum = 2 ;
iface - > cur_mode = TWI_I2C_MODE_COMBINED ;
} else {
iface - > writeNum = 2 ;
iface - > cur_mode = TWI_I2C_MODE_STANDARDSUB ;
}
iface - > transPtr = ( u8 * ) & data - > word ;
break ;
case I2C_SMBUS_PROC_CALL :
iface - > writeNum = 2 ;
iface - > readNum = 2 ;
iface - > cur_mode = TWI_I2C_MODE_COMBINED ;
iface - > transPtr = ( u8 * ) & data - > word ;
break ;
case I2C_SMBUS_BLOCK_DATA :
if ( read_write = = I2C_SMBUS_READ ) {
iface - > readNum = 0 ;
iface - > cur_mode = TWI_I2C_MODE_COMBINED ;
} else {
iface - > writeNum = data - > block [ 0 ] + 1 ;
iface - > cur_mode = TWI_I2C_MODE_STANDARDSUB ;
}
iface - > transPtr = data - > block ;
break ;
2009-05-27 13:24:10 +04:00
case I2C_SMBUS_I2C_BLOCK_DATA :
if ( read_write = = I2C_SMBUS_READ ) {
iface - > readNum = data - > block [ 0 ] ;
iface - > cur_mode = TWI_I2C_MODE_COMBINED ;
} else {
iface - > writeNum = data - > block [ 0 ] ;
iface - > cur_mode = TWI_I2C_MODE_STANDARDSUB ;
}
iface - > transPtr = ( u8 * ) & data - > block [ 1 ] ;
break ;
2007-05-02 01:26:32 +04:00
default :
return - 1 ;
}
iface - > result = 0 ;
iface - > manual_stop = 0 ;
iface - > read_write = read_write ;
iface - > command = command ;
iface - > timeout_count = 10 ;
2008-04-23 00:16:48 +04:00
init_completion ( & ( iface - > complete ) ) ;
2007-05-02 01:26:32 +04:00
/* FIFO Initiation. Data in FIFO should be discarded before
* start a new operation .
*/
2008-04-23 00:16:48 +04:00
write_FIFO_CTL ( iface , 0x3 ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
2008-04-23 00:16:48 +04:00
write_FIFO_CTL ( iface , 0 ) ;
2007-05-02 01:26:32 +04:00
/* clear int stat */
2008-04-23 00:16:48 +04:00
write_INT_STAT ( iface , MERR | MCOMP | XMTSERV | RCVSERV ) ;
2007-05-02 01:26:32 +04:00
/* Set Transmit device address */
2008-04-23 00:16:48 +04:00
write_MASTER_ADDR ( iface , addr ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
iface - > timeout_timer . expires = jiffies + POLL_TIMEOUT ;
add_timer ( & iface - > timeout_timer ) ;
switch ( iface - > cur_mode ) {
case TWI_I2C_MODE_STANDARDSUB :
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface , iface - > command ) ;
write_INT_MASK ( iface , MCOMP | MERR |
2007-05-02 01:26:32 +04:00
( ( iface - > read_write = = I2C_SMBUS_READ ) ?
RCVSERV : XMTSERV ) ) ;
SSYNC ( ) ;
if ( iface - > writeNum + 1 < = 255 )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , ( iface - > writeNum + 1 ) < < 6 ) ;
2007-05-02 01:26:32 +04:00
else {
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , 0xff < < 6 ) ;
2007-05-02 01:26:32 +04:00
iface - > manual_stop = 1 ;
}
/* Master enable */
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , read_MASTER_CTL ( iface ) | MEN |
2007-05-02 01:26:32 +04:00
( ( CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100 ) ? FAST : 0 ) ) ;
break ;
case TWI_I2C_MODE_COMBINED :
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface , iface - > command ) ;
write_INT_MASK ( iface , MCOMP | MERR | RCVSERV | XMTSERV ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
if ( iface - > writeNum > 0 )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , ( iface - > writeNum + 1 ) < < 6 ) ;
2007-05-02 01:26:32 +04:00
else
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , 0x1 < < 6 ) ;
2007-05-02 01:26:32 +04:00
/* Master enable */
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , read_MASTER_CTL ( iface ) | MEN |
2007-05-02 01:26:32 +04:00
( ( CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100 ) ? FAST : 0 ) ) ;
break ;
default :
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , 0 ) ;
2007-05-02 01:26:32 +04:00
if ( size ! = I2C_SMBUS_QUICK ) {
/* Don't access xmit data register when this is a
* read operation .
*/
if ( iface - > read_write ! = I2C_SMBUS_READ ) {
if ( iface - > writeNum > 0 ) {
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface ,
* ( iface - > transPtr + + ) ) ;
2007-05-02 01:26:32 +04:00
if ( iface - > writeNum < = 255 )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
iface - > writeNum < < 6 ) ;
2007-05-02 01:26:32 +04:00
else {
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
0xff < < 6 ) ;
2007-05-02 01:26:32 +04:00
iface - > manual_stop = 1 ;
}
iface - > writeNum - - ;
} else {
2008-04-23 00:16:48 +04:00
write_XMT_DATA8 ( iface , iface - > command ) ;
write_MASTER_CTL ( iface , 1 < < 6 ) ;
2007-05-02 01:26:32 +04:00
}
} else {
if ( iface - > readNum > 0 & & iface - > readNum < = 255 )
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface ,
iface - > readNum < < 6 ) ;
2007-05-02 01:26:32 +04:00
else if ( iface - > readNum > 255 ) {
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , 0xff < < 6 ) ;
2007-05-02 01:26:32 +04:00
iface - > manual_stop = 1 ;
} else {
del_timer ( & iface - > timeout_timer ) ;
break ;
}
}
}
2008-04-23 00:16:48 +04:00
write_INT_MASK ( iface , MCOMP | MERR |
2007-05-02 01:26:32 +04:00
( ( iface - > read_write = = I2C_SMBUS_READ ) ?
RCVSERV : XMTSERV ) ) ;
SSYNC ( ) ;
/* Master enable */
2008-04-23 00:16:48 +04:00
write_MASTER_CTL ( iface , read_MASTER_CTL ( iface ) | MEN |
2007-05-02 01:26:32 +04:00
( ( iface - > read_write = = I2C_SMBUS_READ ) ? MDIR : 0 ) |
( ( CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100 ) ? FAST : 0 ) ) ;
break ;
}
SSYNC ( ) ;
wait_for_completion ( & iface - > complete ) ;
rc = ( iface - > result > = 0 ) ? 0 : - 1 ;
return rc ;
}
/*
* Return what the adapter supports
*/
static u32 bfin_twi_functionality ( struct i2c_adapter * adap )
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL |
2009-05-27 13:24:10 +04:00
I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK ;
2007-05-02 01:26:32 +04:00
}
static struct i2c_algorithm bfin_twi_algorithm = {
. master_xfer = bfin_twi_master_xfer ,
. smbus_xfer = bfin_twi_smbus_xfer ,
. functionality = bfin_twi_functionality ,
} ;
2008-07-27 10:41:54 +04:00
static int i2c_bfin_twi_suspend ( struct platform_device * pdev , pm_message_t state )
2007-05-02 01:26:32 +04:00
{
2008-07-27 10:41:54 +04:00
struct bfin_twi_iface * iface = platform_get_drvdata ( pdev ) ;
iface - > saved_clkdiv = read_CLKDIV ( iface ) ;
iface - > saved_control = read_CONTROL ( iface ) ;
free_irq ( iface - > irq , iface ) ;
2007-05-02 01:26:32 +04:00
/* Disable TWI */
2008-07-27 10:41:54 +04:00
write_CONTROL ( iface , iface - > saved_control & ~ TWI_ENA ) ;
2007-05-02 01:26:32 +04:00
return 0 ;
}
2008-07-27 10:41:54 +04:00
static int i2c_bfin_twi_resume ( struct platform_device * pdev )
2007-05-02 01:26:32 +04:00
{
2008-07-27 10:41:54 +04:00
struct bfin_twi_iface * iface = platform_get_drvdata ( pdev ) ;
2007-05-02 01:26:32 +04:00
2008-07-27 10:41:54 +04:00
int rc = request_irq ( iface - > irq , bfin_twi_interrupt_entry ,
IRQF_DISABLED , pdev - > name , iface ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " Can't get IRQ %d ! \n " , iface - > irq ) ;
return - ENODEV ;
}
/* Resume TWI interface clock as specified */
write_CLKDIV ( iface , iface - > saved_clkdiv ) ;
/* Resume TWI */
write_CONTROL ( iface , iface - > saved_control ) ;
2007-05-02 01:26:32 +04:00
return 0 ;
}
2008-04-23 00:16:48 +04:00
static int i2c_bfin_twi_probe ( struct platform_device * pdev )
2007-05-02 01:26:32 +04:00
{
2008-04-23 00:16:48 +04:00
struct bfin_twi_iface * iface ;
2007-05-02 01:26:32 +04:00
struct i2c_adapter * p_adap ;
2008-04-23 00:16:48 +04:00
struct resource * res ;
2007-05-02 01:26:32 +04:00
int rc ;
2009-05-18 16:14:41 +04:00
unsigned int clkhilow ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:48 +04:00
iface = kzalloc ( sizeof ( struct bfin_twi_iface ) , GFP_KERNEL ) ;
if ( ! iface ) {
dev_err ( & pdev - > dev , " Cannot allocate memory \n " ) ;
rc = - ENOMEM ;
goto out_error_nomem ;
}
2007-05-02 01:26:32 +04:00
spin_lock_init ( & ( iface - > lock ) ) ;
2008-04-23 00:16:48 +04:00
/* Find and map our resources */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " Cannot get IORESOURCE_MEM \n " ) ;
rc = - ENOENT ;
goto out_error_get_res ;
}
2009-06-14 02:20:36 +04:00
iface - > regs_base = ioremap ( res - > start , resource_size ( res ) ) ;
2008-04-23 00:16:48 +04:00
if ( iface - > regs_base = = NULL ) {
dev_err ( & pdev - > dev , " Cannot map IO \n " ) ;
rc = - ENXIO ;
goto out_error_ioremap ;
}
iface - > irq = platform_get_irq ( pdev , 0 ) ;
if ( iface - > irq < 0 ) {
dev_err ( & pdev - > dev , " No IRQ specified \n " ) ;
rc = - ENOENT ;
goto out_error_no_irq ;
}
2007-05-02 01:26:32 +04:00
init_timer ( & ( iface - > timeout_timer ) ) ;
iface - > timeout_timer . function = bfin_twi_timeout ;
iface - > timeout_timer . data = ( unsigned long ) iface ;
p_adap = & iface - > adap ;
2008-04-23 00:16:48 +04:00
p_adap - > nr = pdev - > id ;
strlcpy ( p_adap - > name , pdev - > name , sizeof ( p_adap - > name ) ) ;
2007-05-02 01:26:32 +04:00
p_adap - > algo = & bfin_twi_algorithm ;
p_adap - > algo_data = iface ;
2009-01-07 16:29:16 +03:00
p_adap - > class = I2C_CLASS_HWMON | I2C_CLASS_SPD ;
2008-04-23 00:16:48 +04:00
p_adap - > dev . parent = & pdev - > dev ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:48 +04:00
rc = peripheral_request_list ( pin_req [ pdev - > id ] , " i2c-bfin-twi " ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " Can't setup pin mux! \n " ) ;
goto out_error_pin_mux ;
}
2007-05-02 01:26:32 +04:00
rc = request_irq ( iface - > irq , bfin_twi_interrupt_entry ,
2008-04-23 00:16:48 +04:00
IRQF_DISABLED , pdev - > name , iface ) ;
2007-05-02 01:26:32 +04:00
if ( rc ) {
2008-04-23 00:16:48 +04:00
dev_err ( & pdev - > dev , " Can't get IRQ %d ! \n " , iface - > irq ) ;
rc = - ENODEV ;
goto out_error_req_irq ;
2007-05-02 01:26:32 +04:00
}
/* Set TWI internal clock as 10MHz */
2009-12-21 17:28:30 +03:00
write_CONTROL ( iface , ( ( get_sclk ( ) / 1000 / 1000 + 5 ) / 10 ) & 0x7F ) ;
2007-05-02 01:26:32 +04:00
2009-05-18 16:14:41 +04:00
/*
* We will not end up with a CLKDIV = 0 because no one will specify
2009-12-21 17:28:30 +03:00
* 20 kHz SCL or less in Kconfig now . ( 5 * 1000 / 20 = 250 )
2009-05-18 16:14:41 +04:00
*/
2009-12-21 17:28:30 +03:00
clkhilow = ( ( 10 * 1000 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ ) + 1 ) / 2 ;
2009-05-18 16:14:41 +04:00
2007-05-02 01:26:32 +04:00
/* Set Twi interface clock as specified */
2009-05-18 16:14:41 +04:00
write_CLKDIV ( iface , ( clkhilow < < 8 ) | clkhilow ) ;
2007-05-02 01:26:32 +04:00
/* Enable TWI */
2008-04-23 00:16:48 +04:00
write_CONTROL ( iface , read_CONTROL ( iface ) | TWI_ENA ) ;
2007-05-02 01:26:32 +04:00
SSYNC ( ) ;
2008-01-27 20:14:52 +03:00
rc = i2c_add_numbered_adapter ( p_adap ) ;
2008-04-23 00:16:48 +04:00
if ( rc < 0 ) {
dev_err ( & pdev - > dev , " Can't add i2c adapter! \n " ) ;
goto out_error_add_adapter ;
}
platform_set_drvdata ( pdev , iface ) ;
2007-05-02 01:26:32 +04:00
2008-04-23 00:16:48 +04:00
dev_info ( & pdev - > dev , " Blackfin BF5xx on-chip I2C TWI Contoller, "
" regs_base@%p \n " , iface - > regs_base ) ;
2008-04-23 00:16:48 +04:00
return 0 ;
out_error_add_adapter :
free_irq ( iface - > irq , iface ) ;
out_error_req_irq :
out_error_no_irq :
2008-04-23 00:16:48 +04:00
peripheral_free_list ( pin_req [ pdev - > id ] ) ;
out_error_pin_mux :
2008-04-23 00:16:48 +04:00
iounmap ( iface - > regs_base ) ;
out_error_ioremap :
out_error_get_res :
kfree ( iface ) ;
out_error_nomem :
2007-05-02 01:26:32 +04:00
return rc ;
}
static int i2c_bfin_twi_remove ( struct platform_device * pdev )
{
struct bfin_twi_iface * iface = platform_get_drvdata ( pdev ) ;
platform_set_drvdata ( pdev , NULL ) ;
i2c_del_adapter ( & ( iface - > adap ) ) ;
free_irq ( iface - > irq , iface ) ;
2008-04-23 00:16:48 +04:00
peripheral_free_list ( pin_req [ pdev - > id ] ) ;
2008-04-23 00:16:48 +04:00
iounmap ( iface - > regs_base ) ;
kfree ( iface ) ;
2007-05-02 01:26:32 +04:00
return 0 ;
}
static struct platform_driver i2c_bfin_twi_driver = {
. probe = i2c_bfin_twi_probe ,
. remove = i2c_bfin_twi_remove ,
. suspend = i2c_bfin_twi_suspend ,
. resume = i2c_bfin_twi_resume ,
. driver = {
. name = " i2c-bfin-twi " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init i2c_bfin_twi_init ( void )
{
return platform_driver_register ( & i2c_bfin_twi_driver ) ;
}
static void __exit i2c_bfin_twi_exit ( void )
{
platform_driver_unregister ( & i2c_bfin_twi_driver ) ;
}
module_init ( i2c_bfin_twi_init ) ;
module_exit ( i2c_bfin_twi_exit ) ;
2008-04-23 00:16:48 +04:00
MODULE_AUTHOR ( " Bryan Wu, Sonic Zhang " ) ;
MODULE_DESCRIPTION ( " Blackfin BF5xx on-chip I2C TWI Contoller Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-23 00:16:49 +04:00
MODULE_ALIAS ( " platform:i2c-bfin-twi " ) ;