2016-05-19 00:35:53 +02:00
/*
* Copyright ( C ) 2015 Infineon Technologies AG
* Copyright ( C ) 2016 STMicroelectronics SAS
*
* Authors :
* Peter Huewe < peter . huewe @ infineon . com >
* Christophe Ricard < christophe - h . ricard @ st . com >
*
* Maintained by : < tpmdd - devel @ lists . sourceforge . net >
*
* Device driver for TCG / TCPA TPM ( trusted platform module ) .
* Specifications at www . trustedcomputinggroup . org
*
* This device driver implements the TPM interface as defined in
* the TCG TPM Interface Spec version 1.3 , revision 27 via _raw / native
* SPI access_ .
*
* It is based on the original tpm_tis device driver from Leendert van
* Dorn and Kyleen Hall and Jarko Sakkinnen .
*
* 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 , version 2 of the
* License .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/wait.h>
# include <linux/acpi.h>
# include <linux/freezer.h>
# include <linux/spi/spi.h>
# include <linux/gpio.h>
# include <linux/of_irq.h>
# include <linux/of_gpio.h>
# include <linux/tpm.h>
# include "tpm.h"
# include "tpm_tis_core.h"
# define MAX_SPI_FRAMESIZE 64
struct tpm_tis_spi_phy {
struct tpm_tis_data priv ;
struct spi_device * spi_device ;
2017-09-11 12:26:52 +02:00
u8 * iobuf ;
2016-05-19 00:35:53 +02:00
} ;
static inline struct tpm_tis_spi_phy * to_tpm_tis_spi_phy ( struct tpm_tis_data * data )
{
return container_of ( data , struct tpm_tis_spi_phy , priv ) ;
}
2017-03-02 13:03:11 +00:00
static int tpm_tis_spi_transfer ( struct tpm_tis_data * data , u32 addr , u16 len ,
2017-09-07 15:30:45 +02:00
u8 * in , const u8 * out )
2016-05-19 00:35:53 +02:00
{
struct tpm_tis_spi_phy * phy = to_tpm_tis_spi_phy ( data ) ;
2017-03-02 13:03:14 +00:00
int ret = 0 ;
int i ;
2016-05-19 00:35:53 +02:00
struct spi_message m ;
2017-03-02 13:03:14 +00:00
struct spi_transfer spi_xfer ;
u8 transfer_len ;
2016-05-19 00:35:53 +02:00
2017-03-02 13:03:14 +00:00
spi_bus_lock ( phy - > spi_device - > master ) ;
2016-05-19 00:35:53 +02:00
2017-03-02 13:03:14 +00:00
while ( len ) {
transfer_len = min_t ( u16 , len , MAX_SPI_FRAMESIZE ) ;
2016-05-19 00:35:53 +02:00
2017-09-11 12:26:52 +02:00
phy - > iobuf [ 0 ] = ( in ? 0x80 : 0 ) | ( transfer_len - 1 ) ;
phy - > iobuf [ 1 ] = 0xd4 ;
phy - > iobuf [ 2 ] = addr > > 8 ;
phy - > iobuf [ 3 ] = addr ;
2016-05-19 00:35:53 +02:00
2017-03-02 13:03:14 +00:00
memset ( & spi_xfer , 0 , sizeof ( spi_xfer ) ) ;
2017-09-11 12:26:52 +02:00
spi_xfer . tx_buf = phy - > iobuf ;
spi_xfer . rx_buf = phy - > iobuf ;
2017-03-02 13:03:14 +00:00
spi_xfer . len = 4 ;
spi_xfer . cs_change = 1 ;
spi_message_init ( & m ) ;
spi_message_add_tail ( & spi_xfer , & m ) ;
ret = spi_sync_locked ( phy - > spi_device , & m ) ;
if ( ret < 0 )
goto exit ;
2017-09-11 12:26:52 +02:00
if ( ( phy - > iobuf [ 3 ] & 0x01 ) = = 0 ) {
2017-03-02 13:03:14 +00:00
// handle SPI wait states
2017-09-11 12:26:52 +02:00
phy - > iobuf [ 0 ] = 0 ;
2017-03-02 13:03:14 +00:00
for ( i = 0 ; i < TPM_RETRY ; i + + ) {
spi_xfer . len = 1 ;
spi_message_init ( & m ) ;
spi_message_add_tail ( & spi_xfer , & m ) ;
ret = spi_sync_locked ( phy - > spi_device , & m ) ;
if ( ret < 0 )
goto exit ;
2017-09-11 12:26:52 +02:00
if ( phy - > iobuf [ 0 ] & 0x01 )
2017-03-02 13:03:14 +00:00
break ;
}
if ( i = = TPM_RETRY ) {
ret = - ETIMEDOUT ;
2017-03-02 13:03:13 +00:00
goto exit ;
2017-03-02 13:03:14 +00:00
}
2017-03-02 13:03:13 +00:00
}
2017-03-02 13:03:14 +00:00
spi_xfer . cs_change = 0 ;
spi_xfer . len = transfer_len ;
2017-03-02 13:03:15 +00:00
spi_xfer . delay_usecs = 5 ;
2017-09-11 12:26:52 +02:00
if ( in ) {
spi_xfer . tx_buf = NULL ;
} else if ( out ) {
spi_xfer . rx_buf = NULL ;
memcpy ( phy - > iobuf , out , transfer_len ) ;
out + = transfer_len ;
}
2017-03-02 13:03:12 +00:00
2017-03-02 13:03:14 +00:00
spi_message_init ( & m ) ;
spi_message_add_tail ( & spi_xfer , & m ) ;
ret = spi_sync_locked ( phy - > spi_device , & m ) ;
if ( ret < 0 )
goto exit ;
2017-03-02 13:03:11 +00:00
2017-09-11 12:26:52 +02:00
if ( in ) {
memcpy ( in , phy - > iobuf , transfer_len ) ;
2017-09-07 15:30:45 +02:00
in + = transfer_len ;
2017-09-11 12:26:52 +02:00
}
len - = transfer_len ;
2017-03-02 13:03:11 +00:00
}
2016-05-19 00:35:53 +02:00
exit :
spi_bus_unlock ( phy - > spi_device - > master ) ;
return ret ;
}
2017-03-02 13:03:11 +00:00
static int tpm_tis_spi_read_bytes ( struct tpm_tis_data * data , u32 addr ,
u16 len , u8 * result )
{
2017-09-07 15:30:45 +02:00
return tpm_tis_spi_transfer ( data , addr , len , result , NULL ) ;
2017-03-02 13:03:11 +00:00
}
2016-05-19 00:35:53 +02:00
static int tpm_tis_spi_write_bytes ( struct tpm_tis_data * data , u32 addr ,
2017-09-07 15:30:45 +02:00
u16 len , const u8 * value )
2016-05-19 00:35:53 +02:00
{
2017-09-07 15:30:45 +02:00
return tpm_tis_spi_transfer ( data , addr , len , NULL , value ) ;
2016-05-19 00:35:53 +02:00
}
static int tpm_tis_spi_read16 ( struct tpm_tis_data * data , u32 addr , u16 * result )
{
2017-09-13 10:17:25 -07:00
__le16 result_le ;
2016-05-19 00:35:53 +02:00
int rc ;
2017-09-13 10:17:25 -07:00
rc = data - > phy_ops - > read_bytes ( data , addr , sizeof ( u16 ) ,
( u8 * ) & result_le ) ;
2016-05-19 00:35:53 +02:00
if ( ! rc )
2017-09-13 10:17:25 -07:00
* result = le16_to_cpu ( result_le ) ;
2016-05-19 00:35:53 +02:00
return rc ;
}
static int tpm_tis_spi_read32 ( struct tpm_tis_data * data , u32 addr , u32 * result )
{
2017-09-13 10:17:25 -07:00
__le32 result_le ;
2016-05-19 00:35:53 +02:00
int rc ;
2017-09-13 10:17:25 -07:00
rc = data - > phy_ops - > read_bytes ( data , addr , sizeof ( u32 ) ,
( u8 * ) & result_le ) ;
2016-05-19 00:35:53 +02:00
if ( ! rc )
2017-09-13 10:17:25 -07:00
* result = le32_to_cpu ( result_le ) ;
2016-05-19 00:35:53 +02:00
return rc ;
}
static int tpm_tis_spi_write32 ( struct tpm_tis_data * data , u32 addr , u32 value )
{
2017-09-13 10:17:25 -07:00
__le32 value_le ;
int rc ;
value_le = cpu_to_le32 ( value ) ;
rc = data - > phy_ops - > write_bytes ( data , addr , sizeof ( u32 ) ,
( u8 * ) & value_le ) ;
return rc ;
2016-05-19 00:35:53 +02:00
}
static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
. read_bytes = tpm_tis_spi_read_bytes ,
. write_bytes = tpm_tis_spi_write_bytes ,
. read16 = tpm_tis_spi_read16 ,
. read32 = tpm_tis_spi_read32 ,
. write32 = tpm_tis_spi_write32 ,
} ;
static int tpm_tis_spi_probe ( struct spi_device * dev )
{
struct tpm_tis_spi_phy * phy ;
2018-06-08 09:09:07 +02:00
int irq ;
2016-05-19 00:35:53 +02:00
phy = devm_kzalloc ( & dev - > dev , sizeof ( struct tpm_tis_spi_phy ) ,
GFP_KERNEL ) ;
if ( ! phy )
return - ENOMEM ;
phy - > spi_device = dev ;
2017-09-11 12:26:52 +02:00
phy - > iobuf = devm_kmalloc ( & dev - > dev , MAX_SPI_FRAMESIZE , GFP_KERNEL ) ;
if ( ! phy - > iobuf )
return - ENOMEM ;
2018-06-08 09:09:07 +02:00
/* If the SPI device has an IRQ then use that */
if ( dev - > irq > 0 )
irq = dev - > irq ;
else
irq = - 1 ;
return tpm_tis_core_init ( & dev - > dev , & phy - > priv , irq , & tpm_spi_phy_ops ,
2016-05-19 00:35:53 +02:00
NULL ) ;
}
static SIMPLE_DEV_PM_OPS ( tpm_tis_pm , tpm_pm_suspend , tpm_tis_resume ) ;
static int tpm_tis_spi_remove ( struct spi_device * dev )
{
struct tpm_chip * chip = spi_get_drvdata ( dev ) ;
tpm_chip_unregister ( chip ) ;
tpm_tis_remove ( chip ) ;
return 0 ;
}
static const struct spi_device_id tpm_tis_spi_id [ ] = {
{ " tpm_tis_spi " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , tpm_tis_spi_id ) ;
static const struct of_device_id of_tis_spi_match [ ] = {
{ . compatible = " st,st33htpm-spi " , } ,
{ . compatible = " infineon,slb9670 " , } ,
{ . compatible = " tcg,tpm_tis-spi " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , of_tis_spi_match ) ;
static const struct acpi_device_id acpi_tis_spi_match [ ] = {
{ " SMO0768 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , acpi_tis_spi_match ) ;
static struct spi_driver tpm_tis_spi_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " tpm_tis_spi " ,
. pm = & tpm_tis_pm ,
. of_match_table = of_match_ptr ( of_tis_spi_match ) ,
. acpi_match_table = ACPI_PTR ( acpi_tis_spi_match ) ,
} ,
. probe = tpm_tis_spi_probe ,
. remove = tpm_tis_spi_remove ,
. id_table = tpm_tis_spi_id ,
} ;
module_spi_driver ( tpm_tis_spi_driver ) ;
MODULE_DESCRIPTION ( " TPM Driver for native SPI access " ) ;
MODULE_LICENSE ( " GPL " ) ;