@ -17,6 +17,7 @@
# include <i2c.h>
# include <fdtdec.h>
# include <mapmem.h>
# include <wait_bit.h>
DECLARE_GLOBAL_DATA_PTR ;
@ -67,6 +68,8 @@ struct cdns_i2c_regs {
# define CDNS_I2C_FIFO_DEPTH 16
# define CDNS_I2C_TRANSFER_SIZE_MAX 255 /* Controller transfer limit */
# define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_TRANSFER_SIZE_MAX - 3)
# define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
# ifdef DEBUG
@ -247,15 +250,21 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
u32 len )
{
u8 * cur_data = data ;
struct cdns_i2c_regs * regs = i2c_bus - > regs ;
/* Set the controller in Master transmit mode and clear FIFO */
setbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_CLR_FIFO ) ;
clrbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_RW ) ;
/* Check message size against FIFO depth, and set hold bus bit
* if it is greater than FIFO depth
*/
if ( len > CDNS_I2C_FIFO_DEPTH )
setbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_HOLD ) ;
/* Clear the interrupts in status register */
writel ( 0xFF , & regs - > interrupt_status ) ;
writel ( addr , & regs - > address ) ;
while ( len - - ) {
@ -280,48 +289,98 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
return 0 ;
}
static inline bool cdns_is_hold_quirk ( int hold_quirk , int curr_recv_count )
{
return hold_quirk & & ( curr_recv_count = = CDNS_I2C_FIFO_DEPTH + 1 ) ;
}
static int cdns_i2c_read_data ( struct i2c_cdns_bus * i2c_bus , u32 addr , u8 * data ,
u32 len )
u32 recv_count )
{
u32 status ;
u32 i = 0 ;
u8 * cur_data = data ;
/* TODO: Fix this */
struct cdns_i2c_regs * regs = i2c_bus - > regs ;
int curr_recv_count ;
int updatetx , hold_quirk ;
/* Check the hardware can handle the requested bytes */
if ( ( len < 0 ) )
if ( ( recv_count < 0 ) )
return - EINVAL ;
curr_recv_count = recv_count ;
/* Check for the message size against the FIFO depth */
if ( recv_count > CDNS_I2C_FIFO_DEPTH )
setbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_HOLD ) ;
setbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_CLR_FIFO |
CDNS_I2C_CONTROL_RW ) ;
if ( recv_count > CDNS_I2C_TRANSFER_SIZE ) {
curr_recv_count = CDNS_I2C_TRANSFER_SIZE ;
writel ( curr_recv_count , & regs - > transfer_size ) ;
} else {
writel ( recv_count , & regs - > transfer_size ) ;
}
/* Start reading data */
writel ( addr , & regs - > address ) ;
writel ( len , & regs - > transfer_size ) ;
/* Wait for data */
do {
status = cdns_i2c_wait ( regs , CDNS_I2C_INTERRUPT_COMP |
CDNS_I2C_INTERRUPT_DATA ) ;
if ( ! status ) {
/* Release the bus */
clrbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_HOLD ) ;
return - ETIMEDOUT ;
updatetx = recv_count > curr_recv_count ;
hold_quirk = ( i2c_bus - > quirks & CDNS_I2C_BROKEN_HOLD_BIT ) & & updatetx ;
while ( recv_count ) {
while ( readl ( & regs - > status ) & CDNS_I2C_STATUS_RXDV ) {
if ( recv_count < CDNS_I2C_FIFO_DEPTH & &
! i2c_bus - > hold_flag ) {
clrbits_le32 ( & regs - > control ,
CDNS_I2C_CONTROL_HOLD ) ;
}
* ( cur_data ) + + = readl ( & regs - > data ) ;
recv_count - - ;
curr_recv_count - - ;
if ( cdns_is_hold_quirk ( hold_quirk , curr_recv_count ) )
break ;
}
debug ( " Read %d bytes \n " ,
len - readl ( & regs - > transfer_size ) ) ;
for ( ; i < len - readl ( & regs - > transfer_size ) ; i + + )
* ( cur_data + + ) = readl ( & regs - > data ) ;
} while ( readl ( & regs - > transfer_size ) ! = 0 ) ;
/* All done... release the bus */
if ( ! i2c_bus - > hold_flag )
clrbits_le32 ( & regs - > control , CDNS_I2C_CONTROL_HOLD ) ;
# ifdef DEBUG
cdns_i2c_debug_status ( regs ) ;
# endif
if ( cdns_is_hold_quirk ( hold_quirk , curr_recv_count ) ) {
/* wait while fifo is full */
while ( readl ( & regs - > transfer_size ) ! =
( curr_recv_count - CDNS_I2C_FIFO_DEPTH ) )
;
/*
* Check number of bytes to be received against maximum
* transfer size and update register accordingly .
*/
if ( ( recv_count - CDNS_I2C_FIFO_DEPTH ) >
CDNS_I2C_TRANSFER_SIZE ) {
writel ( CDNS_I2C_TRANSFER_SIZE ,
& regs - > transfer_size ) ;
curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
CDNS_I2C_FIFO_DEPTH ;
} else {
writel ( recv_count - CDNS_I2C_FIFO_DEPTH ,
& regs - > transfer_size ) ;
curr_recv_count = recv_count ;
}
} else if ( recv_count & & ! hold_quirk & & ! curr_recv_count ) {
writel ( addr , & regs - > address ) ;
if ( recv_count > CDNS_I2C_TRANSFER_SIZE ) {
writel ( CDNS_I2C_TRANSFER_SIZE ,
& regs - > transfer_size ) ;
curr_recv_count = CDNS_I2C_TRANSFER_SIZE ;
} else {
writel ( recv_count , & regs - > transfer_size ) ;
curr_recv_count = recv_count ;
}
}
}
/* Wait for the address and data to be sent */
if ( ! cdns_i2c_wait ( regs , CDNS_I2C_INTERRUPT_COMP ) )
return - ETIMEDOUT ;
return 0 ;
}