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