blob: dc8c540391fdc690f2f30c0a69a6f586794d4c86 [file] [log] [blame]
Marcel Selhorstebb81fd2005-07-27 11:45:12 -07001/*
2 * Description:
3 * Device Driver for the Infineon Technologies
Marcel Selhorstf9abb022005-08-05 11:59:33 -07004 * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module
Marcel Selhorstebb81fd2005-07-27 11:45:12 -07005 * Specifications at www.trustedcomputinggroup.org
6 *
7 * Copyright (C) 2005, Marcel Selhorst <selhorst@crypto.rub.de>
8 * Applied Data Security Group, Ruhr-University Bochum, Germany
9 * Project-Homepage: http://www.prosec.rub.de/tpm
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation, version 2 of the
14 * License.
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070015 */
16
Marcel Selhorstf9abb022005-08-05 11:59:33 -070017#include <acpi/acpi_bus.h>
18#include <linux/pnp.h>
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070019#include "tpm.h"
20
21/* Infineon specific definitions */
22/* maximum number of WTX-packages */
23#define TPM_MAX_WTX_PACKAGES 50
24/* msleep-Time for WTX-packages */
25#define TPM_WTX_MSLEEP_TIME 20
26/* msleep-Time --> Interval to check status register */
27#define TPM_MSLEEP_TIME 3
28/* gives number of max. msleep()-calls before throwing timeout */
29#define TPM_MAX_TRIES 5000
Marcel Selhorstf9abb022005-08-05 11:59:33 -070030#define TPM_INFINEON_DEV_VEN_VALUE 0x15D1
31
32/* These values will be filled after ACPI-call */
33static int TPM_INF_DATA = 0;
34static int TPM_INF_ADDR = 0;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070035
36/* TPM header definitions */
37enum infineon_tpm_header {
38 TPM_VL_VER = 0x01,
39 TPM_VL_CHANNEL_CONTROL = 0x07,
40 TPM_VL_CHANNEL_PERSONALISATION = 0x0A,
41 TPM_VL_CHANNEL_TPM = 0x0B,
42 TPM_VL_CONTROL = 0x00,
43 TPM_INF_NAK = 0x15,
44 TPM_CTRL_WTX = 0x10,
45 TPM_CTRL_WTX_ABORT = 0x18,
46 TPM_CTRL_WTX_ABORT_ACK = 0x18,
47 TPM_CTRL_ERROR = 0x20,
48 TPM_CTRL_CHAININGACK = 0x40,
49 TPM_CTRL_CHAINING = 0x80,
50 TPM_CTRL_DATA = 0x04,
51 TPM_CTRL_DATA_CHA = 0x84,
52 TPM_CTRL_DATA_CHA_ACK = 0xC4
53};
54
55enum infineon_tpm_register {
56 WRFIFO = 0x00,
57 RDFIFO = 0x01,
58 STAT = 0x02,
59 CMD = 0x03
60};
61
62enum infineon_tpm_command_bits {
63 CMD_DIS = 0x00,
64 CMD_LP = 0x01,
65 CMD_RES = 0x02,
66 CMD_IRQC = 0x06
67};
68
69enum infineon_tpm_status_bits {
70 STAT_XFE = 0x00,
71 STAT_LPA = 0x01,
72 STAT_FOK = 0x02,
73 STAT_TOK = 0x03,
74 STAT_IRQA = 0x06,
75 STAT_RDA = 0x07
76};
77
78/* some outgoing values */
79enum infineon_tpm_values {
80 CHIP_ID1 = 0x20,
81 CHIP_ID2 = 0x21,
Andrew Morton3dcce8e2005-07-27 11:45:14 -070082 TPM_DAR = 0x30,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070083 RESET_LP_IRQC_DISABLE = 0x41,
84 ENABLE_REGISTER_PAIR = 0x55,
85 IOLIMH = 0x60,
86 IOLIML = 0x61,
87 DISABLE_REGISTER_PAIR = 0xAA,
88 IDVENL = 0xF1,
89 IDVENH = 0xF2,
90 IDPDL = 0xF3,
91 IDPDH = 0xF4
92};
93
94static int number_of_wtx;
95
96static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo)
97{
98 int status;
99 int check = 0;
100 int i;
101
102 if (clear_wrfifo) {
103 for (i = 0; i < 4096; i++) {
104 status = inb(chip->vendor->base + WRFIFO);
105 if (status == 0xff) {
106 if (check == 5)
107 break;
108 else
109 check++;
110 }
111 }
112 }
113 /* Note: The values which are currently in the FIFO of the TPM
114 are thrown away since there is no usage for them. Usually,
115 this has nothing to say, since the TPM will give its answer
116 immediately or will be aborted anyway, so the data here is
117 usually garbage and useless.
118 We have to clean this, because the next communication with
119 the TPM would be rubbish, if there is still some old data
120 in the Read FIFO.
121 */
122 i = 0;
123 do {
124 status = inb(chip->vendor->base + RDFIFO);
125 status = inb(chip->vendor->base + STAT);
126 i++;
127 if (i == TPM_MAX_TRIES)
128 return -EIO;
129 } while ((status & (1 << STAT_RDA)) != 0);
130 return 0;
131}
132
133static int wait(struct tpm_chip *chip, int wait_for_bit)
134{
135 int status;
136 int i;
137 for (i = 0; i < TPM_MAX_TRIES; i++) {
138 status = inb(chip->vendor->base + STAT);
139 /* check the status-register if wait_for_bit is set */
140 if (status & 1 << wait_for_bit)
141 break;
142 msleep(TPM_MSLEEP_TIME);
143 }
144 if (i == TPM_MAX_TRIES) { /* timeout occurs */
145 if (wait_for_bit == STAT_XFE)
146 dev_err(&chip->pci_dev->dev,
147 "Timeout in wait(STAT_XFE)\n");
148 if (wait_for_bit == STAT_RDA)
149 dev_err(&chip->pci_dev->dev,
150 "Timeout in wait(STAT_RDA)\n");
151 return -EIO;
152 }
153 return 0;
154};
155
156static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
157{
158 wait(chip, STAT_XFE);
159 outb(sendbyte, chip->vendor->base + WRFIFO);
160}
161
162 /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more
163 calculation time, it sends a WTX-package, which has to be acknowledged
164 or aborted. This usually occurs if you are hammering the TPM with key
165 creation. Set the maximum number of WTX-packages in the definitions
166 above, if the number is reached, the waiting-time will be denied
167 and the TPM command has to be resend.
168 */
169
170static void tpm_wtx(struct tpm_chip *chip)
171{
172 number_of_wtx++;
173 dev_info(&chip->pci_dev->dev, "Granting WTX (%02d / %02d)\n",
174 number_of_wtx, TPM_MAX_WTX_PACKAGES);
175 wait_and_send(chip, TPM_VL_VER);
176 wait_and_send(chip, TPM_CTRL_WTX);
177 wait_and_send(chip, 0x00);
178 wait_and_send(chip, 0x00);
179 msleep(TPM_WTX_MSLEEP_TIME);
180}
181
182static void tpm_wtx_abort(struct tpm_chip *chip)
183{
184 dev_info(&chip->pci_dev->dev, "Aborting WTX\n");
185 wait_and_send(chip, TPM_VL_VER);
186 wait_and_send(chip, TPM_CTRL_WTX_ABORT);
187 wait_and_send(chip, 0x00);
188 wait_and_send(chip, 0x00);
189 number_of_wtx = 0;
190 msleep(TPM_WTX_MSLEEP_TIME);
191}
192
193static int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count)
194{
195 int i;
196 int ret;
197 u32 size = 0;
198
199recv_begin:
200 /* start receiving header */
201 for (i = 0; i < 4; i++) {
202 ret = wait(chip, STAT_RDA);
203 if (ret)
204 return -EIO;
205 buf[i] = inb(chip->vendor->base + RDFIFO);
206 }
207
208 if (buf[0] != TPM_VL_VER) {
209 dev_err(&chip->pci_dev->dev,
210 "Wrong transport protocol implementation!\n");
211 return -EIO;
212 }
213
214 if (buf[1] == TPM_CTRL_DATA) {
215 /* size of the data received */
216 size = ((buf[2] << 8) | buf[3]);
217
218 for (i = 0; i < size; i++) {
219 wait(chip, STAT_RDA);
220 buf[i] = inb(chip->vendor->base + RDFIFO);
221 }
222
223 if ((size == 0x6D00) && (buf[1] == 0x80)) {
224 dev_err(&chip->pci_dev->dev,
225 "Error handling on vendor layer!\n");
226 return -EIO;
227 }
228
229 for (i = 0; i < size; i++)
230 buf[i] = buf[i + 6];
231
232 size = size - 6;
233 return size;
234 }
235
236 if (buf[1] == TPM_CTRL_WTX) {
237 dev_info(&chip->pci_dev->dev, "WTX-package received\n");
238 if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
239 tpm_wtx(chip);
240 goto recv_begin;
241 } else {
242 tpm_wtx_abort(chip);
243 goto recv_begin;
244 }
245 }
246
247 if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
248 dev_info(&chip->pci_dev->dev, "WTX-abort acknowledged\n");
249 return size;
250 }
251
252 if (buf[1] == TPM_CTRL_ERROR) {
253 dev_err(&chip->pci_dev->dev, "ERROR-package received:\n");
254 if (buf[4] == TPM_INF_NAK)
255 dev_err(&chip->pci_dev->dev,
256 "-> Negative acknowledgement"
257 " - retransmit command!\n");
258 return -EIO;
259 }
260 return -EIO;
261}
262
263static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
264{
265 int i;
266 int ret;
267 u8 count_high, count_low, count_4, count_3, count_2, count_1;
268
269 /* Disabling Reset, LP and IRQC */
270 outb(RESET_LP_IRQC_DISABLE, chip->vendor->base + CMD);
271
272 ret = empty_fifo(chip, 1);
273 if (ret) {
274 dev_err(&chip->pci_dev->dev, "Timeout while clearing FIFO\n");
275 return -EIO;
276 }
277
278 ret = wait(chip, STAT_XFE);
279 if (ret)
280 return -EIO;
281
282 count_4 = (count & 0xff000000) >> 24;
283 count_3 = (count & 0x00ff0000) >> 16;
284 count_2 = (count & 0x0000ff00) >> 8;
285 count_1 = (count & 0x000000ff);
286 count_high = ((count + 6) & 0xffffff00) >> 8;
287 count_low = ((count + 6) & 0x000000ff);
288
289 /* Sending Header */
290 wait_and_send(chip, TPM_VL_VER);
291 wait_and_send(chip, TPM_CTRL_DATA);
292 wait_and_send(chip, count_high);
293 wait_and_send(chip, count_low);
294
295 /* Sending Data Header */
296 wait_and_send(chip, TPM_VL_VER);
297 wait_and_send(chip, TPM_VL_CHANNEL_TPM);
298 wait_and_send(chip, count_4);
299 wait_and_send(chip, count_3);
300 wait_and_send(chip, count_2);
301 wait_and_send(chip, count_1);
302
303 /* Sending Data */
304 for (i = 0; i < count; i++) {
305 wait_and_send(chip, buf[i]);
306 }
307 return count;
308}
309
310static void tpm_inf_cancel(struct tpm_chip *chip)
311{
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700312 /*
313 Since we are using the legacy mode to communicate
314 with the TPM, we have no cancel functions, but have
315 a workaround for interrupting the TPM through WTX.
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700316 */
317}
318
319static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
320static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
321static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
322static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
323
324static struct attribute *inf_attrs[] = {
325 &dev_attr_pubek.attr,
326 &dev_attr_pcrs.attr,
327 &dev_attr_caps.attr,
328 &dev_attr_cancel.attr,
329 NULL,
330};
331
332static struct attribute_group inf_attr_grp = {.attrs = inf_attrs };
333
334static struct file_operations inf_ops = {
335 .owner = THIS_MODULE,
336 .llseek = no_llseek,
337 .open = tpm_open,
338 .read = tpm_read,
339 .write = tpm_write,
340 .release = tpm_release,
341};
342
343static struct tpm_vendor_specific tpm_inf = {
344 .recv = tpm_inf_recv,
345 .send = tpm_inf_send,
346 .cancel = tpm_inf_cancel,
347 .req_complete_mask = 0,
348 .req_complete_val = 0,
349 .attr_group = &inf_attr_grp,
350 .miscdev = {.fops = &inf_ops,},
351};
352
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700353static const struct pnp_device_id tpm_pnp_tbl[] = {
354 /* Infineon TPMs */
355 {"IFX0101", 0},
356 {"IFX0102", 0},
357 {"", 0}
358};
359
360static int __devinit tpm_inf_acpi_probe(struct pnp_dev *dev,
361 const struct pnp_device_id *dev_id)
362{
363 TPM_INF_ADDR = (pnp_port_start(dev, 0) & 0xff);
364 TPM_INF_DATA = ((TPM_INF_ADDR + 1) & 0xff);
365 tpm_inf.base = pnp_port_start(dev, 1);
366 dev_info(&dev->dev, "Found %s with ID %s\n",
367 dev->name, dev_id->id);
368 if (!((tpm_inf.base >> 8) & 0xff))
369 tpm_inf.base = 0;
370 return 0;
371}
372
373static struct pnp_driver tpm_inf_pnp = {
374 .name = "tpm_inf_pnp",
375 .id_table = tpm_pnp_tbl,
376 .probe = tpm_inf_acpi_probe,
377};
378
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700379static int __devinit tpm_inf_probe(struct pci_dev *pci_dev,
380 const struct pci_device_id *pci_id)
381{
382 int rc = 0;
383 u8 iol, ioh;
384 int vendorid[2];
385 int version[2];
386 int productid[2];
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700387 char chipname[20];
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700388
389 if (pci_enable_device(pci_dev))
390 return -EIO;
391
392 dev_info(&pci_dev->dev, "LPC-bus found at 0x%x\n", pci_id->device);
393
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700394 /* read IO-ports from ACPI */
395 pnp_register_driver(&tpm_inf_pnp);
396 pnp_unregister_driver(&tpm_inf_pnp);
397
398 /* Make sure, we have received valid config ports */
399 if (!TPM_INF_ADDR) {
400 pci_disable_device(pci_dev);
401 return -EIO;
402 }
403
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700404 /* query chip for its vendor, its version number a.s.o. */
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700405 outb(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
406 outb(IDVENL, TPM_INF_ADDR);
407 vendorid[1] = inb(TPM_INF_DATA);
408 outb(IDVENH, TPM_INF_ADDR);
409 vendorid[0] = inb(TPM_INF_DATA);
410 outb(IDPDL, TPM_INF_ADDR);
411 productid[1] = inb(TPM_INF_DATA);
412 outb(IDPDH, TPM_INF_ADDR);
413 productid[0] = inb(TPM_INF_DATA);
414 outb(CHIP_ID1, TPM_INF_ADDR);
415 version[1] = inb(TPM_INF_DATA);
416 outb(CHIP_ID2, TPM_INF_ADDR);
417 version[0] = inb(TPM_INF_DATA);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700418
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700419 switch ((productid[0] << 8) | productid[1]) {
420 case 6:
421 sprintf(chipname, " (SLD 9630 TT 1.1)");
422 break;
423 case 11:
424 sprintf(chipname, " (SLB 9635 TT 1.2)");
425 break;
426 default:
427 sprintf(chipname, " (unknown chip)");
428 break;
429 }
430 chipname[19] = 0;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700431
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700432 if ((vendorid[0] << 8 | vendorid[1]) == (TPM_INFINEON_DEV_VEN_VALUE)) {
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700433
434 if (tpm_inf.base == 0) {
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700435 dev_err(&pci_dev->dev, "No IO-ports found!\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700436 pci_disable_device(pci_dev);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700437 return -EIO;
438 }
439 /* configure TPM with IO-ports */
440 outb(IOLIMH, TPM_INF_ADDR);
441 outb(((tpm_inf.base >> 8) & 0xff), TPM_INF_DATA);
442 outb(IOLIML, TPM_INF_ADDR);
443 outb((tpm_inf.base & 0xff), TPM_INF_DATA);
444
445 /* control if IO-ports are set correctly */
446 outb(IOLIMH, TPM_INF_ADDR);
447 ioh = inb(TPM_INF_DATA);
448 outb(IOLIML, TPM_INF_ADDR);
449 iol = inb(TPM_INF_DATA);
450
451 if ((ioh << 8 | iol) != tpm_inf.base) {
452 dev_err(&pci_dev->dev,
453 "Could not set IO-ports to %04x\n",
454 tpm_inf.base);
455 pci_disable_device(pci_dev);
456 return -EIO;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700457 }
458
459 /* activate register */
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700460 outb(TPM_DAR, TPM_INF_ADDR);
461 outb(0x01, TPM_INF_DATA);
462 outb(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700463
464 /* disable RESET, LP and IRQC */
465 outb(RESET_LP_IRQC_DISABLE, tpm_inf.base + CMD);
466
467 /* Finally, we're done, print some infos */
468 dev_info(&pci_dev->dev, "TPM found: "
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700469 "config base 0x%x, "
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700470 "io base 0x%x, "
471 "chip version %02x%02x, "
472 "vendor id %x%x (Infineon), "
473 "product id %02x%02x"
474 "%s\n",
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700475 TPM_INF_ADDR,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700476 tpm_inf.base,
477 version[0], version[1],
478 vendorid[0], vendorid[1],
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700479 productid[0], productid[1], chipname);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700480
481 rc = tpm_register_hardware(pci_dev, &tpm_inf);
482 if (rc < 0) {
483 pci_disable_device(pci_dev);
484 return -ENODEV;
485 }
486 return 0;
487 } else {
488 dev_info(&pci_dev->dev, "No Infineon TPM found!\n");
489 pci_disable_device(pci_dev);
490 return -ENODEV;
491 }
492}
493
494static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
495 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
496 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
497 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
498 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
499 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
500 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0)},
501 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1)},
502 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2)},
503 {0,}
504};
505
506MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
507
508static struct pci_driver inf_pci_driver = {
509 .name = "tpm_inf",
510 .id_table = tpm_pci_tbl,
511 .probe = tpm_inf_probe,
512 .remove = __devexit_p(tpm_remove),
513 .suspend = tpm_pm_suspend,
514 .resume = tpm_pm_resume,
515};
516
517static int __init init_inf(void)
518{
519 return pci_register_driver(&inf_pci_driver);
520}
521
522static void __exit cleanup_inf(void)
523{
524 pci_unregister_driver(&inf_pci_driver);
525}
526
527module_init(init_inf);
528module_exit(cleanup_inf);
529
530MODULE_AUTHOR("Marcel Selhorst <selhorst@crypto.rub.de>");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700531MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");
532MODULE_VERSION("1.5");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700533MODULE_LICENSE("GPL");