2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2004 IBM Corporation
*
* Authors :
* Leendert van Doorn < leendert @ watson . ibm . com >
* Dave Safford < safford @ watson . ibm . com >
* Reiner Sailer < sailer @ watson . ibm . com >
* Kylene Hall < kjhall @ us . ibm . com >
*
2007-08-23 01:01:04 +04:00
* Maintained by : < tpmdd - devel @ lists . sourceforge . net >
2005-04-17 02:20:36 +04:00
*
* Device driver for TCG / TCPA TPM ( trusted platform module ) .
* Specifications at www . trustedcomputinggroup . org
*
* 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 "tpm.h"
2005-11-14 03:07:41 +03:00
# include "tpm_atmel.h"
2005-04-17 02:20:36 +04:00
/* write status bits */
2005-06-24 09:01:48 +04:00
enum tpm_atmel_write_status {
ATML_STATUS_ABORT = 0x01 ,
ATML_STATUS_LASTBYTE = 0x04
} ;
2005-04-17 02:20:36 +04:00
/* read status bits */
2005-06-24 09:01:48 +04:00
enum tpm_atmel_read_status {
ATML_STATUS_BUSY = 0x01 ,
ATML_STATUS_DATA_AVAIL = 0x02 ,
ATML_STATUS_REWRITE = 0x04 ,
ATML_STATUS_READY = 0x08
} ;
2005-04-17 02:20:36 +04:00
2005-10-31 02:03:28 +03:00
static int tpm_atml_recv ( struct tpm_chip * chip , u8 * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2016-03-31 23:57:00 +03:00
struct tpm_atmel_priv * priv = dev_get_drvdata ( & chip - > dev ) ;
2005-04-17 02:20:36 +04:00
u8 status , * hdr = buf ;
u32 size ;
int i ;
__be32 * native_size ;
/* start reading header */
if ( count < 6 )
return - EIO ;
for ( i = 0 ; i < 6 ; i + + ) {
2016-03-31 23:56:55 +03:00
status = ioread8 ( priv - > iobase + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ( status & ATML_STATUS_DATA_AVAIL ) = = 0 ) {
2016-02-29 20:29:47 +03:00
dev_err ( & chip - > dev , " error reading header \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2016-03-31 23:56:55 +03:00
* buf + + = ioread8 ( priv - > iobase ) ;
2005-04-17 02:20:36 +04:00
}
/* size of the data received */
native_size = ( __force __be32 * ) ( hdr + 2 ) ;
size = be32_to_cpu ( * native_size ) ;
if ( count < size ) {
2016-02-29 20:29:47 +03:00
dev_err ( & chip - > dev ,
2005-04-17 02:20:36 +04:00
" Recv size(%d) less than available space \n " , size ) ;
for ( ; i < size ; i + + ) { /* clear the waiting data anyway */
2016-03-31 23:56:55 +03:00
status = ioread8 ( priv - > iobase + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ( status & ATML_STATUS_DATA_AVAIL ) = = 0 ) {
2016-02-29 20:29:47 +03:00
dev_err ( & chip - > dev , " error reading data \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
}
return - EIO ;
}
/* read all the data available */
for ( ; i < size ; i + + ) {
2016-03-31 23:56:55 +03:00
status = ioread8 ( priv - > iobase + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ( status & ATML_STATUS_DATA_AVAIL ) = = 0 ) {
2016-02-29 20:29:47 +03:00
dev_err ( & chip - > dev , " error reading data \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2016-03-31 23:56:55 +03:00
* buf + + = ioread8 ( priv - > iobase ) ;
2005-04-17 02:20:36 +04:00
}
/* make sure data available is gone */
2016-03-31 23:56:55 +03:00
status = ioread8 ( priv - > iobase + 1 ) ;
2005-11-18 12:10:58 +03:00
2005-04-17 02:20:36 +04:00
if ( status & ATML_STATUS_DATA_AVAIL ) {
2016-02-29 20:29:47 +03:00
dev_err ( & chip - > dev , " data available is stuck \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
return size ;
}
2005-10-31 02:03:28 +03:00
static int tpm_atml_send ( struct tpm_chip * chip , u8 * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2016-03-31 23:57:00 +03:00
struct tpm_atmel_priv * priv = dev_get_drvdata ( & chip - > dev ) ;
2005-04-17 02:20:36 +04:00
int i ;
2016-02-29 20:29:47 +03:00
dev_dbg ( & chip - > dev , " tpm_atml_send: \n " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < count ; i + + ) {
2016-02-29 20:29:47 +03:00
dev_dbg ( & chip - > dev , " %d 0x%x(%d) \n " , i , buf [ i ] , buf [ i ] ) ;
2016-03-31 23:56:55 +03:00
iowrite8 ( buf [ i ] , priv - > iobase ) ;
2005-04-17 02:20:36 +04:00
}
return count ;
}
static void tpm_atml_cancel ( struct tpm_chip * chip )
{
2016-03-31 23:57:00 +03:00
struct tpm_atmel_priv * priv = dev_get_drvdata ( & chip - > dev ) ;
2016-03-31 23:56:55 +03:00
iowrite8 ( ATML_STATUS_ABORT , priv - > iobase + 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-10-31 02:03:23 +03:00
static u8 tpm_atml_status ( struct tpm_chip * chip )
{
2016-03-31 23:57:00 +03:00
struct tpm_atmel_priv * priv = dev_get_drvdata ( & chip - > dev ) ;
2016-03-31 23:56:55 +03:00
return ioread8 ( priv - > iobase + 1 ) ;
2005-10-31 02:03:23 +03:00
}
2013-01-22 23:52:35 +04:00
static bool tpm_atml_req_canceled ( struct tpm_chip * chip , u8 status )
{
return ( status = = ATML_STATUS_READY ) ;
}
2013-11-27 00:30:43 +04:00
static const struct tpm_class_ops tpm_atmel = {
2005-04-17 02:20:36 +04:00
. recv = tpm_atml_recv ,
. send = tpm_atml_send ,
. cancel = tpm_atml_cancel ,
2005-10-31 02:03:23 +03:00
. status = tpm_atml_status ,
2005-04-17 02:20:36 +04:00
. req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL ,
. req_complete_val = ATML_STATUS_DATA_AVAIL ,
2013-01-22 23:52:35 +04:00
. req_canceled = tpm_atml_req_canceled ,
2005-04-17 02:20:36 +04:00
} ;
2005-10-31 02:03:28 +03:00
static struct platform_device * pdev ;
2005-10-31 02:03:25 +03:00
2005-11-14 03:07:41 +03:00
static void atml_plat_remove ( void )
2005-10-31 02:03:25 +03:00
{
2005-11-14 03:07:41 +03:00
struct tpm_chip * chip = dev_get_drvdata ( & pdev - > dev ) ;
2016-03-31 23:57:00 +03:00
struct tpm_atmel_priv * priv = dev_get_drvdata ( & chip - > dev ) ;
2005-11-14 03:07:41 +03:00
2017-06-13 22:55:42 +03:00
tpm_chip_unregister ( chip ) ;
if ( priv - > have_region )
atmel_release_region ( priv - > base , priv - > region_size ) ;
atmel_put_base_addr ( priv - > iobase ) ;
platform_device_unregister ( pdev ) ;
2005-10-31 02:03:25 +03:00
}
2012-07-06 21:09:13 +04:00
static SIMPLE_DEV_PM_OPS ( tpm_atml_pm , tpm_pm_suspend , tpm_pm_resume ) ;
2009-02-06 18:40:12 +03:00
static struct platform_driver atml_drv = {
. driver = {
. name = " tpm_atmel " ,
2012-07-06 21:09:13 +04:00
. pm = & tpm_atml_pm ,
2009-02-06 18:40:12 +03:00
} ,
2005-10-31 02:03:25 +03:00
} ;
static int __init init_atmel ( void )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
2006-04-22 13:37:26 +04:00
void __iomem * iobase = NULL ;
int have_region , region_size ;
unsigned long base ;
struct tpm_chip * chip ;
2016-03-23 08:10:22 +03:00
struct tpm_atmel_priv * priv ;
2005-04-17 02:20:36 +04:00
2009-02-06 18:40:12 +03:00
rc = platform_driver_register ( & atml_drv ) ;
2006-10-11 12:21:51 +04:00
if ( rc )
return rc ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:26 +04:00
if ( ( iobase = atmel_get_base_addr ( & base , & region_size ) ) = = NULL ) {
2005-11-14 03:07:41 +03:00
rc = - ENODEV ;
goto err_unreg_drv ;
2005-10-31 02:03:25 +03:00
}
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:26 +04:00
have_region =
2005-11-18 12:10:58 +03:00
( atmel_request_region
2013-09-15 03:36:29 +04:00
( base , region_size , " tpm_atmel0 " ) = = NULL ) ? 0 : 1 ;
2006-04-22 13:37:26 +04:00
2006-10-11 12:21:51 +04:00
pdev = platform_device_register_simple ( " tpm_atmel " , - 1 , NULL , 0 ) ;
if ( IS_ERR ( pdev ) ) {
2005-11-14 03:07:41 +03:00
rc = PTR_ERR ( pdev ) ;
goto err_rel_reg ;
2005-10-31 02:03:25 +03:00
}
2005-04-17 02:20:36 +04:00
2016-03-23 08:10:22 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
rc = - ENOMEM ;
goto err_unreg_dev ;
}
2016-03-31 23:56:55 +03:00
priv - > iobase = iobase ;
2016-03-23 09:16:09 +03:00
priv - > base = base ;
2016-03-23 08:10:22 +03:00
priv - > have_region = have_region ;
priv - > region_size = region_size ;
2014-12-12 22:46:34 +03:00
chip = tpmm_chip_alloc ( & pdev - > dev , & tpm_atmel ) ;
if ( IS_ERR ( chip ) ) {
rc = PTR_ERR ( chip ) ;
2005-11-14 03:07:41 +03:00
goto err_unreg_dev ;
2006-04-22 13:37:26 +04:00
}
2016-03-31 23:57:00 +03:00
dev_set_drvdata ( & chip - > dev , priv ) ;
2006-04-22 13:37:26 +04:00
2014-12-12 22:46:34 +03:00
rc = tpm_chip_register ( chip ) ;
if ( rc )
goto err_unreg_dev ;
2005-10-31 02:03:25 +03:00
return 0 ;
2005-11-14 03:07:41 +03:00
err_unreg_dev :
platform_device_unregister ( pdev ) ;
err_rel_reg :
2006-04-22 13:37:26 +04:00
atmel_put_base_addr ( iobase ) ;
if ( have_region )
atmel_release_region ( base ,
region_size ) ;
2005-11-14 03:07:41 +03:00
err_unreg_drv :
2009-02-06 18:40:12 +03:00
platform_driver_unregister ( & atml_drv ) ;
2005-11-14 03:07:41 +03:00
return rc ;
2005-04-17 02:20:36 +04:00
}
static void __exit cleanup_atmel ( void )
{
2009-02-06 18:40:12 +03:00
platform_driver_unregister ( & atml_drv ) ;
2005-11-14 03:07:41 +03:00
atml_plat_remove ( ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( init_atmel ) ;
module_exit ( cleanup_atmel ) ;
MODULE_AUTHOR ( " Leendert van Doorn (leendert@watson.ibm.com) " ) ;
MODULE_DESCRIPTION ( " TPM Driver " ) ;
MODULE_VERSION ( " 2.0 " ) ;
MODULE_LICENSE ( " GPL " ) ;