2013-10-06 22:43:36 +04:00
/*
* ATMEL I2C TPM AT97SC3204T
*
* Copyright ( C ) 2012 V Lab Technologies
* Teddy Reed < teddy @ prosauce . org >
* Copyright ( C ) 2013 , Obsidian Research Corp .
* Jason Gunthorpe < jgunthorpe @ obsidianresearch . com >
* Device driver for ATMEL I2C TPMs .
*
* Teddy Reed determined the basic I2C command flow , unlike other I2C TPM
* devices the raw TCG formatted TPM command data is written via I2C and then
* raw TCG formatted TPM command data is returned via I2C .
*
* TGC status / locality / etc functions seen in the LPC implementation do not
* seem to be present .
*
* 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 , see http : //www.gnu.org/licenses/>.
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include "tpm.h"
# define I2C_DRIVER_NAME "tpm_i2c_atmel"
# define TPM_I2C_SHORT_TIMEOUT 750 /* ms */
# define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */
# define ATMEL_STS_OK 1
struct priv_data {
size_t len ;
/* This is the amount we read on the first try. 25 was chosen to fit a
* fair number of read responses in the buffer so a 2 nd retry can be
* avoided in small message cases . */
u8 buffer [ sizeof ( struct tpm_output_header ) + 25 ] ;
} ;
static int i2c_atmel_send ( struct tpm_chip * chip , u8 * buf , size_t len )
{
2016-03-31 23:57:00 +03:00
struct priv_data * priv = dev_get_drvdata ( & chip - > dev ) ;
2016-02-29 20:29:47 +03:00
struct i2c_client * client = to_i2c_client ( chip - > dev . parent ) ;
2013-10-06 22:43:36 +04:00
s32 status ;
priv - > len = 0 ;
if ( len < = 2 )
return - EIO ;
status = i2c_master_send ( client , buf , len ) ;
2016-02-29 20:29:47 +03:00
dev_dbg ( & chip - > dev ,
2013-10-06 22:43:36 +04:00
" %s(buf=%*ph len=%0zx) -> sts=%d \n " , __func__ ,
( int ) min_t ( size_t , 64 , len ) , buf , len , status ) ;
return status ;
}
static int i2c_atmel_recv ( struct tpm_chip * chip , u8 * buf , size_t count )
{
2016-03-31 23:57:00 +03:00
struct priv_data * priv = dev_get_drvdata ( & chip - > dev ) ;
2016-02-29 20:29:47 +03:00
struct i2c_client * client = to_i2c_client ( chip - > dev . parent ) ;
2013-10-06 22:43:36 +04:00
struct tpm_output_header * hdr =
( struct tpm_output_header * ) priv - > buffer ;
u32 expected_len ;
int rc ;
if ( priv - > len = = 0 )
return - EIO ;
/* Get the message size from the message header, if we didn't get the
* whole message in read_status then we need to re - read the
* message . */
expected_len = be32_to_cpu ( hdr - > length ) ;
if ( expected_len > count )
return - ENOMEM ;
if ( priv - > len > = expected_len ) {
2016-02-29 20:29:47 +03:00
dev_dbg ( & chip - > dev ,
2013-10-06 22:43:36 +04:00
" %s early(buf=%*ph count=%0zx) -> ret=%d \n " , __func__ ,
( int ) min_t ( size_t , 64 , expected_len ) , buf , count ,
expected_len ) ;
memcpy ( buf , priv - > buffer , expected_len ) ;
return expected_len ;
}
rc = i2c_master_recv ( client , buf , expected_len ) ;
2016-02-29 20:29:47 +03:00
dev_dbg ( & chip - > dev ,
2013-10-06 22:43:36 +04:00
" %s reread(buf=%*ph count=%0zx) -> ret=%d \n " , __func__ ,
( int ) min_t ( size_t , 64 , expected_len ) , buf , count ,
expected_len ) ;
return rc ;
}
static void i2c_atmel_cancel ( struct tpm_chip * chip )
{
2016-02-29 20:29:47 +03:00
dev_err ( & chip - > dev , " TPM operation cancellation was requested, but is not supported " ) ;
2013-10-06 22:43:36 +04:00
}
static u8 i2c_atmel_read_status ( struct tpm_chip * chip )
{
2016-03-31 23:57:00 +03:00
struct priv_data * priv = dev_get_drvdata ( & chip - > dev ) ;
2016-02-29 20:29:47 +03:00
struct i2c_client * client = to_i2c_client ( chip - > dev . parent ) ;
2013-10-06 22:43:36 +04:00
int rc ;
/* The TPM fails the I2C read until it is ready, so we do the entire
* transfer here and buffer it locally . This way the common code can
* properly handle the timeouts . */
priv - > len = 0 ;
memset ( priv - > buffer , 0 , sizeof ( priv - > buffer ) ) ;
/* Once the TPM has completed the command the command remains readable
* until another command is issued . */
rc = i2c_master_recv ( client , priv - > buffer , sizeof ( priv - > buffer ) ) ;
2016-02-29 20:29:47 +03:00
dev_dbg ( & chip - > dev ,
2013-10-06 22:43:36 +04:00
" %s: sts=%d " , __func__ , rc ) ;
if ( rc < = 0 )
return 0 ;
priv - > len = rc ;
return ATMEL_STS_OK ;
}
static bool i2c_atmel_req_canceled ( struct tpm_chip * chip , u8 status )
{
2013-11-02 20:41:51 +04:00
return false ;
2013-10-06 22:43:36 +04:00
}
2013-11-27 00:30:43 +04:00
static const struct tpm_class_ops i2c_atmel = {
2016-07-12 20:41:49 +03:00
. flags = TPM_OPS_AUTO_STARTUP ,
2013-10-06 22:43:36 +04:00
. status = i2c_atmel_read_status ,
. recv = i2c_atmel_recv ,
. send = i2c_atmel_send ,
. cancel = i2c_atmel_cancel ,
. req_complete_mask = ATMEL_STS_OK ,
. req_complete_val = ATMEL_STS_OK ,
. req_canceled = i2c_atmel_req_canceled ,
} ;
static int i2c_atmel_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct tpm_chip * chip ;
struct device * dev = & client - > dev ;
2016-03-31 23:57:00 +03:00
struct priv_data * priv ;
2013-10-06 22:43:36 +04:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) )
return - ENODEV ;
2014-12-12 22:46:34 +03:00
chip = tpmm_chip_alloc ( dev , & i2c_atmel ) ;
if ( IS_ERR ( chip ) )
return PTR_ERR ( chip ) ;
2013-10-06 22:43:36 +04:00
2016-03-31 23:57:00 +03:00
priv = devm_kzalloc ( dev , sizeof ( struct priv_data ) , GFP_KERNEL ) ;
if ( ! priv )
2014-12-12 22:46:34 +03:00
return - ENOMEM ;
2013-10-06 22:43:36 +04:00
/* Default timeouts */
2016-03-31 23:56:59 +03:00
chip - > timeout_a = msecs_to_jiffies ( TPM_I2C_SHORT_TIMEOUT ) ;
chip - > timeout_b = msecs_to_jiffies ( TPM_I2C_LONG_TIMEOUT ) ;
chip - > timeout_c = msecs_to_jiffies ( TPM_I2C_SHORT_TIMEOUT ) ;
chip - > timeout_d = msecs_to_jiffies ( TPM_I2C_SHORT_TIMEOUT ) ;
2013-10-06 22:43:36 +04:00
2016-03-31 23:57:00 +03:00
dev_set_drvdata ( & chip - > dev , priv ) ;
2013-10-06 22:43:36 +04:00
/* There is no known way to probe for this device, and all version
* information seems to be read via TPM commands . Thus we rely on the
* TPM startup process in the common code to detect the device . */
2014-12-12 22:46:34 +03:00
return tpm_chip_register ( chip ) ;
2013-10-06 22:43:36 +04:00
}
static int i2c_atmel_remove ( struct i2c_client * client )
{
struct device * dev = & ( client - > dev ) ;
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2014-12-12 22:46:34 +03:00
tpm_chip_unregister ( chip ) ;
2013-10-06 22:43:36 +04:00
return 0 ;
}
static const struct i2c_device_id i2c_atmel_id [ ] = {
{ I2C_DRIVER_NAME , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , i2c_atmel_id ) ;
# ifdef CONFIG_OF
static const struct of_device_id i2c_atmel_of_match [ ] = {
{ . compatible = " atmel,at97sc3204t " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , i2c_atmel_of_match ) ;
# endif
static SIMPLE_DEV_PM_OPS ( i2c_atmel_pm_ops , tpm_pm_suspend , tpm_pm_resume ) ;
static struct i2c_driver i2c_atmel_driver = {
. id_table = i2c_atmel_id ,
. probe = i2c_atmel_probe ,
. remove = i2c_atmel_remove ,
. driver = {
. name = I2C_DRIVER_NAME ,
. pm = & i2c_atmel_pm_ops ,
. of_match_table = of_match_ptr ( i2c_atmel_of_match ) ,
} ,
} ;
module_i2c_driver ( i2c_atmel_driver ) ;
MODULE_AUTHOR ( " Jason Gunthorpe <jgunthorpe@obsidianresearch.com> " ) ;
MODULE_DESCRIPTION ( " Atmel TPM I2C Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;