1 /* 2 * Copyright (C) 2004 IBM Corporation 3 * 4 * Authors: 5 * Leendert van Doorn <leendert@watson.ibm.com> 6 * Dave Safford <safford@watson.ibm.com> 7 * Reiner Sailer <sailer@watson.ibm.com> 8 * Kylene Hall <kjhall@us.ibm.com> 9 * 10 * Maintained by: <tpmdd_devel@lists.sourceforge.net> 11 * 12 * Device driver for TCG/TCPA TPM (trusted platform module). 13 * Specifications at www.trustedcomputinggroup.org 14 * 15 * This program is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU General Public License as 17 * published by the Free Software Foundation, version 2 of the 18 * License. 19 * 20 */ 21 22 #include "tpm.h" 23 24 /* Atmel definitions */ 25 #define TPM_ATML_BASE 0x400 26 27 /* write status bits */ 28 #define ATML_STATUS_ABORT 0x01 29 #define ATML_STATUS_LASTBYTE 0x04 30 31 /* read status bits */ 32 #define ATML_STATUS_BUSY 0x01 33 #define ATML_STATUS_DATA_AVAIL 0x02 34 #define ATML_STATUS_REWRITE 0x04 35 36 37 static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count) 38 { 39 u8 status, *hdr = buf; 40 u32 size; 41 int i; 42 __be32 *native_size; 43 44 /* start reading header */ 45 if (count < 6) 46 return -EIO; 47 48 for (i = 0; i < 6; i++) { 49 status = inb(chip->vendor->base + 1); 50 if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 51 dev_err(&chip->pci_dev->dev, 52 "error reading header\n"); 53 return -EIO; 54 } 55 *buf++ = inb(chip->vendor->base); 56 } 57 58 /* size of the data received */ 59 native_size = (__force __be32 *) (hdr + 2); 60 size = be32_to_cpu(*native_size); 61 62 if (count < size) { 63 dev_err(&chip->pci_dev->dev, 64 "Recv size(%d) less than available space\n", size); 65 for (; i < size; i++) { /* clear the waiting data anyway */ 66 status = inb(chip->vendor->base + 1); 67 if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 68 dev_err(&chip->pci_dev->dev, 69 "error reading data\n"); 70 return -EIO; 71 } 72 } 73 return -EIO; 74 } 75 76 /* read all the data available */ 77 for (; i < size; i++) { 78 status = inb(chip->vendor->base + 1); 79 if ((status & ATML_STATUS_DATA_AVAIL) == 0) { 80 dev_err(&chip->pci_dev->dev, 81 "error reading data\n"); 82 return -EIO; 83 } 84 *buf++ = inb(chip->vendor->base); 85 } 86 87 /* make sure data available is gone */ 88 status = inb(chip->vendor->base + 1); 89 if (status & ATML_STATUS_DATA_AVAIL) { 90 dev_err(&chip->pci_dev->dev, "data available is stuck\n"); 91 return -EIO; 92 } 93 94 return size; 95 } 96 97 static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count) 98 { 99 int i; 100 101 dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: "); 102 for (i = 0; i < count; i++) { 103 dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]); 104 outb(buf[i], chip->vendor->base); 105 } 106 107 return count; 108 } 109 110 static void tpm_atml_cancel(struct tpm_chip *chip) 111 { 112 outb(ATML_STATUS_ABORT, chip->vendor->base + 1); 113 } 114 115 static struct file_operations atmel_ops = { 116 .owner = THIS_MODULE, 117 .llseek = no_llseek, 118 .open = tpm_open, 119 .read = tpm_read, 120 .write = tpm_write, 121 .release = tpm_release, 122 }; 123 124 static struct tpm_vendor_specific tpm_atmel = { 125 .recv = tpm_atml_recv, 126 .send = tpm_atml_send, 127 .cancel = tpm_atml_cancel, 128 .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, 129 .req_complete_val = ATML_STATUS_DATA_AVAIL, 130 .base = TPM_ATML_BASE, 131 .miscdev = { .fops = &atmel_ops, }, 132 }; 133 134 static int __devinit tpm_atml_init(struct pci_dev *pci_dev, 135 const struct pci_device_id *pci_id) 136 { 137 u8 version[4]; 138 int rc = 0; 139 140 if (pci_enable_device(pci_dev)) 141 return -EIO; 142 143 if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) { 144 rc = -ENODEV; 145 goto out_err; 146 } 147 148 /* verify that it is an Atmel part */ 149 if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T' 150 || tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') { 151 rc = -ENODEV; 152 goto out_err; 153 } 154 155 /* query chip for its version number */ 156 if ((version[0] = tpm_read_index(0x00)) != 0xFF) { 157 version[1] = tpm_read_index(0x01); 158 version[2] = tpm_read_index(0x02); 159 version[3] = tpm_read_index(0x03); 160 } else { 161 dev_info(&pci_dev->dev, "version query failed\n"); 162 rc = -ENODEV; 163 goto out_err; 164 } 165 166 if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0) 167 goto out_err; 168 169 dev_info(&pci_dev->dev, 170 "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1], 171 version[2], version[3]); 172 173 return 0; 174 out_err: 175 pci_disable_device(pci_dev); 176 return rc; 177 } 178 179 static struct pci_device_id tpm_pci_tbl[] __devinitdata = { 180 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)}, 181 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)}, 182 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)}, 183 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)}, 184 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)}, 185 {PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)}, 186 {0,} 187 }; 188 189 MODULE_DEVICE_TABLE(pci, tpm_pci_tbl); 190 191 static struct pci_driver atmel_pci_driver = { 192 .name = "tpm_atmel", 193 .id_table = tpm_pci_tbl, 194 .probe = tpm_atml_init, 195 .remove = __devexit_p(tpm_remove), 196 .suspend = tpm_pm_suspend, 197 .resume = tpm_pm_resume, 198 }; 199 200 static int __init init_atmel(void) 201 { 202 return pci_register_driver(&atmel_pci_driver); 203 } 204 205 static void __exit cleanup_atmel(void) 206 { 207 pci_unregister_driver(&atmel_pci_driver); 208 } 209 210 module_init(init_atmel); 211 module_exit(cleanup_atmel); 212 213 MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); 214 MODULE_DESCRIPTION("TPM Driver"); 215 MODULE_VERSION("2.0"); 216 MODULE_LICENSE("GPL"); 217