2005-07-27 11:45:12 -07:00
/*
* Description :
* Device Driver for the Infineon Technologies
2005-08-05 11:59:33 -07:00
* SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module
2005-07-27 11:45:12 -07:00
* Specifications at www . trustedcomputinggroup . org
*
* Copyright ( C ) 2005 , Marcel Selhorst < selhorst @ crypto . rub . de >
2005-10-30 15:03:27 -08:00
* Sirrix AG - security technologies , http : //www.sirrix.com and
2005-07-27 11:45:12 -07:00
* Applied Data Security Group , Ruhr - University Bochum , Germany
* Project - Homepage : http : //www.prosec.rub.de/tpm
*
* 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 .
*/
2006-04-22 02:39:18 -07:00
# include <linux/init.h>
2005-08-05 11:59:33 -07:00
# include <linux/pnp.h>
2005-07-27 11:45:12 -07:00
# include "tpm.h"
/* Infineon specific definitions */
/* maximum number of WTX-packages */
# define TPM_MAX_WTX_PACKAGES 50
/* msleep-Time for WTX-packages */
# define TPM_WTX_MSLEEP_TIME 20
/* msleep-Time --> Interval to check status register */
# define TPM_MSLEEP_TIME 3
/* gives number of max. msleep()-calls before throwing timeout */
# define TPM_MAX_TRIES 5000
2005-08-05 11:59:33 -07:00
# define TPM_INFINEON_DEV_VEN_VALUE 0x15D1
2005-09-03 15:54:20 -07:00
/* These values will be filled after PnP-call */
2005-10-30 15:03:28 -08:00
static int TPM_INF_DATA ;
static int TPM_INF_ADDR ;
static int TPM_INF_BASE ;
2006-02-17 13:52:41 -08:00
static int TPM_INF_ADDR_LEN ;
2005-10-30 15:03:28 -08:00
static int TPM_INF_PORT_LEN ;
2005-07-27 11:45:12 -07:00
/* TPM header definitions */
enum infineon_tpm_header {
TPM_VL_VER = 0x01 ,
TPM_VL_CHANNEL_CONTROL = 0x07 ,
TPM_VL_CHANNEL_PERSONALISATION = 0x0A ,
TPM_VL_CHANNEL_TPM = 0x0B ,
TPM_VL_CONTROL = 0x00 ,
TPM_INF_NAK = 0x15 ,
TPM_CTRL_WTX = 0x10 ,
TPM_CTRL_WTX_ABORT = 0x18 ,
TPM_CTRL_WTX_ABORT_ACK = 0x18 ,
TPM_CTRL_ERROR = 0x20 ,
TPM_CTRL_CHAININGACK = 0x40 ,
TPM_CTRL_CHAINING = 0x80 ,
TPM_CTRL_DATA = 0x04 ,
TPM_CTRL_DATA_CHA = 0x84 ,
TPM_CTRL_DATA_CHA_ACK = 0xC4
} ;
enum infineon_tpm_register {
WRFIFO = 0x00 ,
RDFIFO = 0x01 ,
STAT = 0x02 ,
CMD = 0x03
} ;
enum infineon_tpm_command_bits {
CMD_DIS = 0x00 ,
CMD_LP = 0x01 ,
CMD_RES = 0x02 ,
CMD_IRQC = 0x06
} ;
enum infineon_tpm_status_bits {
STAT_XFE = 0x00 ,
STAT_LPA = 0x01 ,
STAT_FOK = 0x02 ,
STAT_TOK = 0x03 ,
STAT_IRQA = 0x06 ,
STAT_RDA = 0x07
} ;
/* some outgoing values */
enum infineon_tpm_values {
CHIP_ID1 = 0x20 ,
CHIP_ID2 = 0x21 ,
2005-07-27 11:45:14 -07:00
TPM_DAR = 0x30 ,
2005-07-27 11:45:12 -07:00
RESET_LP_IRQC_DISABLE = 0x41 ,
ENABLE_REGISTER_PAIR = 0x55 ,
IOLIMH = 0x60 ,
IOLIML = 0x61 ,
DISABLE_REGISTER_PAIR = 0xAA ,
IDVENL = 0xF1 ,
IDVENH = 0xF2 ,
IDPDL = 0xF3 ,
IDPDH = 0xF4
} ;
static int number_of_wtx ;
static int empty_fifo ( struct tpm_chip * chip , int clear_wrfifo )
{
int status ;
int check = 0 ;
int i ;
if ( clear_wrfifo ) {
for ( i = 0 ; i < 4096 ; i + + ) {
2006-04-22 02:38:42 -07:00
status = inb ( chip - > vendor . base + WRFIFO ) ;
2005-07-27 11:45:12 -07:00
if ( status = = 0xff ) {
if ( check = = 5 )
break ;
else
check + + ;
}
}
}
/* Note: The values which are currently in the FIFO of the TPM
are thrown away since there is no usage for them . Usually ,
this has nothing to say , since the TPM will give its answer
immediately or will be aborted anyway , so the data here is
usually garbage and useless .
We have to clean this , because the next communication with
the TPM would be rubbish , if there is still some old data
in the Read FIFO .
*/
i = 0 ;
do {
2006-04-22 02:38:42 -07:00
status = inb ( chip - > vendor . base + RDFIFO ) ;
status = inb ( chip - > vendor . base + STAT ) ;
2005-07-27 11:45:12 -07:00
i + + ;
if ( i = = TPM_MAX_TRIES )
return - EIO ;
} while ( ( status & ( 1 < < STAT_RDA ) ) ! = 0 ) ;
return 0 ;
}
static int wait ( struct tpm_chip * chip , int wait_for_bit )
{
int status ;
int i ;
for ( i = 0 ; i < TPM_MAX_TRIES ; i + + ) {
2006-04-22 02:38:42 -07:00
status = inb ( chip - > vendor . base + STAT ) ;
2005-07-27 11:45:12 -07:00
/* check the status-register if wait_for_bit is set */
if ( status & 1 < < wait_for_bit )
break ;
msleep ( TPM_MSLEEP_TIME ) ;
}
if ( i = = TPM_MAX_TRIES ) { /* timeout occurs */
if ( wait_for_bit = = STAT_XFE )
2005-10-30 15:03:27 -08:00
dev_err ( chip - > dev , " Timeout in wait(STAT_XFE) \n " ) ;
2005-07-27 11:45:12 -07:00
if ( wait_for_bit = = STAT_RDA )
2005-10-30 15:03:27 -08:00
dev_err ( chip - > dev , " Timeout in wait(STAT_RDA) \n " ) ;
2005-07-27 11:45:12 -07:00
return - EIO ;
}
return 0 ;
} ;
static void wait_and_send ( struct tpm_chip * chip , u8 sendbyte )
{
wait ( chip , STAT_XFE ) ;
2006-04-22 02:38:42 -07:00
outb ( sendbyte , chip - > vendor . base + WRFIFO ) ;
2005-07-27 11:45:12 -07:00
}
/* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more
calculation time , it sends a WTX - package , which has to be acknowledged
or aborted . This usually occurs if you are hammering the TPM with key
creation . Set the maximum number of WTX - packages in the definitions
above , if the number is reached , the waiting - time will be denied
and the TPM command has to be resend .
*/
static void tpm_wtx ( struct tpm_chip * chip )
{
number_of_wtx + + ;
2005-10-30 15:03:24 -08:00
dev_info ( chip - > dev , " Granting WTX (%02d / %02d) \n " ,
2005-07-27 11:45:12 -07:00
number_of_wtx , TPM_MAX_WTX_PACKAGES ) ;
wait_and_send ( chip , TPM_VL_VER ) ;
wait_and_send ( chip , TPM_CTRL_WTX ) ;
wait_and_send ( chip , 0x00 ) ;
wait_and_send ( chip , 0x00 ) ;
msleep ( TPM_WTX_MSLEEP_TIME ) ;
}
static void tpm_wtx_abort ( struct tpm_chip * chip )
{
2005-10-30 15:03:24 -08:00
dev_info ( chip - > dev , " Aborting WTX \n " ) ;
2005-07-27 11:45:12 -07:00
wait_and_send ( chip , TPM_VL_VER ) ;
wait_and_send ( chip , TPM_CTRL_WTX_ABORT ) ;
wait_and_send ( chip , 0x00 ) ;
wait_and_send ( chip , 0x00 ) ;
number_of_wtx = 0 ;
msleep ( TPM_WTX_MSLEEP_TIME ) ;
}
static int tpm_inf_recv ( struct tpm_chip * chip , u8 * buf , size_t count )
{
int i ;
int ret ;
u32 size = 0 ;
2006-02-17 13:52:41 -08:00
number_of_wtx = 0 ;
2005-07-27 11:45:12 -07:00
recv_begin :
/* start receiving header */
for ( i = 0 ; i < 4 ; i + + ) {
ret = wait ( chip , STAT_RDA ) ;
if ( ret )
return - EIO ;
2006-04-22 02:38:42 -07:00
buf [ i ] = inb ( chip - > vendor . base + RDFIFO ) ;
2005-07-27 11:45:12 -07:00
}
if ( buf [ 0 ] ! = TPM_VL_VER ) {
2005-10-30 15:03:24 -08:00
dev_err ( chip - > dev ,
2005-07-27 11:45:12 -07:00
" Wrong transport protocol implementation! \n " ) ;
return - EIO ;
}
if ( buf [ 1 ] = = TPM_CTRL_DATA ) {
/* size of the data received */
size = ( ( buf [ 2 ] < < 8 ) | buf [ 3 ] ) ;
for ( i = 0 ; i < size ; i + + ) {
wait ( chip , STAT_RDA ) ;
2006-04-22 02:38:42 -07:00
buf [ i ] = inb ( chip - > vendor . base + RDFIFO ) ;
2005-07-27 11:45:12 -07:00
}
if ( ( size = = 0x6D00 ) & & ( buf [ 1 ] = = 0x80 ) ) {
2005-10-30 15:03:27 -08:00
dev_err ( chip - > dev , " Error handling on vendor layer! \n " ) ;
2005-07-27 11:45:12 -07:00
return - EIO ;
}
for ( i = 0 ; i < size ; i + + )
buf [ i ] = buf [ i + 6 ] ;
size = size - 6 ;
return size ;
}
if ( buf [ 1 ] = = TPM_CTRL_WTX ) {
2005-10-30 15:03:24 -08:00
dev_info ( chip - > dev , " WTX-package received \n " ) ;
2005-07-27 11:45:12 -07:00
if ( number_of_wtx < TPM_MAX_WTX_PACKAGES ) {
tpm_wtx ( chip ) ;
goto recv_begin ;
} else {
tpm_wtx_abort ( chip ) ;
goto recv_begin ;
}
}
if ( buf [ 1 ] = = TPM_CTRL_WTX_ABORT_ACK ) {
2005-10-30 15:03:24 -08:00
dev_info ( chip - > dev , " WTX-abort acknowledged \n " ) ;
2005-07-27 11:45:12 -07:00
return size ;
}
if ( buf [ 1 ] = = TPM_CTRL_ERROR ) {
2005-10-30 15:03:24 -08:00
dev_err ( chip - > dev , " ERROR-package received: \n " ) ;
2005-07-27 11:45:12 -07:00
if ( buf [ 4 ] = = TPM_INF_NAK )
2005-10-30 15:03:24 -08:00
dev_err ( chip - > dev ,
2005-07-27 11:45:12 -07:00
" -> Negative acknowledgement "
" - retransmit command! \n " ) ;
return - EIO ;
}
return - EIO ;
}
static int tpm_inf_send ( struct tpm_chip * chip , u8 * buf , size_t count )
{
int i ;
int ret ;
u8 count_high , count_low , count_4 , count_3 , count_2 , count_1 ;
/* Disabling Reset, LP and IRQC */
2006-04-22 02:38:42 -07:00
outb ( RESET_LP_IRQC_DISABLE , chip - > vendor . base + CMD ) ;
2005-07-27 11:45:12 -07:00
ret = empty_fifo ( chip , 1 ) ;
if ( ret ) {
2005-10-30 15:03:24 -08:00
dev_err ( chip - > dev , " Timeout while clearing FIFO \n " ) ;
2005-07-27 11:45:12 -07:00
return - EIO ;
}
ret = wait ( chip , STAT_XFE ) ;
if ( ret )
return - EIO ;
count_4 = ( count & 0xff000000 ) > > 24 ;
count_3 = ( count & 0x00ff0000 ) > > 16 ;
count_2 = ( count & 0x0000ff00 ) > > 8 ;
count_1 = ( count & 0x000000ff ) ;
count_high = ( ( count + 6 ) & 0xffffff00 ) > > 8 ;
count_low = ( ( count + 6 ) & 0x000000ff ) ;
/* Sending Header */
wait_and_send ( chip , TPM_VL_VER ) ;
wait_and_send ( chip , TPM_CTRL_DATA ) ;
wait_and_send ( chip , count_high ) ;
wait_and_send ( chip , count_low ) ;
/* Sending Data Header */
wait_and_send ( chip , TPM_VL_VER ) ;
wait_and_send ( chip , TPM_VL_CHANNEL_TPM ) ;
wait_and_send ( chip , count_4 ) ;
wait_and_send ( chip , count_3 ) ;
wait_and_send ( chip , count_2 ) ;
wait_and_send ( chip , count_1 ) ;
/* Sending Data */
for ( i = 0 ; i < count ; i + + ) {
wait_and_send ( chip , buf [ i ] ) ;
}
return count ;
}
static void tpm_inf_cancel ( struct tpm_chip * chip )
{
2005-08-05 11:59:33 -07:00
/*
Since we are using the legacy mode to communicate
with the TPM , we have no cancel functions , but have
a workaround for interrupting the TPM through WTX .
2005-07-27 11:45:12 -07:00
*/
}
2005-10-30 15:03:23 -08:00
static u8 tpm_inf_status ( struct tpm_chip * chip )
{
2006-04-22 02:38:42 -07:00
return inb ( chip - > vendor . base + STAT ) ;
2005-10-30 15:03:23 -08:00
}
2005-07-27 11:45:12 -07:00
static DEVICE_ATTR ( pubek , S_IRUGO , tpm_show_pubek , NULL ) ;
static DEVICE_ATTR ( pcrs , S_IRUGO , tpm_show_pcrs , NULL ) ;
static DEVICE_ATTR ( caps , S_IRUGO , tpm_show_caps , NULL ) ;
static DEVICE_ATTR ( cancel , S_IWUSR | S_IWGRP , NULL , tpm_store_cancel ) ;
static struct attribute * inf_attrs [ ] = {
& dev_attr_pubek . attr ,
& dev_attr_pcrs . attr ,
& dev_attr_caps . attr ,
& dev_attr_cancel . attr ,
NULL ,
} ;
static struct attribute_group inf_attr_grp = { . attrs = inf_attrs } ;
2006-07-03 00:24:21 -07:00
static const struct file_operations inf_ops = {
2005-07-27 11:45:12 -07:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. open = tpm_open ,
. read = tpm_read ,
. write = tpm_write ,
. release = tpm_release ,
} ;
2006-04-22 02:38:42 -07:00
static const struct tpm_vendor_specific tpm_inf = {
2005-07-27 11:45:12 -07:00
. recv = tpm_inf_recv ,
. send = tpm_inf_send ,
. cancel = tpm_inf_cancel ,
2005-10-30 15:03:23 -08:00
. status = tpm_inf_status ,
2005-07-27 11:45:12 -07:00
. req_complete_mask = 0 ,
. req_complete_val = 0 ,
. attr_group = & inf_attr_grp ,
. miscdev = { . fops = & inf_ops , } ,
} ;
2005-08-05 11:59:33 -07:00
static const struct pnp_device_id tpm_pnp_tbl [ ] = {
/* Infineon TPMs */
{ " IFX0101 " , 0 } ,
{ " IFX0102 " , 0 } ,
{ " " , 0 }
} ;
2005-10-30 15:03:27 -08:00
2005-09-03 15:54:20 -07:00
MODULE_DEVICE_TABLE ( pnp , tpm_pnp_tbl ) ;
2005-08-05 11:59:33 -07:00
2005-09-03 15:54:20 -07:00
static int __devinit tpm_inf_pnp_probe ( struct pnp_dev * dev ,
2005-10-30 15:03:27 -08:00
const struct pnp_device_id * dev_id )
2005-07-27 11:45:12 -07:00
{
int rc = 0 ;
u8 iol , ioh ;
int vendorid [ 2 ] ;
int version [ 2 ] ;
int productid [ 2 ] ;
2005-08-05 11:59:33 -07:00
char chipname [ 20 ] ;
2006-04-22 02:38:42 -07:00
struct tpm_chip * chip ;
2005-07-27 11:45:12 -07:00
2005-10-30 15:03:27 -08:00
/* read IO-ports through PnP */
if ( pnp_port_valid ( dev , 0 ) & & pnp_port_valid ( dev , 1 ) & &
! ( pnp_port_flags ( dev , 0 ) & IORESOURCE_DISABLED ) ) {
TPM_INF_ADDR = pnp_port_start ( dev , 0 ) ;
2006-02-17 13:52:41 -08:00
TPM_INF_ADDR_LEN = pnp_port_len ( dev , 0 ) ;
2005-10-30 15:03:27 -08:00
TPM_INF_DATA = ( TPM_INF_ADDR + 1 ) ;
TPM_INF_BASE = pnp_port_start ( dev , 1 ) ;
TPM_INF_PORT_LEN = pnp_port_len ( dev , 1 ) ;
2006-02-17 13:52:41 -08:00
if ( ( TPM_INF_PORT_LEN < 4 ) | | ( TPM_INF_ADDR_LEN < 2 ) ) {
rc = - EINVAL ;
goto err_last ;
}
2005-10-30 15:03:27 -08:00
dev_info ( & dev - > dev , " Found %s with ID %s \n " ,
dev - > name , dev_id - > id ) ;
2006-02-17 13:52:41 -08:00
if ( ! ( ( TPM_INF_BASE > > 8 ) & 0xff ) ) {
rc = - EINVAL ;
goto err_last ;
}
2005-10-30 15:03:27 -08:00
/* publish my base address and request region */
if ( request_region
2006-04-22 02:38:42 -07:00
( TPM_INF_BASE , TPM_INF_PORT_LEN , " tpm_infineon0 " ) = = NULL ) {
2006-02-17 13:52:41 -08:00
rc = - EINVAL ;
goto err_last ;
}
2006-04-22 02:38:42 -07:00
if ( request_region
( TPM_INF_ADDR , TPM_INF_ADDR_LEN , " tpm_infineon0 " ) = = NULL ) {
2006-02-17 13:52:41 -08:00
rc = - EINVAL ;
goto err_last ;
2005-10-30 15:03:27 -08:00
}
2005-09-03 15:54:20 -07:00
} else {
2006-02-17 13:52:41 -08:00
rc = - EINVAL ;
goto err_last ;
2005-08-05 11:59:33 -07:00
}
2005-07-27 11:45:12 -07:00
/* query chip for its vendor, its version number a.s.o. */
2005-08-05 11:59:33 -07:00
outb ( ENABLE_REGISTER_PAIR , TPM_INF_ADDR ) ;
outb ( IDVENL , TPM_INF_ADDR ) ;
vendorid [ 1 ] = inb ( TPM_INF_DATA ) ;
outb ( IDVENH , TPM_INF_ADDR ) ;
vendorid [ 0 ] = inb ( TPM_INF_DATA ) ;
outb ( IDPDL , TPM_INF_ADDR ) ;
productid [ 1 ] = inb ( TPM_INF_DATA ) ;
outb ( IDPDH , TPM_INF_ADDR ) ;
productid [ 0 ] = inb ( TPM_INF_DATA ) ;
outb ( CHIP_ID1 , TPM_INF_ADDR ) ;
version [ 1 ] = inb ( TPM_INF_DATA ) ;
outb ( CHIP_ID2 , TPM_INF_ADDR ) ;
version [ 0 ] = inb ( TPM_INF_DATA ) ;
switch ( ( productid [ 0 ] < < 8 ) | productid [ 1 ] ) {
case 6 :
2005-09-03 15:54:20 -07:00
snprintf ( chipname , sizeof ( chipname ) , " (SLD 9630 TT 1.1) " ) ;
2005-08-05 11:59:33 -07:00
break ;
case 11 :
2005-09-03 15:54:20 -07:00
snprintf ( chipname , sizeof ( chipname ) , " (SLB 9635 TT 1.2) " ) ;
2005-08-05 11:59:33 -07:00
break ;
default :
2005-09-03 15:54:20 -07:00
snprintf ( chipname , sizeof ( chipname ) , " (unknown chip) " ) ;
2005-08-05 11:59:33 -07:00
break ;
}
if ( ( vendorid [ 0 ] < < 8 | vendorid [ 1 ] ) = = ( TPM_INFINEON_DEV_VEN_VALUE ) ) {
2005-07-27 11:45:12 -07:00
2005-08-05 11:59:33 -07:00
/* configure TPM with IO-ports */
outb ( IOLIMH , TPM_INF_ADDR ) ;
2006-04-22 02:38:42 -07:00
outb ( ( ( TPM_INF_BASE > > 8 ) & 0xff ) , TPM_INF_DATA ) ;
2005-08-05 11:59:33 -07:00
outb ( IOLIML , TPM_INF_ADDR ) ;
2006-04-22 02:38:42 -07:00
outb ( ( TPM_INF_BASE & 0xff ) , TPM_INF_DATA ) ;
2005-08-05 11:59:33 -07:00
/* control if IO-ports are set correctly */
outb ( IOLIMH , TPM_INF_ADDR ) ;
ioh = inb ( TPM_INF_DATA ) ;
outb ( IOLIML , TPM_INF_ADDR ) ;
iol = inb ( TPM_INF_DATA ) ;
2006-04-22 02:38:42 -07:00
if ( ( ioh < < 8 | iol ) ! = TPM_INF_BASE ) {
2005-10-30 15:03:27 -08:00
dev_err ( & dev - > dev ,
2006-04-22 02:38:42 -07:00
" Could not set IO-ports to 0x%x \n " ,
TPM_INF_BASE ) ;
2006-02-17 13:52:41 -08:00
rc = - EIO ;
goto err_release_region ;
2005-07-27 11:45:12 -07:00
}
/* activate register */
2005-08-05 11:59:33 -07:00
outb ( TPM_DAR , TPM_INF_ADDR ) ;
outb ( 0x01 , TPM_INF_DATA ) ;
outb ( DISABLE_REGISTER_PAIR , TPM_INF_ADDR ) ;
2005-07-27 11:45:12 -07:00
/* disable RESET, LP and IRQC */
2006-04-22 02:38:42 -07:00
outb ( RESET_LP_IRQC_DISABLE , TPM_INF_BASE + CMD ) ;
2005-07-27 11:45:12 -07:00
/* Finally, we're done, print some infos */
2005-10-30 15:03:27 -08:00
dev_info ( & dev - > dev , " TPM found: "
2005-08-05 11:59:33 -07:00
" config base 0x%x, "
2005-07-27 11:45:12 -07:00
" io base 0x%x, "
2006-04-22 02:38:42 -07:00
" chip version 0x%02x%02x, "
" vendor id 0x%x%x (Infineon), "
" product id 0x%02x%02x "
2005-07-27 11:45:12 -07:00
" %s \n " ,
2005-08-05 11:59:33 -07:00
TPM_INF_ADDR ,
2005-10-30 15:03:27 -08:00
TPM_INF_BASE ,
2005-07-27 11:45:12 -07:00
version [ 0 ] , version [ 1 ] ,
vendorid [ 0 ] , vendorid [ 1 ] ,
2005-08-05 11:59:33 -07:00
productid [ 0 ] , productid [ 1 ] , chipname ) ;
2005-07-27 11:45:12 -07:00
2006-04-22 02:38:42 -07:00
if ( ! ( chip = tpm_register_hardware ( & dev - > dev , & tpm_inf ) ) ) {
2006-02-17 13:52:41 -08:00
goto err_release_region ;
2005-10-30 15:03:27 -08:00
}
2006-04-22 02:38:42 -07:00
chip - > vendor . base = TPM_INF_BASE ;
2005-07-27 11:45:12 -07:00
return 0 ;
} else {
2006-02-17 13:52:41 -08:00
rc = - ENODEV ;
goto err_release_region ;
2005-07-27 11:45:12 -07:00
}
2006-02-17 13:52:41 -08:00
err_release_region :
2006-04-22 02:38:42 -07:00
release_region ( TPM_INF_BASE , TPM_INF_PORT_LEN ) ;
2006-02-17 13:52:41 -08:00
release_region ( TPM_INF_ADDR , TPM_INF_ADDR_LEN ) ;
err_last :
return rc ;
2005-07-27 11:45:12 -07:00
}
2005-10-30 15:03:27 -08:00
static __devexit void tpm_inf_pnp_remove ( struct pnp_dev * dev )
2005-10-30 15:03:24 -08:00
{
2005-10-30 15:03:27 -08:00
struct tpm_chip * chip = pnp_get_drvdata ( dev ) ;
2005-10-30 15:03:24 -08:00
2005-10-30 15:03:27 -08:00
if ( chip ) {
2006-04-22 02:38:42 -07:00
release_region ( TPM_INF_BASE , TPM_INF_PORT_LEN ) ;
release_region ( TPM_INF_ADDR , TPM_INF_ADDR_LEN ) ;
2005-10-30 15:03:24 -08:00
tpm_remove_hardware ( chip - > dev ) ;
2005-10-30 15:03:27 -08:00
}
2005-10-30 15:03:24 -08:00
}
2005-10-30 15:03:27 -08:00
static struct pnp_driver tpm_inf_pnp = {
. name = " tpm_inf_pnp " ,
. driver = {
. owner = THIS_MODULE ,
. suspend = tpm_pm_suspend ,
. resume = tpm_pm_resume ,
} ,
. id_table = tpm_pnp_tbl ,
. probe = tpm_inf_pnp_probe ,
2006-04-22 02:39:18 -07:00
. remove = __devexit_p ( tpm_inf_pnp_remove ) ,
2005-07-27 11:45:12 -07:00
} ;
static int __init init_inf ( void )
{
2005-10-30 15:03:27 -08:00
return pnp_register_driver ( & tpm_inf_pnp ) ;
2005-07-27 11:45:12 -07:00
}
static void __exit cleanup_inf ( void )
{
2005-10-30 15:03:27 -08:00
pnp_unregister_driver ( & tpm_inf_pnp ) ;
2005-07-27 11:45:12 -07:00
}
module_init ( init_inf ) ;
module_exit ( cleanup_inf ) ;
MODULE_AUTHOR ( " Marcel Selhorst <selhorst@crypto.rub.de> " ) ;
2005-08-05 11:59:33 -07:00
MODULE_DESCRIPTION ( " Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2 " ) ;
2006-04-22 02:38:42 -07:00
MODULE_VERSION ( " 1.8 " ) ;
2005-07-27 11:45:12 -07:00
MODULE_LICENSE ( " GPL " ) ;