2012-01-27 12:45:37 +04:00
/*
* Copyright 2011 , Netlogic Microsystems Inc .
* Copyright 2004 , Matt Porter < mporter @ kernel . crashing . org >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
2013-01-21 14:09:03 +04:00
# include <linux/err.h>
2012-01-27 12:45:37 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/i2c.h>
# include <linux/io.h>
# include <linux/platform_device.h>
/* XLR I2C REGISTERS */
# define XLR_I2C_CFG 0x00
# define XLR_I2C_CLKDIV 0x01
# define XLR_I2C_DEVADDR 0x02
# define XLR_I2C_ADDR 0x03
# define XLR_I2C_DATAOUT 0x04
# define XLR_I2C_DATAIN 0x05
# define XLR_I2C_STATUS 0x06
# define XLR_I2C_STARTXFR 0x07
# define XLR_I2C_BYTECNT 0x08
# define XLR_I2C_HDSTATIM 0x09
/* XLR I2C REGISTERS FLAGS */
# define XLR_I2C_BUS_BUSY 0x01
# define XLR_I2C_SDOEMPTY 0x02
# define XLR_I2C_RXRDY 0x04
# define XLR_I2C_ACK_ERR 0x08
# define XLR_I2C_ARB_STARTERR 0x30
/* Register Values */
# define XLR_I2C_CFG_ADDR 0xF8
# define XLR_I2C_CFG_NOADDR 0xFA
# define XLR_I2C_STARTXFR_ND 0x02 /* No Data */
# define XLR_I2C_STARTXFR_RD 0x01 /* Read */
# define XLR_I2C_STARTXFR_WR 0x00 /* Write */
# define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */
/*
* On XLR / XLS , we need to use __raw_ IO to read the I2C registers
* because they are in the big - endian MMIO area on the SoC .
*
* The readl / writel implementation on XLR / XLS byteswaps , because
* those are for its little - endian PCI space ( see arch / mips / Kconfig ) .
*/
static inline void xlr_i2c_wreg ( u32 __iomem * base , unsigned int reg , u32 val )
{
__raw_writel ( val , base + reg ) ;
}
static inline u32 xlr_i2c_rdreg ( u32 __iomem * base , unsigned int reg )
{
return __raw_readl ( base + reg ) ;
}
struct xlr_i2c_private {
struct i2c_adapter adap ;
u32 __iomem * iobase ;
} ;
static int xlr_i2c_tx ( struct xlr_i2c_private * priv , u16 len ,
u8 * buf , u16 addr )
{
struct i2c_adapter * adap = & priv - > adap ;
unsigned long timeout , stoptime , checktime ;
u32 i2c_status ;
int pos , timedout ;
u8 offset , byte ;
offset = buf [ 0 ] ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_ADDR , offset ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DEVADDR , addr ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_CFG , XLR_I2C_CFG_ADDR ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_BYTECNT , len - 1 ) ;
timeout = msecs_to_jiffies ( XLR_I2C_TIMEOUT ) ;
stoptime = jiffies + timeout ;
timedout = 0 ;
pos = 1 ;
retry :
if ( len = = 1 ) {
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_STARTXFR ,
XLR_I2C_STARTXFR_ND ) ;
} else {
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DATAOUT , buf [ pos ] ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_STARTXFR ,
XLR_I2C_STARTXFR_WR ) ;
}
while ( ! timedout ) {
checktime = jiffies ;
i2c_status = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ;
if ( i2c_status & XLR_I2C_SDOEMPTY ) {
pos + + ;
/* need to do a empty dataout after the last byte */
byte = ( pos < len ) ? buf [ pos ] : 0 ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DATAOUT , byte ) ;
/* reset timeout on successful xmit */
stoptime = jiffies + timeout ;
}
timedout = time_after ( checktime , stoptime ) ;
if ( i2c_status & XLR_I2C_ARB_STARTERR ) {
if ( timedout )
break ;
goto retry ;
}
if ( i2c_status & XLR_I2C_ACK_ERR )
return - EIO ;
if ( ( i2c_status & XLR_I2C_BUS_BUSY ) = = 0 & & pos > = len )
return 0 ;
}
dev_err ( & adap - > dev , " I2C transmit timeout \n " ) ;
return - ETIMEDOUT ;
}
static int xlr_i2c_rx ( struct xlr_i2c_private * priv , u16 len , u8 * buf , u16 addr )
{
struct i2c_adapter * adap = & priv - > adap ;
u32 i2c_status ;
unsigned long timeout , stoptime , checktime ;
int nbytes , timedout ;
u8 byte ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_CFG , XLR_I2C_CFG_NOADDR ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_BYTECNT , len ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DEVADDR , addr ) ;
timeout = msecs_to_jiffies ( XLR_I2C_TIMEOUT ) ;
stoptime = jiffies + timeout ;
timedout = 0 ;
nbytes = 0 ;
retry :
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_STARTXFR , XLR_I2C_STARTXFR_RD ) ;
while ( ! timedout ) {
checktime = jiffies ;
i2c_status = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ;
if ( i2c_status & XLR_I2C_RXRDY ) {
if ( nbytes > len )
return - EIO ; /* should not happen */
/* we need to do a dummy datain when nbytes == len */
byte = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_DATAIN ) ;
if ( nbytes < len )
buf [ nbytes ] = byte ;
nbytes + + ;
/* reset timeout on successful read */
stoptime = jiffies + timeout ;
}
timedout = time_after ( checktime , stoptime ) ;
if ( i2c_status & XLR_I2C_ARB_STARTERR ) {
if ( timedout )
break ;
goto retry ;
}
if ( i2c_status & XLR_I2C_ACK_ERR )
return - EIO ;
if ( ( i2c_status & XLR_I2C_BUS_BUSY ) = = 0 )
return 0 ;
}
dev_err ( & adap - > dev , " I2C receive timeout \n " ) ;
return - ETIMEDOUT ;
}
static int xlr_i2c_xfer ( struct i2c_adapter * adap ,
struct i2c_msg * msgs , int num )
{
struct i2c_msg * msg ;
int i ;
int ret = 0 ;
struct xlr_i2c_private * priv = i2c_get_adapdata ( adap ) ;
for ( i = 0 ; ret = = 0 & & i < num ; i + + ) {
msg = & msgs [ i ] ;
if ( msg - > flags & I2C_M_RD )
ret = xlr_i2c_rx ( priv , msg - > len , & msg - > buf [ 0 ] ,
msg - > addr ) ;
else
ret = xlr_i2c_tx ( priv , msg - > len , & msg - > buf [ 0 ] ,
msg - > addr ) ;
}
return ( ret ! = 0 ) ? ret : num ;
}
static u32 xlr_func ( struct i2c_adapter * adap )
{
/* Emulate SMBUS over I2C */
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C ;
}
static struct i2c_algorithm xlr_i2c_algo = {
. master_xfer = xlr_i2c_xfer ,
. functionality = xlr_func ,
} ;
2012-11-28 00:59:38 +04:00
static int xlr_i2c_probe ( struct platform_device * pdev )
2012-01-27 12:45:37 +04:00
{
struct xlr_i2c_private * priv ;
struct resource * res ;
int ret ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:03 +04:00
priv - > iobase = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( priv - > iobase ) )
return PTR_ERR ( priv - > iobase ) ;
2012-01-27 12:45:37 +04:00
priv - > adap . dev . parent = & pdev - > dev ;
priv - > adap . owner = THIS_MODULE ;
priv - > adap . algo_data = priv ;
priv - > adap . algo = & xlr_i2c_algo ;
priv - > adap . nr = pdev - > id ;
priv - > adap . class = I2C_CLASS_HWMON ;
snprintf ( priv - > adap . name , sizeof ( priv - > adap . name ) , " xlr-i2c " ) ;
i2c_set_adapdata ( & priv - > adap , priv ) ;
ret = i2c_add_numbered_adapter ( & priv - > adap ) ;
if ( ret < 0 ) {
dev_err ( & priv - > adap . dev , " Failed to add i2c bus. \n " ) ;
return ret ;
}
platform_set_drvdata ( pdev , priv ) ;
dev_info ( & priv - > adap . dev , " Added I2C Bus. \n " ) ;
return 0 ;
}
2012-11-28 00:59:38 +04:00
static int xlr_i2c_remove ( struct platform_device * pdev )
2012-01-27 12:45:37 +04:00
{
struct xlr_i2c_private * priv ;
priv = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & priv - > adap ) ;
return 0 ;
}
static struct platform_driver xlr_i2c_driver = {
. probe = xlr_i2c_probe ,
2012-11-28 00:59:38 +04:00
. remove = xlr_i2c_remove ,
2012-01-27 12:45:37 +04:00
. driver = {
. name = " xlr-i2cbus " ,
. owner = THIS_MODULE ,
} ,
} ;
module_platform_driver ( xlr_i2c_driver ) ;
MODULE_AUTHOR ( " Ganesan Ramalingam <ganesanr@netlogicmicro.com> " ) ;
MODULE_DESCRIPTION ( " XLR/XLS SoC I2C Controller driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:xlr-i2cbus " ) ;