2012-11-28 21:22:24 +04:00
/*
* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
* Copyright ( C ) 2009 , 2010 STMicroelectronics
*
* 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 program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* STMicroelectronics version 1.2 .0 , Copyright ( C ) 2010
* STMicroelectronics comes with ABSOLUTELY NO WARRANTY .
* This is free software , and you are welcome to redistribute it
* under certain conditions .
*
* @ Author : Christophe RICARD tpmsupport @ st . com
*
* @ File : tpm_stm_st33_i2c . c
*
* @ Synopsis :
* 09 / 15 / 2010 : First shot driver tpm_tis driver for
lpc is used as model .
*/
2012-12-06 02:52:43 +04:00
# include <linux/pci.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/i2c.h>
# include <linux/fs.h>
# include <linux/miscdevice.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/wait.h>
# include <linux/string.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/sysfs.h>
# include <linux/gpio.h>
# include <linux/sched.h>
# include <linux/uaccess.h>
# include <linux/io.h>
# include <linux/slab.h>
# include "tpm.h"
2013-01-28 17:52:44 +04:00
# include "tpm_i2c_stm_st33.h"
2012-11-28 21:22:24 +04:00
enum stm33zp24_access {
TPM_ACCESS_VALID = 0x80 ,
TPM_ACCESS_ACTIVE_LOCALITY = 0x20 ,
TPM_ACCESS_REQUEST_PENDING = 0x04 ,
TPM_ACCESS_REQUEST_USE = 0x02 ,
} ;
enum stm33zp24_status {
TPM_STS_VALID = 0x80 ,
TPM_STS_COMMAND_READY = 0x40 ,
TPM_STS_GO = 0x20 ,
TPM_STS_DATA_AVAIL = 0x10 ,
TPM_STS_DATA_EXPECT = 0x08 ,
} ;
enum stm33zp24_int_flags {
TPM_GLOBAL_INT_ENABLE = 0x80 ,
TPM_INTF_CMD_READY_INT = 0x080 ,
TPM_INTF_FIFO_AVALAIBLE_INT = 0x040 ,
TPM_INTF_WAKE_UP_READY_INT = 0x020 ,
TPM_INTF_LOCALITY_CHANGE_INT = 0x004 ,
TPM_INTF_STS_VALID_INT = 0x002 ,
TPM_INTF_DATA_AVAIL_INT = 0x001 ,
} ;
enum tis_defaults {
TIS_SHORT_TIMEOUT = 750 ,
TIS_LONG_TIMEOUT = 2000 ,
} ;
/*
* write8_reg
* Send byte to the TIS register according to the ST33ZP24 I2C protocol .
* @ param : tpm_register , the tpm tis register where the data should be written
* @ param : tpm_data , the tpm_data to write inside the tpm_register
* @ param : tpm_size , The length of the data
* @ return : Returns negative errno , or else the number of bytes written .
*/
static int write8_reg ( struct i2c_client * client , u8 tpm_register ,
u8 * tpm_data , u16 tpm_size )
{
struct st33zp24_platform_data * pin_infos ;
pin_infos = client - > dev . platform_data ;
2013-01-30 01:01:59 +04:00
pin_infos - > tpm_i2c_buffer [ 0 ] [ 0 ] = tpm_register ;
memcpy ( & pin_infos - > tpm_i2c_buffer [ 0 ] [ 1 ] , tpm_data , tpm_size ) ;
2013-01-30 01:02:02 +04:00
return i2c_master_send ( client , pin_infos - > tpm_i2c_buffer [ 0 ] ,
2012-11-28 21:22:24 +04:00
tpm_size + 1 ) ;
} /* write8_reg() */
/*
* read8_reg
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol .
* @ param : tpm_register , the tpm tis register where the data should be read
* @ param : tpm_data , the TPM response
* @ param : tpm_size , tpm TPM response size to read .
* @ return : number of byte read successfully : should be one if success .
*/
static int read8_reg ( struct i2c_client * client , u8 tpm_register ,
u8 * tpm_data , int tpm_size )
{
u8 status = 0 ;
u8 data ;
data = TPM_DUMMY_BYTE ;
status = write8_reg ( client , tpm_register , & data , 1 ) ;
if ( status = = 2 )
status = i2c_master_recv ( client , tpm_data , tpm_size ) ;
return status ;
} /* read8_reg() */
/*
* I2C_WRITE_DATA
* Send byte to the TIS register according to the ST33ZP24 I2C protocol .
* @ param : client , the chip description
* @ param : tpm_register , the tpm tis register where the data should be written
* @ param : tpm_data , the tpm_data to write inside the tpm_register
* @ param : tpm_size , The length of the data
* @ return : number of byte written successfully : should be one if success .
*/
# define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \
( write8_reg ( client , tpm_register | \
TPM_WRITE_DIRECTION , tpm_data , tpm_size ) )
/*
* I2C_READ_DATA
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol .
* @ param : tpm , the chip description
* @ param : tpm_register , the tpm tis register where the data should be read
* @ param : tpm_data , the TPM response
* @ param : tpm_size , tpm TPM response size to read .
* @ return : number of byte read successfully : should be one if success .
*/
# define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \
( read8_reg ( client , tpm_register , tpm_data , tpm_size ) )
/*
* clear_interruption
* clear the TPM interrupt register .
* @ param : tpm , the chip description
*/
static void clear_interruption ( struct i2c_client * client )
{
u8 interrupt ;
I2C_READ_DATA ( client , TPM_INT_STATUS , & interrupt , 1 ) ;
I2C_WRITE_DATA ( client , TPM_INT_STATUS , & interrupt , 1 ) ;
I2C_READ_DATA ( client , TPM_INT_STATUS , & interrupt , 1 ) ;
} /* clear_interruption() */
/*
* _wait_for_interrupt_serirq_timeout
* @ param : tpm , the chip description
* @ param : timeout , the timeout of the interrupt
* @ return : the status of the interruption .
*/
static long _wait_for_interrupt_serirq_timeout ( struct tpm_chip * chip ,
unsigned long timeout )
{
long status ;
struct i2c_client * client ;
struct st33zp24_platform_data * pin_infos ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
pin_infos = client - > dev . platform_data ;
status = wait_for_completion_interruptible_timeout (
& pin_infos - > irq_detection ,
timeout ) ;
if ( status > 0 )
enable_irq ( gpio_to_irq ( pin_infos - > io_serirq ) ) ;
gpio_direction_input ( pin_infos - > io_serirq ) ;
return status ;
} /* wait_for_interrupt_serirq_timeout() */
2012-12-06 02:52:43 +04:00
static int wait_for_serirq_timeout ( struct tpm_chip * chip , bool condition ,
2012-11-28 21:22:24 +04:00
unsigned long timeout )
{
int status = 2 ;
struct i2c_client * client ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
status = _wait_for_interrupt_serirq_timeout ( chip , timeout ) ;
if ( ! status ) {
status = - EBUSY ;
2013-03-01 02:15:48 +04:00
} else {
2012-11-28 21:22:24 +04:00
clear_interruption ( client ) ;
if ( condition )
status = 1 ;
}
return status ;
}
/*
* tpm_stm_i2c_cancel , cancel is not implemented .
* @ param : chip , the tpm_chip description as specified in driver / char / tpm / tpm . h
*/
static void tpm_stm_i2c_cancel ( struct tpm_chip * chip )
{
struct i2c_client * client ;
u8 data ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
data = TPM_STS_COMMAND_READY ;
I2C_WRITE_DATA ( client , TPM_STS , & data , 1 ) ;
if ( chip - > vendor . irq )
wait_for_serirq_timeout ( chip , 1 , chip - > vendor . timeout_a ) ;
} /* tpm_stm_i2c_cancel() */
/*
* tpm_stm_spi_status return the TPM_STS register
* @ param : chip , the tpm chip description
* @ return : the TPM_STS register value .
*/
static u8 tpm_stm_i2c_status ( struct tpm_chip * chip )
{
struct i2c_client * client ;
u8 data ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
I2C_READ_DATA ( client , TPM_STS , & data , 1 ) ;
return data ;
} /* tpm_stm_i2c_status() */
/*
* check_locality if the locality is active
* @ param : chip , the tpm chip description
* @ return : the active locality or - EACCESS .
*/
static int check_locality ( struct tpm_chip * chip )
{
struct i2c_client * client ;
u8 data ;
u8 status ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
status = I2C_READ_DATA ( client , TPM_ACCESS , & data , 1 ) ;
if ( status & & ( data &
( TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID ) ) = =
( TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID ) )
return chip - > vendor . locality ;
return - EACCES ;
} /* check_locality() */
/*
* request_locality request the TPM locality
* @ param : chip , the chip description
* @ return : the active locality or EACCESS .
*/
static int request_locality ( struct tpm_chip * chip )
{
unsigned long stop ;
long rc ;
struct i2c_client * client ;
u8 data ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
if ( check_locality ( chip ) = = chip - > vendor . locality )
return chip - > vendor . locality ;
data = TPM_ACCESS_REQUEST_USE ;
rc = I2C_WRITE_DATA ( client , TPM_ACCESS , & data , 1 ) ;
if ( rc < 0 )
goto end ;
if ( chip - > vendor . irq ) {
rc = wait_for_serirq_timeout ( chip , ( check_locality
( chip ) > = 0 ) ,
chip - > vendor . timeout_a ) ;
if ( rc > 0 )
return chip - > vendor . locality ;
2013-03-01 02:15:48 +04:00
} else {
2012-11-28 21:22:24 +04:00
stop = jiffies + chip - > vendor . timeout_a ;
do {
if ( check_locality ( chip ) > = 0 )
return chip - > vendor . locality ;
msleep ( TPM_TIMEOUT ) ;
} while ( time_before ( jiffies , stop ) ) ;
}
rc = - EACCES ;
end :
return rc ;
} /* request_locality() */
/*
* release_locality release the active locality
* @ param : chip , the tpm chip description .
*/
static void release_locality ( struct tpm_chip * chip )
{
struct i2c_client * client ;
u8 data ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
data = TPM_ACCESS_ACTIVE_LOCALITY ;
I2C_WRITE_DATA ( client , TPM_ACCESS , & data , 1 ) ;
}
/*
* get_burstcount return the burstcount address 0x19 0x1A
* @ param : chip , the chip description
* return : the burstcount .
*/
static int get_burstcount ( struct tpm_chip * chip )
{
unsigned long stop ;
int burstcnt , status ;
u8 tpm_reg , temp ;
2013-03-01 02:15:48 +04:00
struct i2c_client * client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
stop = jiffies + chip - > vendor . timeout_d ;
do {
tpm_reg = TPM_STS + 1 ;
status = I2C_READ_DATA ( client , tpm_reg , & temp , 1 ) ;
if ( status < 0 )
goto end ;
tpm_reg = tpm_reg + 1 ;
burstcnt = temp ;
status = I2C_READ_DATA ( client , tpm_reg , & temp , 1 ) ;
if ( status < 0 )
goto end ;
burstcnt | = temp < < 8 ;
if ( burstcnt )
return burstcnt ;
msleep ( TPM_TIMEOUT ) ;
} while ( time_before ( jiffies , stop ) ) ;
end :
return - EBUSY ;
} /* get_burstcount() */
/*
* wait_for_stat wait for a TPM_STS value
* @ param : chip , the tpm chip description
* @ param : mask , the value mask to wait
* @ param : timeout , the timeout
* @ param : queue , the wait queue .
* @ return : the tpm status , 0 if success , - ETIME if timeout is reached .
*/
static int wait_for_stat ( struct tpm_chip * chip , u8 mask , unsigned long timeout ,
wait_queue_head_t * queue )
{
unsigned long stop ;
long rc ;
u8 status ;
if ( chip - > vendor . irq ) {
rc = wait_for_serirq_timeout ( chip , ( ( tpm_stm_i2c_status
( chip ) & mask ) = =
mask ) , timeout ) ;
if ( rc > 0 )
return 0 ;
2013-03-01 02:15:48 +04:00
} else {
2012-11-28 21:22:24 +04:00
stop = jiffies + timeout ;
do {
msleep ( TPM_TIMEOUT ) ;
status = tpm_stm_i2c_status ( chip ) ;
if ( ( status & mask ) = = mask )
return 0 ;
} while ( time_before ( jiffies , stop ) ) ;
}
return - ETIME ;
} /* wait_for_stat() */
/*
* recv_data receive data
* @ param : chip , the tpm chip description
* @ param : buf , the buffer where the data are received
* @ param : count , the number of data to receive
* @ return : the number of bytes read from TPM FIFO .
*/
static int recv_data ( struct tpm_chip * chip , u8 * buf , size_t count )
{
int size = 0 , burstcnt , len ;
struct i2c_client * client ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
while ( size < count & &
wait_for_stat ( chip ,
TPM_STS_DATA_AVAIL | TPM_STS_VALID ,
chip - > vendor . timeout_c ,
& chip - > vendor . read_queue )
= = 0 ) {
burstcnt = get_burstcount ( chip ) ;
2013-10-30 03:54:20 +04:00
if ( burstcnt < 0 )
return burstcnt ;
2012-11-28 21:22:24 +04:00
len = min_t ( int , burstcnt , count - size ) ;
I2C_READ_DATA ( client , TPM_DATA_FIFO , buf + size , len ) ;
size + = len ;
}
return size ;
}
/*
* tpm_ioserirq_handler the serirq irq handler
* @ param : irq , the tpm chip description
* @ param : dev_id , the description of the chip
* @ return : the status of the handler .
*/
static irqreturn_t tpm_ioserirq_handler ( int irq , void * dev_id )
{
struct tpm_chip * chip = dev_id ;
struct i2c_client * client ;
struct st33zp24_platform_data * pin_infos ;
disable_irq_nosync ( irq ) ;
2013-03-01 02:15:48 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
pin_infos = client - > dev . platform_data ;
complete ( & pin_infos - > irq_detection ) ;
return IRQ_HANDLED ;
} /* tpm_ioserirq_handler() */
/*
* tpm_stm_i2c_send send TPM commands through the I2C bus .
*
* @ param : chip , the tpm_chip description as specified in driver / char / tpm / tpm . h
* @ param : buf , the buffer to send .
* @ param : count , the number of bytes to send .
* @ return : In case of success the number of bytes sent .
* In other case , a < 0 value describing the issue .
*/
static int tpm_stm_i2c_send ( struct tpm_chip * chip , unsigned char * buf ,
size_t len )
{
2013-10-30 03:54:20 +04:00
u32 status , i , size ;
int burstcnt = 0 ;
2012-12-06 02:52:43 +04:00
int ret ;
2012-11-28 21:22:24 +04:00
u8 data ;
struct i2c_client * client ;
if ( chip = = NULL )
return - EBUSY ;
if ( len < TPM_HEADER_SIZE )
return - EBUSY ;
2012-12-06 02:52:43 +04:00
client = ( struct i2c_client * ) TPM_VPRIV ( chip ) ;
2012-11-28 21:22:24 +04:00
client - > flags = 0 ;
ret = request_locality ( chip ) ;
if ( ret < 0 )
return ret ;
status = tpm_stm_i2c_status ( chip ) ;
if ( ( status & TPM_STS_COMMAND_READY ) = = 0 ) {
tpm_stm_i2c_cancel ( chip ) ;
if ( wait_for_stat
( chip , TPM_STS_COMMAND_READY , chip - > vendor . timeout_b ,
& chip - > vendor . int_queue ) < 0 ) {
ret = - ETIME ;
goto out_err ;
}
}
2013-03-01 02:15:48 +04:00
for ( i = 0 ; i < len - 1 ; ) {
2012-11-28 21:22:24 +04:00
burstcnt = get_burstcount ( chip ) ;
2013-10-30 03:54:20 +04:00
if ( burstcnt < 0 )
return burstcnt ;
2012-11-28 21:22:24 +04:00
size = min_t ( int , len - i - 1 , burstcnt ) ;
ret = I2C_WRITE_DATA ( client , TPM_DATA_FIFO , buf , size ) ;
if ( ret < 0 )
goto out_err ;
i + = size ;
}
status = tpm_stm_i2c_status ( chip ) ;
if ( ( status & TPM_STS_DATA_EXPECT ) = = 0 ) {
ret = - EIO ;
goto out_err ;
}
ret = I2C_WRITE_DATA ( client , TPM_DATA_FIFO , buf + len - 1 , 1 ) ;
if ( ret < 0 )
goto out_err ;
status = tpm_stm_i2c_status ( chip ) ;
if ( ( status & TPM_STS_DATA_EXPECT ) ! = 0 ) {
ret = - EIO ;
goto out_err ;
}
data = TPM_STS_GO ;
I2C_WRITE_DATA ( client , TPM_STS , & data , 1 ) ;
return len ;
out_err :
tpm_stm_i2c_cancel ( chip ) ;
release_locality ( chip ) ;
return ret ;
}
/*
* tpm_stm_i2c_recv received TPM response through the I2C bus .
* @ param : chip , the tpm_chip description as specified in driver / char / tpm / tpm . h .
* @ param : buf , the buffer to store datas .
* @ param : count , the number of bytes to send .
* @ return : In case of success the number of bytes received .
* In other case , a < 0 value describing the issue .
*/
static int tpm_stm_i2c_recv ( struct tpm_chip * chip , unsigned char * buf ,
size_t count )
{
int size = 0 ;
int expected ;
if ( chip = = NULL )
return - EBUSY ;
if ( count < TPM_HEADER_SIZE ) {
size = - EIO ;
goto out ;
}
size = recv_data ( chip , buf , TPM_HEADER_SIZE ) ;
if ( size < TPM_HEADER_SIZE ) {
dev_err ( chip - > dev , " Unable to read header \n " ) ;
goto out ;
}
2013-03-01 02:15:48 +04:00
expected = be32_to_cpu ( * ( __be32 * ) ( buf + 2 ) ) ;
2012-11-28 21:22:24 +04:00
if ( expected > count ) {
size = - EIO ;
goto out ;
}
size + = recv_data ( chip , & buf [ TPM_HEADER_SIZE ] ,
expected - TPM_HEADER_SIZE ) ;
if ( size < expected ) {
dev_err ( chip - > dev , " Unable to read remainder of result \n " ) ;
size = - ETIME ;
goto out ;
}
out :
2013-11-27 00:30:44 +04:00
chip - > ops - > cancel ( chip ) ;
2012-11-28 21:22:24 +04:00
release_locality ( chip ) ;
return size ;
}
2013-01-22 23:52:35 +04:00
static bool tpm_st33_i2c_req_canceled ( struct tpm_chip * chip , u8 status )
{
2013-03-01 02:15:48 +04:00
return ( status = = TPM_STS_COMMAND_READY ) ;
2013-01-22 23:52:35 +04:00
}
2013-11-27 00:30:43 +04:00
static const struct tpm_class_ops st_i2c_tpm = {
2012-11-28 21:22:24 +04:00
. send = tpm_stm_i2c_send ,
. recv = tpm_stm_i2c_recv ,
. cancel = tpm_stm_i2c_cancel ,
. status = tpm_stm_i2c_status ,
. req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID ,
. req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID ,
2013-01-22 23:52:35 +04:00
. req_canceled = tpm_st33_i2c_req_canceled ,
2012-11-28 21:22:24 +04:00
} ;
2013-03-01 02:15:48 +04:00
static int interrupts ;
2012-11-28 21:22:24 +04:00
module_param ( interrupts , int , 0444 ) ;
MODULE_PARM_DESC ( interrupts , " Enable interrupts " ) ;
static int power_mgt = 1 ;
module_param ( power_mgt , int , 0444 ) ;
MODULE_PARM_DESC ( power_mgt , " Power Management " ) ;
/*
* tpm_st33_i2c_probe initialize the TPM device
* @ param : client , the i2c_client drescription ( TPM I2C description ) .
* @ param : id , the i2c_device_id struct .
* @ return : 0 in case of success .
* - 1 in other case .
*/
static int
tpm_st33_i2c_probe ( struct i2c_client * client , const struct i2c_device_id * id )
{
2012-12-06 02:52:43 +04:00
int err ;
2012-11-28 21:22:24 +04:00
u8 intmask ;
struct tpm_chip * chip ;
struct st33zp24_platform_data * platform_data ;
if ( client = = NULL ) {
2013-01-19 03:42:25 +04:00
pr_info ( " %s: i2c client is NULL. Device not accessible. \n " ,
__func__ ) ;
2012-11-28 21:22:24 +04:00
err = - ENODEV ;
goto end ;
}
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_info ( & client - > dev , " client not i2c capable \n " ) ;
err = - ENODEV ;
goto end ;
}
chip = tpm_register_hardware ( & client - > dev , & st_i2c_tpm ) ;
if ( ! chip ) {
dev_info ( & client - > dev , " fail chip \n " ) ;
err = - ENODEV ;
goto end ;
}
platform_data = client - > dev . platform_data ;
2013-01-19 03:42:25 +04:00
if ( ! platform_data ) {
dev_info ( & client - > dev , " chip not available \n " ) ;
err = - ENODEV ;
goto _tpm_clean_answer ;
}
2012-11-28 21:22:24 +04:00
platform_data - > tpm_i2c_buffer [ 0 ] =
kmalloc ( TPM_BUFSIZE * sizeof ( u8 ) , GFP_KERNEL ) ;
if ( platform_data - > tpm_i2c_buffer [ 0 ] = = NULL ) {
err = - ENOMEM ;
goto _tpm_clean_answer ;
}
platform_data - > tpm_i2c_buffer [ 1 ] =
kmalloc ( TPM_BUFSIZE * sizeof ( u8 ) , GFP_KERNEL ) ;
if ( platform_data - > tpm_i2c_buffer [ 1 ] = = NULL ) {
err = - ENOMEM ;
2012-12-06 02:52:43 +04:00
goto _tpm_clean_response1 ;
2012-11-28 21:22:24 +04:00
}
2012-12-06 02:52:43 +04:00
TPM_VPRIV ( chip ) = client ;
2012-11-28 21:22:24 +04:00
chip - > vendor . timeout_a = msecs_to_jiffies ( TIS_SHORT_TIMEOUT ) ;
chip - > vendor . timeout_b = msecs_to_jiffies ( TIS_LONG_TIMEOUT ) ;
chip - > vendor . timeout_c = msecs_to_jiffies ( TIS_SHORT_TIMEOUT ) ;
chip - > vendor . timeout_d = msecs_to_jiffies ( TIS_SHORT_TIMEOUT ) ;
chip - > vendor . locality = LOCALITY0 ;
if ( power_mgt ) {
err = gpio_request ( platform_data - > io_lpcpd , " TPM IO_LPCPD " ) ;
if ( err )
goto _gpio_init1 ;
gpio_set_value ( platform_data - > io_lpcpd , 1 ) ;
}
if ( interrupts ) {
init_completion ( & platform_data - > irq_detection ) ;
if ( request_locality ( chip ) ! = LOCALITY0 ) {
err = - ENODEV ;
2012-12-06 02:52:43 +04:00
goto _tpm_clean_response2 ;
2012-11-28 21:22:24 +04:00
}
err = gpio_request ( platform_data - > io_serirq , " TPM IO_SERIRQ " ) ;
if ( err )
goto _gpio_init2 ;
clear_interruption ( client ) ;
err = request_irq ( gpio_to_irq ( platform_data - > io_serirq ) ,
& tpm_ioserirq_handler ,
IRQF_TRIGGER_HIGH ,
" TPM SERIRQ management " , chip ) ;
if ( err < 0 ) {
dev_err ( chip - > dev , " TPM SERIRQ signals %d not available \n " ,
2013-03-01 02:15:48 +04:00
gpio_to_irq ( platform_data - > io_serirq ) ) ;
2012-11-28 21:22:24 +04:00
goto _irq_set ;
}
err = I2C_READ_DATA ( client , TPM_INT_ENABLE , & intmask , 1 ) ;
if ( err < 0 )
goto _irq_set ;
intmask | = TPM_INTF_CMD_READY_INT
| TPM_INTF_FIFO_AVALAIBLE_INT
| TPM_INTF_WAKE_UP_READY_INT
| TPM_INTF_LOCALITY_CHANGE_INT
| TPM_INTF_STS_VALID_INT
| TPM_INTF_DATA_AVAIL_INT ;
err = I2C_WRITE_DATA ( client , TPM_INT_ENABLE , & intmask , 1 ) ;
if ( err < 0 )
goto _irq_set ;
intmask = TPM_GLOBAL_INT_ENABLE ;
err = I2C_WRITE_DATA ( client , ( TPM_INT_ENABLE + 3 ) , & intmask , 1 ) ;
if ( err < 0 )
goto _irq_set ;
err = I2C_READ_DATA ( client , TPM_INT_STATUS , & intmask , 1 ) ;
if ( err < 0 )
goto _irq_set ;
chip - > vendor . irq = interrupts ;
tpm_gen_interrupt ( chip ) ;
}
tpm_get_timeouts ( chip ) ;
dev_info ( chip - > dev , " TPM I2C Initialized \n " ) ;
return 0 ;
_irq_set :
2013-03-01 02:15:48 +04:00
free_irq ( gpio_to_irq ( platform_data - > io_serirq ) , ( void * ) chip ) ;
2012-11-28 21:22:24 +04:00
_gpio_init2 :
2012-12-06 02:52:43 +04:00
if ( interrupts )
2012-11-28 21:22:24 +04:00
gpio_free ( platform_data - > io_serirq ) ;
_gpio_init1 :
2012-12-06 02:52:43 +04:00
if ( power_mgt )
2012-11-28 21:22:24 +04:00
gpio_free ( platform_data - > io_lpcpd ) ;
2012-12-06 02:52:43 +04:00
_tpm_clean_response2 :
kzfree ( platform_data - > tpm_i2c_buffer [ 1 ] ) ;
platform_data - > tpm_i2c_buffer [ 1 ] = NULL ;
_tpm_clean_response1 :
kzfree ( platform_data - > tpm_i2c_buffer [ 0 ] ) ;
platform_data - > tpm_i2c_buffer [ 0 ] = NULL ;
2012-11-28 21:22:24 +04:00
_tpm_clean_answer :
2012-12-06 02:52:43 +04:00
tpm_remove_hardware ( chip - > dev ) ;
2012-11-28 21:22:24 +04:00
end :
pr_info ( " TPM I2C initialisation fail \n " ) ;
return err ;
}
/*
* tpm_st33_i2c_remove remove the TPM device
* @ param : client , the i2c_client drescription ( TPM I2C description ) .
clear_bit ( 0 , & chip - > is_open ) ;
* @ return : 0 in case of success .
*/
2013-01-30 01:02:00 +04:00
static int tpm_st33_i2c_remove ( struct i2c_client * client )
2012-11-28 21:22:24 +04:00
{
struct tpm_chip * chip = ( struct tpm_chip * ) i2c_get_clientdata ( client ) ;
struct st33zp24_platform_data * pin_infos =
2013-03-01 02:15:48 +04:00
( ( struct i2c_client * ) TPM_VPRIV ( chip ) ) - > dev . platform_data ;
2012-11-28 21:22:24 +04:00
if ( pin_infos ! = NULL ) {
free_irq ( pin_infos - > io_serirq , chip ) ;
gpio_free ( pin_infos - > io_serirq ) ;
gpio_free ( pin_infos - > io_lpcpd ) ;
2013-01-19 03:42:25 +04:00
tpm_remove_hardware ( chip - > dev ) ;
2012-11-28 21:22:24 +04:00
if ( pin_infos - > tpm_i2c_buffer [ 1 ] ! = NULL ) {
kzfree ( pin_infos - > tpm_i2c_buffer [ 1 ] ) ;
pin_infos - > tpm_i2c_buffer [ 1 ] = NULL ;
}
if ( pin_infos - > tpm_i2c_buffer [ 0 ] ! = NULL ) {
kzfree ( pin_infos - > tpm_i2c_buffer [ 0 ] ) ;
pin_infos - > tpm_i2c_buffer [ 0 ] = NULL ;
}
}
return 0 ;
}
2012-12-06 04:20:51 +04:00
# ifdef CONFIG_PM_SLEEP
2012-11-28 21:22:24 +04:00
/*
* tpm_st33_i2c_pm_suspend suspend the TPM device
* @ param : client , the i2c_client drescription ( TPM I2C description ) .
* @ param : mesg , the power management message .
* @ return : 0 in case of success .
*/
2012-12-06 04:20:51 +04:00
static int tpm_st33_i2c_pm_suspend ( struct device * dev )
2012-11-28 21:22:24 +04:00
{
2012-12-06 04:20:51 +04:00
struct st33zp24_platform_data * pin_infos = dev - > platform_data ;
2012-11-28 21:22:24 +04:00
int ret = 0 ;
2013-03-01 02:15:48 +04:00
if ( power_mgt ) {
2012-11-28 21:22:24 +04:00
gpio_set_value ( pin_infos - > io_lpcpd , 0 ) ;
2013-03-01 02:15:48 +04:00
} else {
2012-12-06 04:20:51 +04:00
ret = tpm_pm_suspend ( dev ) ;
2012-11-28 21:22:24 +04:00
}
return ret ;
} /* tpm_st33_i2c_suspend() */
/*
* tpm_st33_i2c_pm_resume resume the TPM device
* @ param : client , the i2c_client drescription ( TPM I2C description ) .
* @ return : 0 in case of success .
*/
2012-12-06 04:20:51 +04:00
static int tpm_st33_i2c_pm_resume ( struct device * dev )
2012-11-28 21:22:24 +04:00
{
2012-12-06 04:20:51 +04:00
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
struct st33zp24_platform_data * pin_infos = dev - > platform_data ;
2012-11-28 21:22:24 +04:00
int ret = 0 ;
if ( power_mgt ) {
gpio_set_value ( pin_infos - > io_lpcpd , 1 ) ;
ret = wait_for_serirq_timeout ( chip ,
2013-11-27 00:30:44 +04:00
( chip - > ops - > status ( chip ) &
2012-11-28 21:22:24 +04:00
TPM_STS_VALID ) = = TPM_STS_VALID ,
chip - > vendor . timeout_b ) ;
2013-03-01 02:15:48 +04:00
} else {
ret = tpm_pm_resume ( dev ) ;
if ( ! ret )
tpm_do_selftest ( chip ) ;
2012-11-28 21:22:24 +04:00
}
return ret ;
} /* tpm_st33_i2c_pm_resume() */
2012-12-06 04:20:51 +04:00
# endif
2012-11-28 21:22:24 +04:00
static const struct i2c_device_id tpm_st33_i2c_id [ ] = {
{ TPM_ST33_I2C , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tpm_st33_i2c_id ) ;
2013-03-01 02:15:48 +04:00
static SIMPLE_DEV_PM_OPS ( tpm_st33_i2c_ops , tpm_st33_i2c_pm_suspend ,
tpm_st33_i2c_pm_resume ) ;
2012-11-28 21:22:24 +04:00
static struct i2c_driver tpm_st33_i2c_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = TPM_ST33_I2C ,
2012-12-06 04:20:51 +04:00
. pm = & tpm_st33_i2c_ops ,
2012-11-28 21:22:24 +04:00
} ,
. probe = tpm_st33_i2c_probe ,
. remove = tpm_st33_i2c_remove ,
. id_table = tpm_st33_i2c_id
} ;
2012-12-06 02:52:43 +04:00
module_i2c_driver ( tpm_st33_i2c_driver ) ;
2012-11-28 21:22:24 +04:00
MODULE_AUTHOR ( " Christophe Ricard (tpmsupport@st.com) " ) ;
MODULE_DESCRIPTION ( " STM TPM I2C ST33 Driver " ) ;
MODULE_VERSION ( " 1.2.0 " ) ;
2012-12-06 02:52:43 +04:00
MODULE_LICENSE ( " GPL " ) ;