2019-06-04 10:10:54 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2010-03-13 17:10:24 -03:00
/*
* ngene - i2c . c : nGene PCIe bridge driver i2c functions
*
* Copyright ( C ) 2005 - 2007 Micronas
*
* Copyright ( C ) 2008 - 2009 Ralph Metzler < rjkm @ metzlerbros . de >
* Modifications for new nGene firmware ,
* support for EEPROM - copying ,
* support for new dual DVB - S2 card prototype
*/
/* FIXME - some of these can probably be removed */
# include <linux/module.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/io.h>
# include <asm/div64.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include <linux/timer.h>
# include <linux/byteorder/generic.h>
# include <linux/firmware.h>
# include <linux/vmalloc.h>
# include "ngene.h"
/* Firmware command for i2c operations */
static int ngene_command_i2c_read ( struct ngene * dev , u8 adr ,
u8 * out , u8 outlen , u8 * in , u8 inlen , int flag )
{
struct ngene_command com ;
com . cmd . hdr . Opcode = CMD_I2C_READ ;
com . cmd . hdr . Length = outlen + 3 ;
com . cmd . I2CRead . Device = adr < < 1 ;
memcpy ( com . cmd . I2CRead . Data , out , outlen ) ;
com . cmd . I2CRead . Data [ outlen ] = inlen ;
com . cmd . I2CRead . Data [ outlen + 1 ] = 0 ;
com . in_len = outlen + 3 ;
com . out_len = inlen + 1 ;
if ( ngene_command ( dev , & com ) < 0 )
return - EIO ;
if ( ( com . cmd . raw8 [ 0 ] > > 1 ) ! = adr )
return - EIO ;
if ( flag )
memcpy ( in , com . cmd . raw8 , inlen + 1 ) ;
else
memcpy ( in , com . cmd . raw8 + 1 , inlen ) ;
return 0 ;
}
static int ngene_command_i2c_write ( struct ngene * dev , u8 adr ,
u8 * out , u8 outlen )
{
struct ngene_command com ;
com . cmd . hdr . Opcode = CMD_I2C_WRITE ;
com . cmd . hdr . Length = outlen + 1 ;
com . cmd . I2CRead . Device = adr < < 1 ;
memcpy ( com . cmd . I2CRead . Data , out , outlen ) ;
com . in_len = outlen + 1 ;
com . out_len = 1 ;
if ( ngene_command ( dev , & com ) < 0 )
return - EIO ;
if ( com . cmd . raw8 [ 0 ] = = 1 )
return - EIO ;
return 0 ;
}
static void ngene_i2c_set_bus ( struct ngene * dev , int bus )
{
if ( ! ( dev - > card_info - > i2c_access & 2 ) )
return ;
if ( dev - > i2c_current_bus = = bus )
return ;
switch ( bus ) {
case 0 :
ngene_command_gpio_set ( dev , 3 , 0 ) ;
ngene_command_gpio_set ( dev , 2 , 1 ) ;
break ;
case 1 :
ngene_command_gpio_set ( dev , 2 , 0 ) ;
ngene_command_gpio_set ( dev , 3 , 1 ) ;
break ;
}
dev - > i2c_current_bus = bus ;
}
static int ngene_i2c_master_xfer ( struct i2c_adapter * adapter ,
struct i2c_msg msg [ ] , int num )
{
struct ngene_channel * chan =
( struct ngene_channel * ) i2c_get_adapdata ( adapter ) ;
struct ngene * dev = chan - > dev ;
2017-06-13 05:58:50 -03:00
mutex_lock ( & dev - > i2c_switch_mutex ) ;
2010-03-13 17:10:24 -03:00
ngene_i2c_set_bus ( dev , chan - > number ) ;
if ( num = = 2 & & msg [ 1 ] . flags & I2C_M_RD & & ! ( msg [ 0 ] . flags & I2C_M_RD ) )
if ( ! ngene_command_i2c_read ( dev , msg [ 0 ] . addr ,
msg [ 0 ] . buf , msg [ 0 ] . len ,
msg [ 1 ] . buf , msg [ 1 ] . len , 0 ) )
goto done ;
if ( num = = 1 & & ! ( msg [ 0 ] . flags & I2C_M_RD ) )
if ( ! ngene_command_i2c_write ( dev , msg [ 0 ] . addr ,
msg [ 0 ] . buf , msg [ 0 ] . len ) )
goto done ;
if ( num = = 1 & & ( msg [ 0 ] . flags & I2C_M_RD ) )
if ( ! ngene_command_i2c_read ( dev , msg [ 0 ] . addr , NULL , 0 ,
msg [ 0 ] . buf , msg [ 0 ] . len , 0 ) )
goto done ;
2017-06-13 05:58:50 -03:00
mutex_unlock ( & dev - > i2c_switch_mutex ) ;
2010-03-13 17:10:24 -03:00
return - EIO ;
done :
2017-06-13 05:58:50 -03:00
mutex_unlock ( & dev - > i2c_switch_mutex ) ;
2010-03-13 17:10:24 -03:00
return num ;
}
static u32 ngene_i2c_functionality ( struct i2c_adapter * adap )
{
2018-03-06 11:39:11 -05:00
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL ;
2010-03-13 17:10:24 -03:00
}
2017-07-09 21:12:37 -04:00
static const struct i2c_algorithm ngene_i2c_algo = {
2010-03-13 17:10:24 -03:00
. master_xfer = ngene_i2c_master_xfer ,
. functionality = ngene_i2c_functionality ,
} ;
int ngene_i2c_init ( struct ngene * dev , int dev_nr )
{
struct i2c_adapter * adap = & ( dev - > channel [ dev_nr ] . i2c_adapter ) ;
i2c_set_adapdata ( adap , & ( dev - > channel [ dev_nr ] ) ) ;
2018-09-10 16:20:42 -04:00
strscpy ( adap - > name , " nGene " , sizeof ( adap - > name ) ) ;
2010-03-13 17:10:24 -03:00
adap - > algo = & ngene_i2c_algo ;
adap - > algo_data = ( void * ) & ( dev - > channel [ dev_nr ] ) ;
adap - > dev . parent = & dev - > pci_dev - > dev ;
return i2c_add_adapter ( adap ) ;
}