2005-04-16 15:20:36 -07: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 >
*
* Maintained by : < tpmdd_devel @ lists . sourceforge . net >
*
* 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"
/* Atmel definitions */
2005-06-23 22:02:06 -07:00
enum tpm_atmel_addr {
TPM_ATMEL_BASE_ADDR_LO = 0x08 ,
TPM_ATMEL_BASE_ADDR_HI = 0x09
2005-06-23 22:01:48 -07:00
} ;
2005-04-16 15:20:36 -07:00
/* write status bits */
2005-06-23 22:01:48 -07:00
enum tpm_atmel_write_status {
ATML_STATUS_ABORT = 0x01 ,
ATML_STATUS_LASTBYTE = 0x04
} ;
2005-04-16 15:20:36 -07:00
/* read status bits */
2005-06-23 22:01:48 -07: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-16 15:20:36 -07:00
static int tpm_atml_recv ( struct tpm_chip * chip , u8 * buf , size_t count )
{
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 + + ) {
status = inb ( chip - > vendor - > base + 1 ) ;
if ( ( status & ATML_STATUS_DATA_AVAIL ) = = 0 ) {
dev_err ( & chip - > pci_dev - > dev ,
" error reading header \n " ) ;
return - EIO ;
}
* buf + + = inb ( chip - > vendor - > base ) ;
}
/* size of the data received */
native_size = ( __force __be32 * ) ( hdr + 2 ) ;
size = be32_to_cpu ( * native_size ) ;
if ( count < size ) {
dev_err ( & chip - > pci_dev - > dev ,
" Recv size(%d) less than available space \n " , size ) ;
for ( ; i < size ; i + + ) { /* clear the waiting data anyway */
status = inb ( chip - > vendor - > base + 1 ) ;
if ( ( status & ATML_STATUS_DATA_AVAIL ) = = 0 ) {
dev_err ( & chip - > pci_dev - > dev ,
" error reading data \n " ) ;
return - EIO ;
}
}
return - EIO ;
}
/* read all the data available */
for ( ; i < size ; i + + ) {
status = inb ( chip - > vendor - > base + 1 ) ;
if ( ( status & ATML_STATUS_DATA_AVAIL ) = = 0 ) {
dev_err ( & chip - > pci_dev - > dev ,
" error reading data \n " ) ;
return - EIO ;
}
* buf + + = inb ( chip - > vendor - > base ) ;
}
/* make sure data available is gone */
status = inb ( chip - > vendor - > base + 1 ) ;
if ( status & ATML_STATUS_DATA_AVAIL ) {
dev_err ( & chip - > pci_dev - > dev , " data available is stuck \n " ) ;
return - EIO ;
}
return size ;
}
static int tpm_atml_send ( struct tpm_chip * chip , u8 * buf , size_t count )
{
int i ;
dev_dbg ( & chip - > pci_dev - > dev , " tpm_atml_send: " ) ;
for ( i = 0 ; i < count ; i + + ) {
dev_dbg ( & chip - > pci_dev - > dev , " 0x%x(%d) " , buf [ i ] , buf [ i ] ) ;
outb ( buf [ i ] , chip - > vendor - > base ) ;
}
return count ;
}
static void tpm_atml_cancel ( struct tpm_chip * chip )
{
outb ( ATML_STATUS_ABORT , chip - > vendor - > base + 1 ) ;
}
static struct file_operations atmel_ops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. open = tpm_open ,
. read = tpm_read ,
. write = tpm_write ,
. release = tpm_release ,
} ;
2005-06-23 22:02:00 -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 * atmel_attrs [ ] = {
& dev_attr_pubek . attr ,
& dev_attr_pcrs . attr ,
& dev_attr_caps . attr ,
& dev_attr_cancel . attr ,
0 ,
} ;
static struct attribute_group atmel_attr_grp = { . attrs = atmel_attrs } ;
2005-04-16 15:20:36 -07:00
static struct tpm_vendor_specific tpm_atmel = {
. recv = tpm_atml_recv ,
. send = tpm_atml_send ,
. cancel = tpm_atml_cancel ,
. req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL ,
. req_complete_val = ATML_STATUS_DATA_AVAIL ,
2005-06-23 22:02:02 -07:00
. req_canceled = ATML_STATUS_READY ,
2005-06-23 22:02:00 -07:00
. attr_group = & atmel_attr_grp ,
2005-04-16 15:20:36 -07:00
. miscdev = { . fops = & atmel_ops , } ,
} ;
static int __devinit tpm_atml_init ( struct pci_dev * pci_dev ,
const struct pci_device_id * pci_id )
{
u8 version [ 4 ] ;
int rc = 0 ;
2005-06-23 22:02:06 -07:00
int lo , hi ;
2005-04-16 15:20:36 -07:00
if ( pci_enable_device ( pci_dev ) )
return - EIO ;
2005-06-25 14:55:39 -07:00
lo = tpm_read_index ( TPM_ADDR , TPM_ATMEL_BASE_ADDR_LO ) ;
hi = tpm_read_index ( TPM_ADDR , TPM_ATMEL_BASE_ADDR_HI ) ;
2005-06-23 22:02:06 -07:00
tpm_atmel . base = ( hi < < 8 ) | lo ;
dev_dbg ( & pci_dev - > dev , " Operating with base: 0x%x \n " , tpm_atmel . base ) ;
2005-04-16 15:20:36 -07:00
/* verify that it is an Atmel part */
2005-06-25 14:55:39 -07:00
if ( tpm_read_index ( TPM_ADDR , 4 ) ! = ' A ' | | tpm_read_index ( TPM_ADDR , 5 ) ! = ' T '
| | tpm_read_index ( TPM_ADDR , 6 ) ! = ' M ' | | tpm_read_index ( TPM_ADDR , 7 ) ! = ' L ' ) {
2005-04-16 15:20:36 -07:00
rc = - ENODEV ;
goto out_err ;
}
/* query chip for its version number */
2005-06-25 14:55:39 -07:00
if ( ( version [ 0 ] = tpm_read_index ( TPM_ADDR , 0x00 ) ) ! = 0xFF ) {
version [ 1 ] = tpm_read_index ( TPM_ADDR , 0x01 ) ;
version [ 2 ] = tpm_read_index ( TPM_ADDR , 0x02 ) ;
version [ 3 ] = tpm_read_index ( TPM_ADDR , 0x03 ) ;
2005-04-16 15:20:36 -07:00
} else {
dev_info ( & pci_dev - > dev , " version query failed \n " ) ;
rc = - ENODEV ;
goto out_err ;
}
if ( ( rc = tpm_register_hardware ( pci_dev , & tpm_atmel ) ) < 0 )
goto out_err ;
dev_info ( & pci_dev - > dev ,
" Atmel TPM version %d.%d.%d.%d \n " , version [ 0 ] , version [ 1 ] ,
version [ 2 ] , version [ 3 ] ) ;
return 0 ;
out_err :
pci_disable_device ( pci_dev ) ;
return rc ;
}
static struct pci_device_id tpm_pci_tbl [ ] __devinitdata = {
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82801BA_0 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82801CA_12 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82801DB_0 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82801DB_12 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82801EB_0 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_AMD , PCI_DEVICE_ID_AMD_8111_LPC ) } ,
2005-06-23 22:02:04 -07:00
{ PCI_DEVICE ( PCI_VENDOR_ID_SERVERWORKS , PCI_DEVICE_ID_SERVERWORKS_CSB6LPC ) } ,
2005-04-16 15:20:36 -07:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , tpm_pci_tbl ) ;
static struct pci_driver atmel_pci_driver = {
. name = " tpm_atmel " ,
. id_table = tpm_pci_tbl ,
. probe = tpm_atml_init ,
. remove = __devexit_p ( tpm_remove ) ,
. suspend = tpm_pm_suspend ,
. resume = tpm_pm_resume ,
} ;
static int __init init_atmel ( void )
{
return pci_register_driver ( & atmel_pci_driver ) ;
}
static void __exit cleanup_atmel ( void )
{
pci_unregister_driver ( & atmel_pci_driver ) ;
}
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 " ) ;