2020-12-10 19:51:11 +03:00
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2016-11-20 19:56:14 +03:00
/*
2020-12-10 19:51:11 +03:00
* Mellanox i2c driver
2016-11-20 19:56:14 +03:00
*
2020-12-10 19:51:11 +03:00
* Copyright ( C ) 2016 - 2020 Mellanox Technologies
2016-11-20 19:56:14 +03:00
*/
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
2021-01-06 02:33:47 +03:00
# include <linux/platform_data/mlxreg.h>
2016-11-20 19:56:14 +03:00
# include <linux/platform_device.h>
2021-01-06 02:33:47 +03:00
# include <linux/regmap.h>
2016-11-20 19:56:14 +03:00
/* General defines */
# define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000
# define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld"
# define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD)
# define MLXCPLD_I2C_BUS_NUM 1
# define MLXCPLD_I2C_DATA_REG_SZ 36
2018-03-27 17:01:22 +03:00
# define MLXCPLD_I2C_DATA_SZ_BIT BIT(5)
# define MLXCPLD_I2C_DATA_SZ_MASK GENMASK(6, 5)
2018-03-27 17:01:23 +03:00
# define MLXCPLD_I2C_SMBUS_BLK_BIT BIT(7)
2016-11-20 19:56:14 +03:00
# define MLXCPLD_I2C_MAX_ADDR_LEN 4
# define MLXCPLD_I2C_RETR_NUM 2
# define MLXCPLD_I2C_XFER_TO 500000 /* usec */
2021-08-23 17:45:03 +03:00
# define MLXCPLD_I2C_POLL_TIME 200 /* usec */
2016-11-20 19:56:14 +03:00
/* LPC I2C registers */
2018-03-27 17:01:22 +03:00
# define MLXCPLD_LPCI2C_CPBLTY_REG 0x0
2016-11-20 19:56:14 +03:00
# define MLXCPLD_LPCI2C_CTRL_REG 0x1
# define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4
# define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5
# define MLXCPLD_LPCI2C_CMD_REG 0x6
# define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7
# define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8
# define MLXCPLD_LPCI2C_STATUS_REG 0x9
# define MLXCPLD_LPCI2C_DATA_REG 0xa
/* LPC I2C masks and parametres */
# define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1
# define MLXCPLD_LPCI2C_TRANS_END 0x1
# define MLXCPLD_LPCI2C_STATUS_NACK 0x10
# define MLXCPLD_LPCI2C_NO_IND 0
# define MLXCPLD_LPCI2C_ACK_IND 1
# define MLXCPLD_LPCI2C_NACK_IND 2
2021-01-06 02:33:47 +03:00
# define MLXCPLD_I2C_FREQ_1000KHZ_SET 0x04
2021-08-23 17:45:02 +03:00
# define MLXCPLD_I2C_FREQ_400KHZ_SET 0x0c
2021-01-06 02:33:47 +03:00
# define MLXCPLD_I2C_FREQ_100KHZ_SET 0x42
enum mlxcpld_i2c_frequency {
MLXCPLD_I2C_FREQ_1000KHZ = 1 ,
MLXCPLD_I2C_FREQ_400KHZ = 2 ,
MLXCPLD_I2C_FREQ_100KHZ = 3 ,
} ;
2016-11-20 19:56:14 +03:00
struct mlxcpld_i2c_curr_xfer {
u8 cmd ;
u8 addr_width ;
u8 data_len ;
u8 msg_num ;
struct i2c_msg * msg ;
} ;
struct mlxcpld_i2c_priv {
struct i2c_adapter adap ;
u32 base_addr ;
struct mutex lock ;
struct mlxcpld_i2c_curr_xfer xfer ;
struct device * dev ;
2018-03-27 17:01:23 +03:00
bool smbus_block ;
2021-08-23 17:45:04 +03:00
int polling_time ;
2016-11-20 19:56:14 +03:00
} ;
static void mlxcpld_i2c_lpc_write_buf ( u8 * data , u8 len , u32 addr )
{
int i ;
for ( i = 0 ; i < len - len % 4 ; i + = 4 )
outl ( * ( u32 * ) ( data + i ) , addr + i ) ;
for ( ; i < len ; + + i )
outb ( * ( data + i ) , addr + i ) ;
}
static void mlxcpld_i2c_lpc_read_buf ( u8 * data , u8 len , u32 addr )
{
int i ;
for ( i = 0 ; i < len - len % 4 ; i + = 4 )
* ( u32 * ) ( data + i ) = inl ( addr + i ) ;
for ( ; i < len ; + + i )
* ( data + i ) = inb ( addr + i ) ;
}
static void mlxcpld_i2c_read_comm ( struct mlxcpld_i2c_priv * priv , u8 offs ,
u8 * data , u8 datalen )
{
u32 addr = priv - > base_addr + offs ;
switch ( datalen ) {
case 1 :
* ( data ) = inb ( addr ) ;
break ;
case 2 :
* ( ( u16 * ) data ) = inw ( addr ) ;
break ;
case 3 :
* ( ( u16 * ) data ) = inw ( addr ) ;
* ( data + 2 ) = inb ( addr + 2 ) ;
break ;
case 4 :
* ( ( u32 * ) data ) = inl ( addr ) ;
break ;
default :
mlxcpld_i2c_lpc_read_buf ( data , datalen , addr ) ;
break ;
}
}
static void mlxcpld_i2c_write_comm ( struct mlxcpld_i2c_priv * priv , u8 offs ,
u8 * data , u8 datalen )
{
u32 addr = priv - > base_addr + offs ;
switch ( datalen ) {
case 1 :
outb ( * ( data ) , addr ) ;
break ;
case 2 :
outw ( * ( ( u16 * ) data ) , addr ) ;
break ;
case 3 :
outw ( * ( ( u16 * ) data ) , addr ) ;
outb ( * ( data + 2 ) , addr + 2 ) ;
break ;
case 4 :
outl ( * ( ( u32 * ) data ) , addr ) ;
break ;
default :
mlxcpld_i2c_lpc_write_buf ( data , datalen , addr ) ;
break ;
}
}
/*
* Check validity of received i2c messages parameters .
* Returns 0 if OK , other - in case of invalid parameters .
*/
static int mlxcpld_i2c_check_msg_params ( struct mlxcpld_i2c_priv * priv ,
struct i2c_msg * msgs , int num )
{
int i ;
if ( ! num ) {
dev_err ( priv - > dev , " Incorrect 0 num of messages \n " ) ;
return - EINVAL ;
}
if ( unlikely ( msgs [ 0 ] . addr > 0x7f ) ) {
dev_err ( priv - > dev , " Invalid address 0x%03x \n " ,
msgs [ 0 ] . addr ) ;
return - EINVAL ;
}
for ( i = 0 ; i < num ; + + i ) {
if ( unlikely ( ! msgs [ i ] . buf ) ) {
dev_err ( priv - > dev , " Invalid buf in msg[%d] \n " ,
i ) ;
return - EINVAL ;
}
if ( unlikely ( msgs [ 0 ] . addr ! = msgs [ i ] . addr ) ) {
dev_err ( priv - > dev , " Invalid addr in msg[%d] \n " ,
i ) ;
return - EINVAL ;
}
}
return 0 ;
}
/*
* Check if transfer is completed and status of operation .
* Returns 0 - transfer completed ( both ACK or NACK ) ,
* negative - transfer isn ' t finished .
*/
static int mlxcpld_i2c_check_status ( struct mlxcpld_i2c_priv * priv , int * status )
{
u8 val ;
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_STATUS_REG , & val , 1 ) ;
if ( val & MLXCPLD_LPCI2C_TRANS_END ) {
if ( val & MLXCPLD_LPCI2C_STATUS_NACK )
/*
* The slave is unable to accept the data . No such
* slave , command not understood , or unable to accept
* any more data .
*/
* status = MLXCPLD_LPCI2C_NACK_IND ;
else
* status = MLXCPLD_LPCI2C_ACK_IND ;
return 0 ;
}
* status = MLXCPLD_LPCI2C_NO_IND ;
return - EIO ;
}
static void mlxcpld_i2c_set_transf_data ( struct mlxcpld_i2c_priv * priv ,
struct i2c_msg * msgs , int num ,
u8 comm_len )
{
priv - > xfer . msg = msgs ;
priv - > xfer . msg_num = num ;
/*
* All upper layers currently are never use transfer with more than
* 2 messages . Actually , it ' s also not so relevant in Mellanox systems
* because of HW limitation . Max size of transfer is not more than 32
2018-03-27 17:01:22 +03:00
* or 68 bytes in the current x86 LPCI2C bridge .
2016-11-20 19:56:14 +03:00
*/
priv - > xfer . cmd = msgs [ num - 1 ] . flags & I2C_M_RD ;
if ( priv - > xfer . cmd = = I2C_M_RD & & comm_len ! = msgs [ 0 ] . len ) {
priv - > xfer . addr_width = msgs [ 0 ] . len ;
priv - > xfer . data_len = comm_len - priv - > xfer . addr_width ;
} else {
priv - > xfer . addr_width = 0 ;
priv - > xfer . data_len = comm_len ;
}
}
/* Reset CPLD LPCI2C block */
static void mlxcpld_i2c_reset ( struct mlxcpld_i2c_priv * priv )
{
u8 val ;
mutex_lock ( & priv - > lock ) ;
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_CTRL_REG , & val , 1 ) ;
val & = ~ MLXCPLD_LPCI2C_RST_SEL_MASK ;
mlxcpld_i2c_write_comm ( priv , MLXCPLD_LPCI2C_CTRL_REG , & val , 1 ) ;
mutex_unlock ( & priv - > lock ) ;
}
/* Make sure the CPLD is ready to start transmitting. */
static int mlxcpld_i2c_check_busy ( struct mlxcpld_i2c_priv * priv )
{
u8 val ;
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_STATUS_REG , & val , 1 ) ;
if ( val & MLXCPLD_LPCI2C_TRANS_END )
return 0 ;
return - EIO ;
}
static int mlxcpld_i2c_wait_for_free ( struct mlxcpld_i2c_priv * priv )
{
int timeout = 0 ;
do {
if ( ! mlxcpld_i2c_check_busy ( priv ) )
break ;
2021-08-23 17:45:04 +03:00
usleep_range ( priv - > polling_time / 2 , priv - > polling_time ) ;
timeout + = priv - > polling_time ;
2016-11-20 19:56:14 +03:00
} while ( timeout < = MLXCPLD_I2C_XFER_TO ) ;
if ( timeout > MLXCPLD_I2C_XFER_TO )
return - ETIMEDOUT ;
return 0 ;
}
/*
* Wait for master transfer to complete .
* It puts current process to sleep until we get interrupt or timeout expires .
* Returns the number of transferred or read bytes or error ( < 0 ) .
*/
static int mlxcpld_i2c_wait_for_tc ( struct mlxcpld_i2c_priv * priv )
{
int status , i , timeout = 0 ;
2018-03-27 17:01:23 +03:00
u8 datalen , val ;
2016-11-20 19:56:14 +03:00
do {
2021-08-23 17:45:04 +03:00
usleep_range ( priv - > polling_time / 2 , priv - > polling_time ) ;
2016-11-20 19:56:14 +03:00
if ( ! mlxcpld_i2c_check_status ( priv , & status ) )
break ;
2021-08-23 17:45:04 +03:00
timeout + = priv - > polling_time ;
2016-11-20 19:56:14 +03:00
} while ( status = = 0 & & timeout < MLXCPLD_I2C_XFER_TO ) ;
switch ( status ) {
case MLXCPLD_LPCI2C_NO_IND :
return - ETIMEDOUT ;
case MLXCPLD_LPCI2C_ACK_IND :
if ( priv - > xfer . cmd ! = I2C_M_RD )
return ( priv - > xfer . addr_width + priv - > xfer . data_len ) ;
if ( priv - > xfer . msg_num = = 1 )
i = 0 ;
else
i = 1 ;
if ( ! priv - > xfer . msg [ i ] . buf )
return - EINVAL ;
/*
* Actual read data len will be always the same as
* requested len . 0xff ( line pull - up ) will be returned
* if slave has no data to return . Thus don ' t read
2018-03-27 17:01:23 +03:00
* MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD . Only in case of
* SMBus block read transaction data len can be different ,
* check this case .
2016-11-20 19:56:14 +03:00
*/
2018-03-27 17:01:23 +03:00
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_NUM_ADDR_REG , & val ,
1 ) ;
if ( priv - > smbus_block & & ( val & MLXCPLD_I2C_SMBUS_BLK_BIT ) ) {
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_NUM_DAT_REG ,
& datalen , 1 ) ;
2020-06-28 14:52:44 +03:00
if ( unlikely ( datalen > I2C_SMBUS_BLOCK_MAX ) ) {
2018-03-27 17:01:23 +03:00
dev_err ( priv - > dev , " Incorrect smbus block read message len \n " ) ;
2020-06-28 14:52:44 +03:00
return - EPROTO ;
2018-03-27 17:01:23 +03:00
}
} else {
datalen = priv - > xfer . data_len ;
}
2016-11-20 19:56:14 +03:00
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_DATA_REG ,
priv - > xfer . msg [ i ] . buf , datalen ) ;
return datalen ;
case MLXCPLD_LPCI2C_NACK_IND :
return - ENXIO ;
default :
return - EINVAL ;
}
}
static void mlxcpld_i2c_xfer_msg ( struct mlxcpld_i2c_priv * priv )
{
int i , len = 0 ;
2018-03-27 17:01:23 +03:00
u8 cmd , val ;
2016-11-20 19:56:14 +03:00
mlxcpld_i2c_write_comm ( priv , MLXCPLD_LPCI2C_NUM_DAT_REG ,
& priv - > xfer . data_len , 1 ) ;
2018-03-27 17:01:23 +03:00
val = priv - > xfer . addr_width ;
/* Notify HW about SMBus block read transaction */
if ( priv - > smbus_block & & priv - > xfer . msg_num > = 2 & &
priv - > xfer . msg [ 1 ] . len = = 1 & &
( priv - > xfer . msg [ 1 ] . flags & I2C_M_RECV_LEN ) & &
( priv - > xfer . msg [ 1 ] . flags & I2C_M_RD ) )
val | = MLXCPLD_I2C_SMBUS_BLK_BIT ;
mlxcpld_i2c_write_comm ( priv , MLXCPLD_LPCI2C_NUM_ADDR_REG , & val , 1 ) ;
2016-11-20 19:56:14 +03:00
for ( i = 0 ; i < priv - > xfer . msg_num ; i + + ) {
if ( ( priv - > xfer . msg [ i ] . flags & I2C_M_RD ) ! = I2C_M_RD ) {
/* Don't write to CPLD buffer in read transaction */
mlxcpld_i2c_write_comm ( priv , MLXCPLD_LPCI2C_DATA_REG +
len , priv - > xfer . msg [ i ] . buf ,
priv - > xfer . msg [ i ] . len ) ;
len + = priv - > xfer . msg [ i ] . len ;
}
}
/*
* Set target slave address with command for master transfer .
* It should be latest executed function before CPLD transaction .
*/
cmd = ( priv - > xfer . msg [ 0 ] . addr < < 1 ) | priv - > xfer . cmd ;
mlxcpld_i2c_write_comm ( priv , MLXCPLD_LPCI2C_CMD_REG , & cmd , 1 ) ;
}
/*
* Generic lpc - i2c transfer .
* Returns the number of processed messages or error ( < 0 ) .
*/
static int mlxcpld_i2c_xfer ( struct i2c_adapter * adap , struct i2c_msg * msgs ,
int num )
{
struct mlxcpld_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
u8 comm_len = 0 ;
int i , err ;
err = mlxcpld_i2c_check_msg_params ( priv , msgs , num ) ;
if ( err ) {
dev_err ( priv - > dev , " Incorrect message \n " ) ;
return err ;
}
for ( i = 0 ; i < num ; + + i )
comm_len + = msgs [ i ] . len ;
/* Check bus state */
if ( mlxcpld_i2c_wait_for_free ( priv ) ) {
dev_err ( priv - > dev , " LPCI2C bridge is busy \n " ) ;
/*
* Usually it means something serious has happened .
* We can not have unfinished previous transfer
* so it doesn ' t make any sense to try to stop it .
* Probably we were not able to recover from the
* previous error .
* The only reasonable thing - is soft reset .
*/
mlxcpld_i2c_reset ( priv ) ;
if ( mlxcpld_i2c_check_busy ( priv ) ) {
dev_err ( priv - > dev , " LPCI2C bridge is busy after reset \n " ) ;
return - EIO ;
}
}
mlxcpld_i2c_set_transf_data ( priv , msgs , num , comm_len ) ;
mutex_lock ( & priv - > lock ) ;
/* Do real transfer. Can't fail */
mlxcpld_i2c_xfer_msg ( priv ) ;
/* Wait for transaction complete */
err = mlxcpld_i2c_wait_for_tc ( priv ) ;
mutex_unlock ( & priv - > lock ) ;
return err < 0 ? err : num ;
}
static u32 mlxcpld_i2c_func ( struct i2c_adapter * adap )
{
2018-03-27 17:01:24 +03:00
struct mlxcpld_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
if ( priv - > smbus_block )
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA ;
else
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_I2C_BLOCK ;
2016-11-20 19:56:14 +03:00
}
static const struct i2c_algorithm mlxcpld_i2c_algo = {
. master_xfer = mlxcpld_i2c_xfer ,
. functionality = mlxcpld_i2c_func
} ;
2017-08-21 15:12:04 +03:00
static const struct i2c_adapter_quirks mlxcpld_i2c_quirks = {
2016-11-20 19:56:14 +03:00
. flags = I2C_AQ_COMB_WRITE_THEN_READ ,
. max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN ,
. max_write_len = MLXCPLD_I2C_DATA_REG_SZ ,
. max_comb_1st_msg_len = 4 ,
} ;
2018-03-27 17:01:22 +03:00
static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = {
. flags = I2C_AQ_COMB_WRITE_THEN_READ ,
. max_read_len = MLXCPLD_I2C_DATA_REG_SZ * 2 - MLXCPLD_I2C_MAX_ADDR_LEN ,
. max_write_len = MLXCPLD_I2C_DATA_REG_SZ * 2 ,
. max_comb_1st_msg_len = 4 ,
} ;
2016-11-20 19:56:14 +03:00
static struct i2c_adapter mlxcpld_i2c_adapter = {
. owner = THIS_MODULE ,
. name = " i2c-mlxcpld " ,
. class = I2C_CLASS_HWMON | I2C_CLASS_SPD ,
. algo = & mlxcpld_i2c_algo ,
. quirks = & mlxcpld_i2c_quirks ,
. retries = MLXCPLD_I2C_RETR_NUM ,
. nr = MLXCPLD_I2C_BUS_NUM ,
} ;
2021-01-06 02:33:47 +03:00
static int
mlxcpld_i2c_set_frequency ( struct mlxcpld_i2c_priv * priv ,
struct mlxreg_core_hotplug_platform_data * pdata )
{
struct mlxreg_core_item * item = pdata - > items ;
struct mlxreg_core_data * data ;
u32 regval ;
u8 freq ;
int err ;
if ( ! item )
return 0 ;
/* Read frequency setting. */
data = item - > data ;
err = regmap_read ( pdata - > regmap , data - > reg , & regval ) ;
if ( err )
return err ;
/* Set frequency only if it is not 100KHz, which is default. */
2021-08-23 17:45:01 +03:00
switch ( ( regval & data - > mask ) > > data - > bit ) {
2021-01-06 02:33:47 +03:00
case MLXCPLD_I2C_FREQ_1000KHZ :
freq = MLXCPLD_I2C_FREQ_1000KHZ_SET ;
2021-08-23 17:45:04 +03:00
priv - > polling_time / = 4 ;
2021-01-06 02:33:47 +03:00
break ;
case MLXCPLD_I2C_FREQ_400KHZ :
freq = MLXCPLD_I2C_FREQ_400KHZ_SET ;
2021-08-23 17:45:04 +03:00
priv - > polling_time / = 4 ;
2021-01-06 02:33:47 +03:00
break ;
default :
return 0 ;
}
mlxcpld_i2c_write_comm ( priv , MLXCPLD_LPCI2C_HALF_CYC_REG , & freq , 1 ) ;
return 0 ;
}
2016-11-20 19:56:14 +03:00
static int mlxcpld_i2c_probe ( struct platform_device * pdev )
{
2021-01-06 02:33:47 +03:00
struct mlxreg_core_hotplug_platform_data * pdata ;
2016-11-20 19:56:14 +03:00
struct mlxcpld_i2c_priv * priv ;
int err ;
2018-03-27 17:01:22 +03:00
u8 val ;
2016-11-20 19:56:14 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
mutex_init ( & priv - > lock ) ;
platform_set_drvdata ( pdev , priv ) ;
priv - > dev = & pdev - > dev ;
2019-05-16 20:15:41 +03:00
priv - > base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR ;
2021-08-23 17:45:04 +03:00
priv - > polling_time = MLXCPLD_I2C_POLL_TIME ;
2016-11-20 19:56:14 +03:00
2021-01-06 02:33:47 +03:00
/* Set I2C bus frequency if platform data provides this info. */
pdata = dev_get_platdata ( & pdev - > dev ) ;
if ( pdata ) {
err = mlxcpld_i2c_set_frequency ( priv , pdata ) ;
if ( err )
goto mlxcpld_i2_probe_failed ;
}
2016-11-20 19:56:14 +03:00
/* Register with i2c layer */
mlxcpld_i2c_adapter . timeout = usecs_to_jiffies ( MLXCPLD_I2C_XFER_TO ) ;
2018-03-27 17:01:22 +03:00
/* Read capability register */
mlxcpld_i2c_read_comm ( priv , MLXCPLD_LPCI2C_CPBLTY_REG , & val , 1 ) ;
/* Check support for extended transaction length */
if ( ( val & MLXCPLD_I2C_DATA_SZ_MASK ) = = MLXCPLD_I2C_DATA_SZ_BIT )
mlxcpld_i2c_adapter . quirks = & mlxcpld_i2c_quirks_ext ;
2018-03-27 17:01:23 +03:00
/* Check support for smbus block transaction */
if ( val & MLXCPLD_I2C_SMBUS_BLK_BIT )
priv - > smbus_block = true ;
2018-03-27 17:01:25 +03:00
if ( pdev - > id > = - 1 )
mlxcpld_i2c_adapter . nr = pdev - > id ;
2016-11-20 19:56:14 +03:00
priv - > adap = mlxcpld_i2c_adapter ;
priv - > adap . dev . parent = & pdev - > dev ;
i2c_set_adapdata ( & priv - > adap , priv ) ;
err = i2c_add_numbered_adapter ( & priv - > adap ) ;
if ( err )
2021-01-06 02:33:47 +03:00
goto mlxcpld_i2_probe_failed ;
2016-11-20 19:56:14 +03:00
2021-01-06 02:33:47 +03:00
return 0 ;
mlxcpld_i2_probe_failed :
mutex_destroy ( & priv - > lock ) ;
2016-11-20 19:56:14 +03:00
return err ;
}
static int mlxcpld_i2c_remove ( struct platform_device * pdev )
{
struct mlxcpld_i2c_priv * priv = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & priv - > adap ) ;
mutex_destroy ( & priv - > lock ) ;
return 0 ;
}
static struct platform_driver mlxcpld_i2c_driver = {
. probe = mlxcpld_i2c_probe ,
. remove = mlxcpld_i2c_remove ,
. driver = {
. name = MLXCPLD_I2C_DEVICE_NAME ,
} ,
} ;
module_platform_driver ( mlxcpld_i2c_driver ) ;
MODULE_AUTHOR ( " Michael Shych <michaels@mellanox.com> " ) ;
MODULE_DESCRIPTION ( " Mellanox I2C-CPLD controller driver " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_ALIAS ( " platform:i2c-mlxcpld " ) ;