/*
* Freescale i . MX28 I2C Driver
*
* Copyright ( C ) 2011 Marek Vasut < marek . vasut @ gmail . com >
* on behalf of DENX Software Engineering GmbH
*
* Partly based on Linux kernel i2c - mxs . c driver :
* Copyright ( C ) 2011 Wolfram Sang , Pengutronix e . K .
*
* Which was based on a ( non - working ) driver which was :
* Copyright ( C ) 2009 - 2010 Freescale Semiconductor , Inc . All Rights Reserved .
*
* SPDX - License - Identifier : GPL - 2.0 +
*/
# include <common.h>
# include <malloc.h>
# include <i2c.h>
# include <asm/errno.h>
# include <asm/io.h>
# include <asm/arch/clock.h>
# include <asm/arch/imx-regs.h>
# include <asm/arch/sys_proto.h>
# define MXS_I2C_MAX_TIMEOUT 1000000
static struct mxs_i2c_regs * mxs_i2c_get_base ( struct i2c_adapter * adap )
{
if ( adap - > hwadapnr = = 0 )
return ( struct mxs_i2c_regs * ) MXS_I2C0_BASE ;
else
return ( struct mxs_i2c_regs * ) MXS_I2C1_BASE ;
}
static unsigned int mxs_i2c_get_bus_speed ( struct i2c_adapter * adap )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
uint32_t clk = mxc_get_clock ( MXC_XTAL_CLK ) ;
uint32_t timing0 ;
timing0 = readl ( & i2c_regs - > hw_i2c_timing0 ) ;
/*
* This is a reverse version of the algorithm presented in
* i2c_set_bus_speed ( ) . Please refer there for details .
*/
return clk / ( ( ( ( timing0 > > 16 ) - 3 ) * 2 ) + 38 ) ;
}
static uint mxs_i2c_set_bus_speed ( struct i2c_adapter * adap , uint speed )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
/*
* The timing derivation algorithm . There is no documentation for this
* algorithm available , it was derived by using the scope and fiddling
* with constants until the result observed on the scope was good enough
* for 20 kHz , 50 kHz , 100 kHz , 200 kHz , 300 kHz and 400 kHz . It should be
* possible to assume the algorithm works for other frequencies as well .
*
* Note it was necessary to cap the frequency on both ends as it ' s not
* possible to configure completely arbitrary frequency for the I2C bus
* clock .
*/
uint32_t clk = mxc_get_clock ( MXC_XTAL_CLK ) ;
uint32_t base = ( ( clk / speed ) - 38 ) / 2 ;
uint16_t high_count = base + 3 ;
uint16_t low_count = base - 3 ;
uint16_t rcv_count = ( high_count * 3 ) / 4 ;
uint16_t xmit_count = low_count / 4 ;
if ( speed > 540000 ) {
printf ( " MXS I2C: Speed too high (%d Hz) \n " , speed ) ;
return - EINVAL ;
}
if ( speed < 12000 ) {
printf ( " MXS I2C: Speed too low (%d Hz) \n " , speed ) ;
return - EINVAL ;
}
writel ( ( high_count < < 16 ) | rcv_count , & i2c_regs - > hw_i2c_timing0 ) ;
writel ( ( low_count < < 16 ) | xmit_count , & i2c_regs - > hw_i2c_timing1 ) ;
writel ( ( 0x0030 < < I2C_TIMING2_BUS_FREE_OFFSET ) |
( 0x0030 < < I2C_TIMING2_LEADIN_COUNT_OFFSET ) ,
& i2c_regs - > hw_i2c_timing2 ) ;
return 0 ;
}
static void mxs_i2c_reset ( struct i2c_adapter * adap )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
int ret ;
int speed = mxs_i2c_get_bus_speed ( adap ) ;
ret = mxs_reset_block ( & i2c_regs - > hw_i2c_ctrl0_reg ) ;
if ( ret ) {
debug ( " MXS I2C: Block reset timeout \n " ) ;
return ;
}
writel ( I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | I2C_CTRL1_NO_SLAVE_ACK_IRQ |
I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ |
I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ ,
& i2c_regs - > hw_i2c_ctrl1_clr ) ;
writel ( I2C_QUEUECTRL_PIO_QUEUE_MODE , & i2c_regs - > hw_i2c_queuectrl_set ) ;
mxs_i2c_set_bus_speed ( adap , speed ) ;
}
static void mxs_i2c_setup_read ( struct i2c_adapter * adap , uint8_t chip , int len )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
writel ( I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START |
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
( 1 < < I2C_QUEUECMD_XFER_COUNT_OFFSET ) ,
& i2c_regs - > hw_i2c_queuecmd ) ;
writel ( ( chip < < 1 ) | 1 , & i2c_regs - > hw_i2c_data ) ;
writel ( I2C_QUEUECMD_SEND_NAK_ON_LAST | I2C_QUEUECMD_MASTER_MODE |
( len < < I2C_QUEUECMD_XFER_COUNT_OFFSET ) |
I2C_QUEUECMD_POST_SEND_STOP , & i2c_regs - > hw_i2c_queuecmd ) ;
writel ( I2C_QUEUECTRL_QUEUE_RUN , & i2c_regs - > hw_i2c_queuectrl_set ) ;
}
static int mxs_i2c_write ( struct i2c_adapter * adap , uchar chip , uint addr ,
int alen , uchar * buf , int blen , int stop )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
uint32_t data , tmp ;
int i , remain , off ;
int timeout = MXS_I2C_MAX_TIMEOUT ;
if ( ( alen > 4 ) | | ( alen = = 0 ) ) {
debug ( " MXS I2C: Invalid address length \n " ) ;
return - EINVAL ;
}
if ( stop )
stop = I2C_QUEUECMD_POST_SEND_STOP ;
writel ( I2C_QUEUECMD_PRE_SEND_START |
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
( ( blen + alen + 1 ) < < I2C_QUEUECMD_XFER_COUNT_OFFSET ) | stop ,
& i2c_regs - > hw_i2c_queuecmd ) ;
data = ( chip < < 1 ) < < 24 ;
for ( i = 0 ; i < alen ; i + + ) {
data > > = 8 ;
data | = ( ( char * ) & addr ) [ alen - i - 1 ] < < 24 ;
if ( ( i & 3 ) = = 2 )
writel ( data , & i2c_regs - > hw_i2c_data ) ;
}
off = i ;
for ( ; i < off + blen ; i + + ) {
data > > = 8 ;
data | = buf [ i - off ] < < 24 ;
if ( ( i & 3 ) = = 2 )
writel ( data , & i2c_regs - > hw_i2c_data ) ;
}
remain = 24 - ( ( i & 3 ) * 8 ) ;
if ( remain )
writel ( data > > remain , & i2c_regs - > hw_i2c_data ) ;
writel ( I2C_QUEUECTRL_QUEUE_RUN , & i2c_regs - > hw_i2c_queuectrl_set ) ;
while ( - - timeout ) {
tmp = readl ( & i2c_regs - > hw_i2c_queuestat ) ;
if ( tmp & I2C_QUEUESTAT_WR_QUEUE_EMPTY )
break ;
}
if ( ! timeout ) {
debug ( " MXS I2C: Failed transmitting data! \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int mxs_i2c_wait_for_ack ( struct i2c_adapter * adap )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
uint32_t tmp ;
int timeout = MXS_I2C_MAX_TIMEOUT ;
for ( ; ; ) {
tmp = readl ( & i2c_regs - > hw_i2c_ctrl1 ) ;
if ( tmp & I2C_CTRL1_NO_SLAVE_ACK_IRQ ) {
debug ( " MXS I2C: No slave ACK \n " ) ;
goto err ;
}
if ( tmp & (
I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ |
I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ ) ) {
debug ( " MXS I2C: Error (CTRL1 = %08x) \n " , tmp ) ;
goto err ;
}
if ( tmp & I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ )
break ;
if ( ! timeout - - ) {
debug ( " MXS I2C: Operation timed out \n " ) ;
goto err ;
}
udelay ( 1 ) ;
}
return 0 ;
err :
mxs_i2c_reset ( adap ) ;
return 1 ;
}
static int mxs_i2c_if_read ( struct i2c_adapter * adap , uint8_t chip ,
uint addr , int alen , uint8_t * buffer ,
int len )
{
struct mxs_i2c_regs * i2c_regs = mxs_i2c_get_base ( adap ) ;
uint32_t tmp = 0 ;
int timeout = MXS_I2C_MAX_TIMEOUT ;
int ret ;
int i ;
ret = mxs_i2c_write ( adap , chip , addr , alen , NULL , 0 , 0 ) ;
if ( ret ) {
debug ( " MXS I2C: Failed writing address \n " ) ;
return ret ;
}
ret = mxs_i2c_wait_for_ack ( adap ) ;
if ( ret ) {
debug ( " MXS I2C: Failed writing address \n " ) ;
return ret ;
}
mxs_i2c_setup_read ( adap , chip , len ) ;
ret = mxs_i2c_wait_for_ack ( adap ) ;
if ( ret ) {
debug ( " MXS I2C: Failed reading address \n " ) ;
return ret ;
}
for ( i = 0 ; i < len ; i + + ) {
if ( ! ( i & 3 ) ) {
while ( - - timeout ) {
tmp = readl ( & i2c_regs - > hw_i2c_queuestat ) ;
if ( ! ( tmp & I2C_QUEUESTAT_RD_QUEUE_EMPTY ) )
break ;
}
if ( ! timeout ) {
debug ( " MXS I2C: Failed receiving data! \n " ) ;
return - ETIMEDOUT ;
}
tmp = readl ( & i2c_regs - > hw_i2c_queuedata ) ;
}
buffer [ i ] = tmp & 0xff ;
tmp > > = 8 ;
}
return 0 ;
}
static int mxs_i2c_if_write ( struct i2c_adapter * adap , uint8_t chip ,
uint addr , int alen , uint8_t * buffer ,
int len )
{
int ret ;
ret = mxs_i2c_write ( adap , chip , addr , alen , buffer , len , 1 ) ;
if ( ret ) {
debug ( " MXS I2C: Failed writing address \n " ) ;
return ret ;
}
ret = mxs_i2c_wait_for_ack ( adap ) ;
if ( ret )
debug ( " MXS I2C: Failed writing address \n " ) ;
return ret ;
}
static int mxs_i2c_probe ( struct i2c_adapter * adap , uint8_t chip )
{
int ret ;
ret = mxs_i2c_write ( adap , chip , 0 , 1 , NULL , 0 , 1 ) ;
if ( ! ret )
ret = mxs_i2c_wait_for_ack ( adap ) ;
mxs_i2c_reset ( adap ) ;
return ret ;
}
static void mxs_i2c_init ( struct i2c_adapter * adap , int speed , int slaveaddr )
{
mxs_i2c_reset ( adap ) ;
mxs_i2c_set_bus_speed ( adap , speed ) ;
return ;
}
U_BOOT_I2C_ADAP_COMPLETE ( mxs0 , mxs_i2c_init , mxs_i2c_probe ,
mxs_i2c_if_read , mxs_i2c_if_write ,
mxs_i2c_set_bus_speed ,
CONFIG_SYS_I2C_SPEED , 0 , 0 )
U_BOOT_I2C_ADAP_COMPLETE ( mxs1 , mxs_i2c_init , mxs_i2c_probe ,
mxs_i2c_if_read , mxs_i2c_if_write ,
mxs_i2c_set_bus_speed ,
CONFIG_SYS_I2C_SPEED , 0 , 1 )