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