2007-07-12 16:12:31 +04:00
/*
* Specific bus support for PMC - TWI compliant implementation on MSP71xx .
*
* Copyright 2005 - 2007 PMC - Sierra , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/completion.h>
# include <linux/mutex.h>
# include <linux/delay.h>
2010-05-21 20:41:01 +04:00
# include <linux/io.h>
2007-07-12 16:12:31 +04:00
# define DRV_NAME "pmcmsptwi"
# define MSP_TWI_SF_CLK_REG_OFFSET 0x00
# define MSP_TWI_HS_CLK_REG_OFFSET 0x04
# define MSP_TWI_CFG_REG_OFFSET 0x08
# define MSP_TWI_CMD_REG_OFFSET 0x0c
# define MSP_TWI_ADD_REG_OFFSET 0x10
# define MSP_TWI_DAT_0_REG_OFFSET 0x14
# define MSP_TWI_DAT_1_REG_OFFSET 0x18
# define MSP_TWI_INT_STS_REG_OFFSET 0x1c
# define MSP_TWI_INT_MSK_REG_OFFSET 0x20
# define MSP_TWI_BUSY_REG_OFFSET 0x24
# define MSP_TWI_INT_STS_DONE (1 << 0)
# define MSP_TWI_INT_STS_LOST_ARBITRATION (1 << 1)
# define MSP_TWI_INT_STS_NO_RESPONSE (1 << 2)
# define MSP_TWI_INT_STS_DATA_COLLISION (1 << 3)
# define MSP_TWI_INT_STS_BUSY (1 << 4)
# define MSP_TWI_INT_STS_ALL 0x1f
# define MSP_MAX_BYTES_PER_RW 8
# define MSP_MAX_POLL 5
# define MSP_POLL_DELAY 10
# define MSP_IRQ_TIMEOUT (MSP_MAX_POLL * MSP_POLL_DELAY)
/* IO Operation macros */
# define pmcmsptwi_readl __raw_readl
# define pmcmsptwi_writel __raw_writel
/* TWI command type */
enum pmcmsptwi_cmd_type {
MSP_TWI_CMD_WRITE = 0 , /* Write only */
MSP_TWI_CMD_READ = 1 , /* Read only */
MSP_TWI_CMD_WRITE_READ = 2 , /* Write then Read */
} ;
/* The possible results of the xferCmd */
enum pmcmsptwi_xfer_result {
MSP_TWI_XFER_OK = 0 ,
MSP_TWI_XFER_TIMEOUT ,
MSP_TWI_XFER_BUSY ,
MSP_TWI_XFER_DATA_COLLISION ,
MSP_TWI_XFER_NO_RESPONSE ,
MSP_TWI_XFER_LOST_ARBITRATION ,
} ;
/* Corresponds to a PMCTWI clock configuration register */
struct pmcmsptwi_clock {
u8 filter ; /* Bits 15:12, default = 0x03 */
u16 clock ; /* Bits 9:0, default = 0x001f */
} ;
struct pmcmsptwi_clockcfg {
struct pmcmsptwi_clock standard ; /* The standard/fast clock config */
struct pmcmsptwi_clock highspeed ; /* The highspeed clock config */
} ;
/* Corresponds to the main TWI configuration register */
struct pmcmsptwi_cfg {
u8 arbf ; /* Bits 15:12, default=0x03 */
u8 nak ; /* Bits 11:8, default=0x03 */
u8 add10 ; /* Bit 7, default=0x00 */
u8 mst_code ; /* Bits 6:4, default=0x00 */
u8 arb ; /* Bit 1, default=0x01 */
u8 highspeed ; /* Bit 0, default=0x00 */
} ;
/* A single pmctwi command to issue */
struct pmcmsptwi_cmd {
u16 addr ; /* The slave address (7 or 10 bits) */
enum pmcmsptwi_cmd_type type ; /* The command type */
u8 write_len ; /* Number of bytes in the write buffer */
u8 read_len ; /* Number of bytes in the read buffer */
u8 * write_data ; /* Buffer of characters to send */
u8 * read_data ; /* Buffer to fill with incoming data */
} ;
/* The private data */
struct pmcmsptwi_data {
void __iomem * iobase ; /* iomapped base for IO */
int irq ; /* IRQ to use (0 disables) */
struct completion wait ; /* Completion for xfer */
struct mutex lock ; /* Used for threadsafeness */
enum pmcmsptwi_xfer_result last_result ; /* result of last xfer */
} ;
/* The default settings */
2008-02-24 22:03:42 +03:00
static const struct pmcmsptwi_clockcfg pmcmsptwi_defclockcfg = {
2007-07-12 16:12:31 +04:00
. standard = {
. filter = 0x3 ,
. clock = 0x1f ,
} ,
. highspeed = {
. filter = 0x3 ,
. clock = 0x1f ,
} ,
} ;
2008-02-24 22:03:42 +03:00
static const struct pmcmsptwi_cfg pmcmsptwi_defcfg = {
2007-07-12 16:12:31 +04:00
. arbf = 0x03 ,
. nak = 0x03 ,
. add10 = 0x00 ,
. mst_code = 0x00 ,
. arb = 0x01 ,
. highspeed = 0x00 ,
} ;
static struct pmcmsptwi_data pmcmsptwi_data ;
static struct i2c_adapter pmcmsptwi_adapter ;
/* inline helper functions */
static inline u32 pmcmsptwi_clock_to_reg (
const struct pmcmsptwi_clock * clock )
{
return ( ( clock - > filter & 0xf ) < < 12 ) | ( clock - > clock & 0x03ff ) ;
}
static inline u32 pmcmsptwi_cfg_to_reg ( const struct pmcmsptwi_cfg * cfg )
{
return ( ( cfg - > arbf & 0xf ) < < 12 ) |
( ( cfg - > nak & 0xf ) < < 8 ) |
( ( cfg - > add10 & 0x1 ) < < 7 ) |
( ( cfg - > mst_code & 0x7 ) < < 4 ) |
( ( cfg - > arb & 0x1 ) < < 1 ) |
( cfg - > highspeed & 0x1 ) ;
}
static inline void pmcmsptwi_reg_to_cfg ( u32 reg , struct pmcmsptwi_cfg * cfg )
{
cfg - > arbf = ( reg > > 12 ) & 0xf ;
cfg - > nak = ( reg > > 8 ) & 0xf ;
cfg - > add10 = ( reg > > 7 ) & 0x1 ;
cfg - > mst_code = ( reg > > 4 ) & 0x7 ;
cfg - > arb = ( reg > > 1 ) & 0x1 ;
cfg - > highspeed = reg & 0x1 ;
}
/*
* Sets the current clock configuration
*/
static void pmcmsptwi_set_clock_config ( const struct pmcmsptwi_clockcfg * cfg ,
struct pmcmsptwi_data * data )
{
mutex_lock ( & data - > lock ) ;
pmcmsptwi_writel ( pmcmsptwi_clock_to_reg ( & cfg - > standard ) ,
data - > iobase + MSP_TWI_SF_CLK_REG_OFFSET ) ;
pmcmsptwi_writel ( pmcmsptwi_clock_to_reg ( & cfg - > highspeed ) ,
data - > iobase + MSP_TWI_HS_CLK_REG_OFFSET ) ;
mutex_unlock ( & data - > lock ) ;
}
/*
* Gets the current TWI bus configuration
*/
static void pmcmsptwi_get_twi_config ( struct pmcmsptwi_cfg * cfg ,
struct pmcmsptwi_data * data )
{
mutex_lock ( & data - > lock ) ;
pmcmsptwi_reg_to_cfg ( pmcmsptwi_readl (
data - > iobase + MSP_TWI_CFG_REG_OFFSET ) , cfg ) ;
mutex_unlock ( & data - > lock ) ;
}
/*
* Sets the current TWI bus configuration
*/
static void pmcmsptwi_set_twi_config ( const struct pmcmsptwi_cfg * cfg ,
struct pmcmsptwi_data * data )
{
mutex_lock ( & data - > lock ) ;
pmcmsptwi_writel ( pmcmsptwi_cfg_to_reg ( cfg ) ,
data - > iobase + MSP_TWI_CFG_REG_OFFSET ) ;
mutex_unlock ( & data - > lock ) ;
}
/*
* Parses the ' int_sts ' register and returns a well - defined error code
*/
static enum pmcmsptwi_xfer_result pmcmsptwi_get_result ( u32 reg )
{
if ( reg & MSP_TWI_INT_STS_LOST_ARBITRATION ) {
dev_dbg ( & pmcmsptwi_adapter . dev ,
" Result: Lost arbitration \n " ) ;
return MSP_TWI_XFER_LOST_ARBITRATION ;
} else if ( reg & MSP_TWI_INT_STS_NO_RESPONSE ) {
dev_dbg ( & pmcmsptwi_adapter . dev ,
" Result: No response \n " ) ;
return MSP_TWI_XFER_NO_RESPONSE ;
} else if ( reg & MSP_TWI_INT_STS_DATA_COLLISION ) {
dev_dbg ( & pmcmsptwi_adapter . dev ,
" Result: Data collision \n " ) ;
return MSP_TWI_XFER_DATA_COLLISION ;
} else if ( reg & MSP_TWI_INT_STS_BUSY ) {
dev_dbg ( & pmcmsptwi_adapter . dev ,
" Result: Bus busy \n " ) ;
return MSP_TWI_XFER_BUSY ;
}
dev_dbg ( & pmcmsptwi_adapter . dev , " Result: Operation succeeded \n " ) ;
return MSP_TWI_XFER_OK ;
}
/*
* In interrupt mode , handle the interrupt .
* NOTE : Assumes data - > lock is held .
*/
static irqreturn_t pmcmsptwi_interrupt ( int irq , void * ptr )
{
struct pmcmsptwi_data * data = ptr ;
u32 reason = pmcmsptwi_readl ( data - > iobase +
MSP_TWI_INT_STS_REG_OFFSET ) ;
pmcmsptwi_writel ( reason , data - > iobase + MSP_TWI_INT_STS_REG_OFFSET ) ;
dev_dbg ( & pmcmsptwi_adapter . dev , " Got interrupt 0x%08x \n " , reason ) ;
if ( ! ( reason & MSP_TWI_INT_STS_DONE ) )
return IRQ_NONE ;
data - > last_result = pmcmsptwi_get_result ( reason ) ;
complete ( & data - > wait ) ;
return IRQ_HANDLED ;
}
/*
* Probe for and register the device and return 0 if there is one .
*/
2012-11-28 00:59:38 +04:00
static int pmcmsptwi_probe ( struct platform_device * pldev )
2007-07-12 16:12:31 +04:00
{
struct resource * res ;
int rc = - ENODEV ;
/* get the static platform resources */
res = platform_get_resource ( pldev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pldev - > dev , " IOMEM resource not found \n " ) ;
goto ret_err ;
}
/* reserve the memory region */
2009-06-14 02:20:36 +04:00
if ( ! request_mem_region ( res - > start , resource_size ( res ) ,
2007-07-12 16:12:31 +04:00
pldev - > name ) ) {
dev_err ( & pldev - > dev ,
2020-01-15 23:02:48 +03:00
" Unable to get memory/io address region %pap \n " ,
& res - > start ) ;
2007-07-12 16:12:31 +04:00
rc = - EBUSY ;
goto ret_err ;
}
/* remap the memory */
2020-01-06 11:43:50 +03:00
pmcmsptwi_data . iobase = ioremap ( res - > start ,
2009-06-14 02:20:36 +04:00
resource_size ( res ) ) ;
2007-07-12 16:12:31 +04:00
if ( ! pmcmsptwi_data . iobase ) {
dev_err ( & pldev - > dev ,
2020-01-15 23:02:48 +03:00
" Unable to ioremap address %pap \n " , & res - > start ) ;
2007-07-12 16:12:31 +04:00
rc = - EIO ;
goto ret_unreserve ;
}
/* request the irq */
pmcmsptwi_data . irq = platform_get_irq ( pldev , 0 ) ;
if ( pmcmsptwi_data . irq ) {
rc = request_irq ( pmcmsptwi_data . irq , & pmcmsptwi_interrupt ,
2012-07-17 21:34:18 +04:00
IRQF_SHARED , pldev - > name , & pmcmsptwi_data ) ;
2007-07-12 16:12:31 +04:00
if ( rc = = 0 ) {
/*
* Enable ' DONE ' interrupt only .
*
* If you enable all interrupts , you will get one on
* error and another when the operation completes .
* This way you only have to handle one interrupt ,
* but you can still check all result flags .
*/
pmcmsptwi_writel ( MSP_TWI_INT_STS_DONE ,
pmcmsptwi_data . iobase +
MSP_TWI_INT_MSK_REG_OFFSET ) ;
} else {
dev_warn ( & pldev - > dev ,
" Could not assign TWI IRQ handler "
" to irq %d (continuing with poll) \n " ,
pmcmsptwi_data . irq ) ;
pmcmsptwi_data . irq = 0 ;
}
}
init_completion ( & pmcmsptwi_data . wait ) ;
mutex_init ( & pmcmsptwi_data . lock ) ;
pmcmsptwi_set_clock_config ( & pmcmsptwi_defclockcfg , & pmcmsptwi_data ) ;
pmcmsptwi_set_twi_config ( & pmcmsptwi_defcfg , & pmcmsptwi_data ) ;
printk ( KERN_INFO DRV_NAME " : Registering MSP71xx I2C adapter \n " ) ;
pmcmsptwi_adapter . dev . parent = & pldev - > dev ;
platform_set_drvdata ( pldev , & pmcmsptwi_adapter ) ;
i2c_set_adapdata ( & pmcmsptwi_adapter , & pmcmsptwi_data ) ;
rc = i2c_add_adapter ( & pmcmsptwi_adapter ) ;
2016-08-09 14:36:17 +03:00
if ( rc )
2007-07-12 16:12:31 +04:00
goto ret_unmap ;
return 0 ;
ret_unmap :
if ( pmcmsptwi_data . irq ) {
pmcmsptwi_writel ( 0 ,
pmcmsptwi_data . iobase + MSP_TWI_INT_MSK_REG_OFFSET ) ;
free_irq ( pmcmsptwi_data . irq , & pmcmsptwi_data ) ;
}
iounmap ( pmcmsptwi_data . iobase ) ;
ret_unreserve :
2009-06-14 02:20:36 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2007-07-12 16:12:31 +04:00
ret_err :
return rc ;
}
/*
* Release the device and return 0 if there is one .
*/
2012-11-28 00:59:38 +04:00
static int pmcmsptwi_remove ( struct platform_device * pldev )
2007-07-12 16:12:31 +04:00
{
struct resource * res ;
i2c_del_adapter ( & pmcmsptwi_adapter ) ;
if ( pmcmsptwi_data . irq ) {
pmcmsptwi_writel ( 0 ,
pmcmsptwi_data . iobase + MSP_TWI_INT_MSK_REG_OFFSET ) ;
free_irq ( pmcmsptwi_data . irq , & pmcmsptwi_data ) ;
}
iounmap ( pmcmsptwi_data . iobase ) ;
res = platform_get_resource ( pldev , IORESOURCE_MEM , 0 ) ;
2009-06-14 02:20:36 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2007-07-12 16:12:31 +04:00
return 0 ;
}
/*
* Polls the ' busy ' register until the command is complete .
* NOTE : Assumes data - > lock is held .
*/
static void pmcmsptwi_poll_complete ( struct pmcmsptwi_data * data )
{
int i ;
for ( i = 0 ; i < MSP_MAX_POLL ; i + + ) {
u32 val = pmcmsptwi_readl ( data - > iobase +
MSP_TWI_BUSY_REG_OFFSET ) ;
if ( val = = 0 ) {
u32 reason = pmcmsptwi_readl ( data - > iobase +
MSP_TWI_INT_STS_REG_OFFSET ) ;
pmcmsptwi_writel ( reason , data - > iobase +
MSP_TWI_INT_STS_REG_OFFSET ) ;
data - > last_result = pmcmsptwi_get_result ( reason ) ;
return ;
}
udelay ( MSP_POLL_DELAY ) ;
}
dev_dbg ( & pmcmsptwi_adapter . dev , " Result: Poll timeout \n " ) ;
data - > last_result = MSP_TWI_XFER_TIMEOUT ;
}
/*
* Do the transfer ( low level ) :
* May use interrupt - driven or polling , depending on if an IRQ is
* presently registered .
* NOTE : Assumes data - > lock is held .
*/
static enum pmcmsptwi_xfer_result pmcmsptwi_do_xfer (
u32 reg , struct pmcmsptwi_data * data )
{
dev_dbg ( & pmcmsptwi_adapter . dev , " Writing cmd reg 0x%08x \n " , reg ) ;
pmcmsptwi_writel ( reg , data - > iobase + MSP_TWI_CMD_REG_OFFSET ) ;
if ( data - > irq ) {
unsigned long timeleft = wait_for_completion_timeout (
& data - > wait , MSP_IRQ_TIMEOUT ) ;
if ( timeleft = = 0 ) {
dev_dbg ( & pmcmsptwi_adapter . dev ,
" Result: IRQ timeout \n " ) ;
complete ( & data - > wait ) ;
data - > last_result = MSP_TWI_XFER_TIMEOUT ;
}
} else
pmcmsptwi_poll_complete ( data ) ;
return data - > last_result ;
}
/*
* Helper routine , converts ' pmctwi_cmd ' struct to register format
*/
static inline u32 pmcmsptwi_cmd_to_reg ( const struct pmcmsptwi_cmd * cmd )
{
return ( ( cmd - > type & 0x3 ) < < 8 ) |
( ( ( cmd - > write_len - 1 ) & 0x7 ) < < 4 ) |
( ( cmd - > read_len - 1 ) & 0x7 ) ;
}
/*
* Do the transfer ( high level )
*/
static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd (
struct pmcmsptwi_cmd * cmd ,
struct pmcmsptwi_data * data )
{
enum pmcmsptwi_xfer_result retval ;
mutex_lock ( & data - > lock ) ;
dev_dbg ( & pmcmsptwi_adapter . dev ,
" Setting address to 0x%04x \n " , cmd - > addr ) ;
pmcmsptwi_writel ( cmd - > addr , data - > iobase + MSP_TWI_ADD_REG_OFFSET ) ;
if ( cmd - > type = = MSP_TWI_CMD_WRITE | |
cmd - > type = = MSP_TWI_CMD_WRITE_READ ) {
2008-12-11 14:11:20 +03:00
u64 tmp = be64_to_cpup ( ( __be64 * ) cmd - > write_data ) ;
2007-07-12 16:12:31 +04:00
tmp > > = ( MSP_MAX_BYTES_PER_RW - cmd - > write_len ) * 8 ;
dev_dbg ( & pmcmsptwi_adapter . dev , " Writing 0x%016llx \n " , tmp ) ;
pmcmsptwi_writel ( tmp & 0x00000000ffffffffLL ,
data - > iobase + MSP_TWI_DAT_0_REG_OFFSET ) ;
if ( cmd - > write_len > 4 )
pmcmsptwi_writel ( tmp > > 32 ,
data - > iobase + MSP_TWI_DAT_1_REG_OFFSET ) ;
}
retval = pmcmsptwi_do_xfer ( pmcmsptwi_cmd_to_reg ( cmd ) , data ) ;
if ( retval ! = MSP_TWI_XFER_OK )
goto xfer_err ;
if ( cmd - > type = = MSP_TWI_CMD_READ | |
cmd - > type = = MSP_TWI_CMD_WRITE_READ ) {
int i ;
u64 rmsk = ~ ( 0xffffffffffffffffLL < < ( cmd - > read_len * 8 ) ) ;
u64 tmp = ( u64 ) pmcmsptwi_readl ( data - > iobase +
MSP_TWI_DAT_0_REG_OFFSET ) ;
if ( cmd - > read_len > 4 )
tmp | = ( u64 ) pmcmsptwi_readl ( data - > iobase +
MSP_TWI_DAT_1_REG_OFFSET ) < < 32 ;
tmp & = rmsk ;
dev_dbg ( & pmcmsptwi_adapter . dev , " Read 0x%016llx \n " , tmp ) ;
for ( i = 0 ; i < cmd - > read_len ; i + + )
cmd - > read_data [ i ] = tmp > > i ;
}
xfer_err :
mutex_unlock ( & data - > lock ) ;
return retval ;
}
/* -- Algorithm functions -- */
/*
* Sends an i2c command out on the adapter
*/
static int pmcmsptwi_master_xfer ( struct i2c_adapter * adap ,
struct i2c_msg * msg , int num )
{
struct pmcmsptwi_data * data = i2c_get_adapdata ( adap ) ;
struct pmcmsptwi_cmd cmd ;
struct pmcmsptwi_cfg oldcfg , newcfg ;
int ret ;
2015-01-07 14:24:10 +03:00
if ( num = = 2 ) {
2007-07-12 16:12:31 +04:00
struct i2c_msg * nextmsg = msg + 1 ;
2015-01-07 14:24:10 +03:00
cmd . type = MSP_TWI_CMD_WRITE_READ ;
cmd . write_len = msg - > len ;
cmd . write_data = msg - > buf ;
cmd . read_len = nextmsg - > len ;
cmd . read_data = nextmsg - > buf ;
2007-07-12 16:12:31 +04:00
} else if ( msg - > flags & I2C_M_RD ) {
cmd . type = MSP_TWI_CMD_READ ;
cmd . read_len = msg - > len ;
cmd . read_data = msg - > buf ;
cmd . write_len = 0 ;
cmd . write_data = NULL ;
} else {
cmd . type = MSP_TWI_CMD_WRITE ;
cmd . read_len = 0 ;
cmd . read_data = NULL ;
cmd . write_len = msg - > len ;
cmd . write_data = msg - > buf ;
}
cmd . addr = msg - > addr ;
if ( msg - > flags & I2C_M_TEN ) {
pmcmsptwi_get_twi_config ( & newcfg , data ) ;
memcpy ( & oldcfg , & newcfg , sizeof ( oldcfg ) ) ;
/* Set the special 10-bit address flag */
newcfg . add10 = 1 ;
pmcmsptwi_set_twi_config ( & newcfg , data ) ;
}
/* Execute the command */
ret = pmcmsptwi_xfer_cmd ( & cmd , data ) ;
if ( msg - > flags & I2C_M_TEN )
pmcmsptwi_set_twi_config ( & oldcfg , data ) ;
2007-10-18 14:06:30 +04:00
dev_dbg ( & adap - > dev , " I2C %s of %d bytes %s \n " ,
( msg - > flags & I2C_M_RD ) ? " read " : " write " , msg - > len ,
( ret = = MSP_TWI_XFER_OK ) ? " succeeded " : " failed " ) ;
2007-07-12 16:12:31 +04:00
if ( ret ! = MSP_TWI_XFER_OK ) {
/*
* TODO : We could potentially loop and retry in the case
* of MSP_TWI_XFER_TIMEOUT .
*/
2018-05-09 22:46:30 +03:00
return - EIO ;
2007-07-12 16:12:31 +04:00
}
2018-05-09 22:46:29 +03:00
return num ;
2007-07-12 16:12:31 +04:00
}
static u32 pmcmsptwi_i2c_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL ;
}
2017-07-09 23:57:44 +03:00
static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = {
2018-07-23 23:26:09 +03:00
. flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN ,
2015-01-07 14:24:10 +03:00
. max_write_len = MSP_MAX_BYTES_PER_RW ,
. max_read_len = MSP_MAX_BYTES_PER_RW ,
. max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW ,
. max_comb_2nd_msg_len = MSP_MAX_BYTES_PER_RW ,
} ;
2007-07-12 16:12:31 +04:00
/* -- Initialization -- */
2017-07-09 23:57:44 +03:00
static const struct i2c_algorithm pmcmsptwi_algo = {
2007-07-12 16:12:31 +04:00
. master_xfer = pmcmsptwi_master_xfer ,
. functionality = pmcmsptwi_i2c_func ,
} ;
static struct i2c_adapter pmcmsptwi_adapter = {
. owner = THIS_MODULE ,
2008-07-15 00:38:29 +04:00
. class = I2C_CLASS_HWMON | I2C_CLASS_SPD ,
2007-07-12 16:12:31 +04:00
. algo = & pmcmsptwi_algo ,
2015-01-07 14:24:10 +03:00
. quirks = & pmcmsptwi_i2c_quirks ,
2007-07-12 16:12:31 +04:00
. name = DRV_NAME ,
} ;
static struct platform_driver pmcmsptwi_driver = {
. probe = pmcmsptwi_probe ,
2012-11-28 00:59:38 +04:00
. remove = pmcmsptwi_remove ,
2007-07-17 15:03:01 +04:00
. driver = {
2007-07-12 16:12:31 +04:00
. name = DRV_NAME ,
} ,
} ;
2012-01-12 23:32:04 +04:00
module_platform_driver ( pmcmsptwi_driver ) ;
2007-07-12 16:12:31 +04:00
MODULE_DESCRIPTION ( " PMC MSP TWI/SMBus/I2C driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-01-12 23:32:04 +04:00
MODULE_ALIAS ( " platform: " DRV_NAME ) ;