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>
2015-11-02 05:03:36 +03:00
# include <linux/of_device.h>
# include <linux/clk.h>
2015-12-16 02:15:06 +03:00
# include <linux/interrupt.h>
# include <linux/wait.h>
2012-01-27 12:45:37 +04:00
/* 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
2015-12-16 02:15:06 +03:00
/* Sigma Designs additional registers */
# define XLR_I2C_INT_EN 0x09
# define XLR_I2C_INT_STAT 0x0a
2012-01-27 12:45:37 +04:00
/* 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 ) ;
}
2015-12-16 02:15:06 +03:00
# define XLR_I2C_FLAG_IRQ 1
2015-11-02 05:03:36 +03:00
struct xlr_i2c_config {
2015-12-16 02:15:06 +03:00
u32 flags ; /* optional feature support */
2015-11-02 05:03:36 +03:00
u32 status_busy ; /* value of STATUS[0] when busy */
u32 cfg_extra ; /* extra CFG bits to set */
} ;
2012-01-27 12:45:37 +04:00
struct xlr_i2c_private {
struct i2c_adapter adap ;
u32 __iomem * iobase ;
2015-12-16 02:15:06 +03:00
int irq ;
int pos ;
struct i2c_msg * msg ;
2015-11-02 05:03:36 +03:00
const struct xlr_i2c_config * cfg ;
2015-12-16 02:15:06 +03:00
wait_queue_head_t wait ;
2015-11-02 05:03:36 +03:00
struct clk * clk ;
2012-01-27 12:45:37 +04:00
} ;
2015-11-02 05:03:36 +03:00
static int xlr_i2c_busy ( struct xlr_i2c_private * priv , u32 status )
{
return ( status & XLR_I2C_BUS_BUSY ) = = priv - > cfg - > status_busy ;
}
2015-12-16 02:15:06 +03:00
static int xlr_i2c_idle ( struct xlr_i2c_private * priv )
{
return ! xlr_i2c_busy ( priv , xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ) ;
}
static int xlr_i2c_wait ( struct xlr_i2c_private * priv , unsigned long timeout )
{
int status ;
int t ;
t = wait_event_timeout ( priv - > wait , xlr_i2c_idle ( priv ) ,
msecs_to_jiffies ( timeout ) ) ;
if ( ! t )
return - ETIMEDOUT ;
status = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ;
return status & XLR_I2C_ACK_ERR ? - EIO : 0 ;
}
static void xlr_i2c_tx_irq ( struct xlr_i2c_private * priv , u32 status )
{
struct i2c_msg * msg = priv - > msg ;
if ( status & XLR_I2C_SDOEMPTY )
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DATAOUT ,
msg - > buf [ priv - > pos + + ] ) ;
}
static void xlr_i2c_rx_irq ( struct xlr_i2c_private * priv , u32 status )
{
struct i2c_msg * msg = priv - > msg ;
if ( status & XLR_I2C_RXRDY )
msg - > buf [ priv - > pos + + ] =
xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_DATAIN ) ;
}
static irqreturn_t xlr_i2c_irq ( int irq , void * dev_id )
{
struct xlr_i2c_private * priv = dev_id ;
struct i2c_msg * msg = priv - > msg ;
u32 int_stat , status ;
int_stat = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_INT_STAT ) ;
if ( ! int_stat )
return IRQ_NONE ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_INT_STAT , int_stat ) ;
if ( ! msg )
return IRQ_HANDLED ;
status = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ;
if ( priv - > pos < msg - > len ) {
if ( msg - > flags & I2C_M_RD )
xlr_i2c_rx_irq ( priv , status ) ;
else
xlr_i2c_tx_irq ( priv , status ) ;
}
if ( ! xlr_i2c_busy ( priv , status ) )
wake_up ( & priv - > wait ) ;
return IRQ_HANDLED ;
}
2012-01-27 12:45:37 +04:00
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 ;
2015-12-16 02:15:05 +03:00
u8 offset ;
u32 xfer ;
if ( ! len )
return - EOPNOTSUPP ;
2012-01-27 12:45:37 +04:00
offset = buf [ 0 ] ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_ADDR , offset ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DEVADDR , addr ) ;
2015-11-02 05:03:36 +03:00
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_CFG ,
XLR_I2C_CFG_ADDR | priv - > cfg - > cfg_extra ) ;
2012-01-27 12:45:37 +04:00
timeout = msecs_to_jiffies ( XLR_I2C_TIMEOUT ) ;
stoptime = jiffies + timeout ;
timedout = 0 ;
2015-12-16 02:15:05 +03:00
2012-01-27 12:45:37 +04:00
if ( len = = 1 ) {
2015-12-16 02:15:05 +03:00
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_BYTECNT , len - 1 ) ;
xfer = XLR_I2C_STARTXFR_ND ;
pos = 1 ;
2012-01-27 12:45:37 +04:00
} else {
2015-12-16 02:15:05 +03:00
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_BYTECNT , len - 2 ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DATAOUT , buf [ 1 ] ) ;
xfer = XLR_I2C_STARTXFR_WR ;
pos = 2 ;
2012-01-27 12:45:37 +04:00
}
2015-12-16 02:15:06 +03:00
priv - > pos = pos ;
2015-12-16 02:15:05 +03:00
retry :
/* retry can only happen on the first byte */
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_STARTXFR , xfer ) ;
2015-12-16 02:15:06 +03:00
if ( priv - > irq > 0 )
return xlr_i2c_wait ( priv , XLR_I2C_TIMEOUT * len ) ;
2012-01-27 12:45:37 +04:00
while ( ! timedout ) {
checktime = jiffies ;
i2c_status = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ;
2015-12-16 02:15:05 +03:00
if ( ( i2c_status & XLR_I2C_SDOEMPTY ) & & pos < len ) {
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DATAOUT , buf [ pos + + ] ) ;
2012-01-27 12:45:37 +04:00
/* 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 ;
2015-11-02 05:03:36 +03:00
if ( ! xlr_i2c_busy ( priv , i2c_status ) & & pos > = len )
2012-01-27 12:45:37 +04:00
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 ;
2015-12-16 02:15:05 +03:00
if ( ! len )
return - EOPNOTSUPP ;
2012-01-27 12:45:37 +04:00
2015-11-02 05:03:36 +03:00
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_CFG ,
XLR_I2C_CFG_NOADDR | priv - > cfg - > cfg_extra ) ;
2015-12-16 02:15:05 +03:00
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_BYTECNT , len - 1 ) ;
2012-01-27 12:45:37 +04:00
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_DEVADDR , addr ) ;
2015-12-16 02:15:06 +03:00
priv - > pos = 0 ;
2012-01-27 12:45:37 +04:00
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 ) ;
2015-12-16 02:15:06 +03:00
if ( priv - > irq > 0 )
return xlr_i2c_wait ( priv , XLR_I2C_TIMEOUT * len ) ;
2012-01-27 12:45:37 +04:00
while ( ! timedout ) {
checktime = jiffies ;
i2c_status = xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_STATUS ) ;
if ( i2c_status & XLR_I2C_RXRDY ) {
2015-12-16 02:15:05 +03:00
if ( nbytes > = len )
2012-01-27 12:45:37 +04:00
return - EIO ; /* should not happen */
2015-12-16 02:15:05 +03:00
buf [ nbytes + + ] =
xlr_i2c_rdreg ( priv - > iobase , XLR_I2C_DATAIN ) ;
2012-01-27 12:45:37 +04:00
/* 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 ;
2015-11-02 05:03:36 +03:00
if ( ! xlr_i2c_busy ( priv , i2c_status ) )
2012-01-27 12:45:37 +04:00
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 ) ;
2015-11-02 05:03:36 +03:00
ret = clk_enable ( priv - > clk ) ;
if ( ret )
return ret ;
2015-12-16 02:15:06 +03:00
if ( priv - > irq )
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_INT_EN , 0xf ) ;
2012-01-27 12:45:37 +04:00
for ( i = 0 ; ret = = 0 & & i < num ; i + + ) {
msg = & msgs [ i ] ;
2015-12-16 02:15:06 +03:00
priv - > msg = msg ;
2012-01-27 12:45:37 +04:00
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 ) ;
}
2015-12-16 02:15:06 +03:00
if ( priv - > irq )
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_INT_EN , 0 ) ;
2015-11-02 05:03:36 +03:00
clk_disable ( priv - > clk ) ;
2015-12-16 02:15:06 +03:00
priv - > msg = NULL ;
2015-11-02 05:03:36 +03:00
2012-01-27 12:45:37 +04:00
return ( ret ! = 0 ) ? ret : num ;
}
static u32 xlr_func ( struct i2c_adapter * adap )
{
/* Emulate SMBUS over I2C */
2015-12-16 02:15:05 +03:00
return ( I2C_FUNC_SMBUS_EMUL & ~ I2C_FUNC_SMBUS_QUICK ) | I2C_FUNC_I2C ;
2012-01-27 12:45:37 +04:00
}
2017-01-27 21:06:17 +03:00
static const struct i2c_algorithm xlr_i2c_algo = {
2012-01-27 12:45:37 +04:00
. master_xfer = xlr_i2c_xfer ,
. functionality = xlr_func ,
} ;
2015-11-02 05:03:36 +03:00
static const struct xlr_i2c_config xlr_i2c_config_default = {
. status_busy = XLR_I2C_BUS_BUSY ,
. cfg_extra = 0 ,
} ;
static const struct xlr_i2c_config xlr_i2c_config_tangox = {
2015-12-16 02:15:06 +03:00
. flags = XLR_I2C_FLAG_IRQ ,
2015-11-02 05:03:36 +03:00
. status_busy = 0 ,
. cfg_extra = 1 < < 8 ,
} ;
static const struct of_device_id xlr_i2c_dt_ids [ ] = {
{
. compatible = " sigma,smp8642-i2c " ,
. data = & xlr_i2c_config_tangox ,
} ,
{ }
} ;
2016-10-19 00:01:47 +03:00
MODULE_DEVICE_TABLE ( of , xlr_i2c_dt_ids ) ;
2015-11-02 05:03:36 +03:00
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
{
2015-11-02 05:03:36 +03:00
const struct of_device_id * match ;
2012-01-27 12:45:37 +04:00
struct xlr_i2c_private * priv ;
struct resource * res ;
2015-11-02 05:03:36 +03:00
struct clk * clk ;
unsigned long clk_rate ;
unsigned long clk_div ;
u32 busfreq ;
2015-12-16 02:15:06 +03:00
int irq ;
2012-01-27 12:45:37 +04:00
int ret ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2015-11-02 05:03:36 +03:00
match = of_match_device ( xlr_i2c_dt_ids , & pdev - > dev ) ;
if ( match )
priv - > cfg = match - > data ;
else
priv - > cfg = & xlr_i2c_config_default ;
2012-01-27 12:45:37 +04:00
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
2015-12-16 02:15:06 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq > 0 & & ( priv - > cfg - > flags & XLR_I2C_FLAG_IRQ ) ) {
priv - > irq = irq ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_INT_EN , 0 ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_INT_STAT , 0xf ) ;
ret = devm_request_irq ( & pdev - > dev , priv - > irq , xlr_i2c_irq ,
IRQF_SHARED , dev_name ( & pdev - > dev ) ,
priv ) ;
if ( ret )
return ret ;
init_waitqueue_head ( & priv - > wait ) ;
}
2015-11-02 05:03:36 +03:00
if ( of_property_read_u32 ( pdev - > dev . of_node , " clock-frequency " ,
& busfreq ) )
busfreq = 100000 ;
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( ! IS_ERR ( clk ) ) {
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
clk_rate = clk_get_rate ( clk ) ;
clk_div = DIV_ROUND_UP ( clk_rate , 2 * busfreq ) ;
xlr_i2c_wreg ( priv - > iobase , XLR_I2C_CLKDIV , clk_div ) ;
clk_disable ( clk ) ;
priv - > clk = clk ;
}
2012-01-27 12:45:37 +04:00
priv - > adap . dev . parent = & pdev - > dev ;
2015-11-02 05:03:36 +03:00
priv - > adap . dev . of_node = pdev - > dev . of_node ;
2012-01-27 12:45:37 +04:00
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 ) ;
2016-08-09 14:36:17 +03:00
if ( ret < 0 )
2012-01-27 12:45:37 +04:00
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 ) ;
2015-11-02 05:03:36 +03:00
clk_unprepare ( priv - > clk ) ;
2012-01-27 12:45:37 +04:00
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 " ,
2015-11-02 05:03:36 +03:00
. of_match_table = xlr_i2c_dt_ids ,
2012-01-27 12:45:37 +04:00
} ,
} ;
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 " ) ;