blob: 8198dbb7370f1596a3b55eed4c7ac7cbaa429408 [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>
Marcel Selhorst1b8333b2005-10-30 15:03:27 -08008 * Sirrix AG - security technologies, http://www.sirrix.com and
Marcel Selhorstebb81fd2005-07-27 11:45:12 -07009 * 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.
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070016 */
17
Marcel Selhorstf9abb022005-08-05 11:59:33 -070018#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
Marcel Selhorste8a65012005-09-03 15:54:20 -070032/* These values will be filled after PnP-call */
Andrew Mortonb888c872005-10-30 15:03:28 -080033static int TPM_INF_DATA;
34static int TPM_INF_ADDR;
35static int TPM_INF_BASE;
36static int TPM_INF_PORT_LEN;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070037
38/* TPM header definitions */
39enum infineon_tpm_header {
40 TPM_VL_VER = 0x01,
41 TPM_VL_CHANNEL_CONTROL = 0x07,
42 TPM_VL_CHANNEL_PERSONALISATION = 0x0A,
43 TPM_VL_CHANNEL_TPM = 0x0B,
44 TPM_VL_CONTROL = 0x00,
45 TPM_INF_NAK = 0x15,
46 TPM_CTRL_WTX = 0x10,
47 TPM_CTRL_WTX_ABORT = 0x18,
48 TPM_CTRL_WTX_ABORT_ACK = 0x18,
49 TPM_CTRL_ERROR = 0x20,
50 TPM_CTRL_CHAININGACK = 0x40,
51 TPM_CTRL_CHAINING = 0x80,
52 TPM_CTRL_DATA = 0x04,
53 TPM_CTRL_DATA_CHA = 0x84,
54 TPM_CTRL_DATA_CHA_ACK = 0xC4
55};
56
57enum infineon_tpm_register {
58 WRFIFO = 0x00,
59 RDFIFO = 0x01,
60 STAT = 0x02,
61 CMD = 0x03
62};
63
64enum infineon_tpm_command_bits {
65 CMD_DIS = 0x00,
66 CMD_LP = 0x01,
67 CMD_RES = 0x02,
68 CMD_IRQC = 0x06
69};
70
71enum infineon_tpm_status_bits {
72 STAT_XFE = 0x00,
73 STAT_LPA = 0x01,
74 STAT_FOK = 0x02,
75 STAT_TOK = 0x03,
76 STAT_IRQA = 0x06,
77 STAT_RDA = 0x07
78};
79
80/* some outgoing values */
81enum infineon_tpm_values {
82 CHIP_ID1 = 0x20,
83 CHIP_ID2 = 0x21,
Andrew Morton3dcce8e2005-07-27 11:45:14 -070084 TPM_DAR = 0x30,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070085 RESET_LP_IRQC_DISABLE = 0x41,
86 ENABLE_REGISTER_PAIR = 0x55,
87 IOLIMH = 0x60,
88 IOLIML = 0x61,
89 DISABLE_REGISTER_PAIR = 0xAA,
90 IDVENL = 0xF1,
91 IDVENH = 0xF2,
92 IDPDL = 0xF3,
93 IDPDH = 0xF4
94};
95
96static int number_of_wtx;
97
98static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo)
99{
100 int status;
101 int check = 0;
102 int i;
103
104 if (clear_wrfifo) {
105 for (i = 0; i < 4096; i++) {
106 status = inb(chip->vendor->base + WRFIFO);
107 if (status == 0xff) {
108 if (check == 5)
109 break;
110 else
111 check++;
112 }
113 }
114 }
115 /* Note: The values which are currently in the FIFO of the TPM
116 are thrown away since there is no usage for them. Usually,
117 this has nothing to say, since the TPM will give its answer
118 immediately or will be aborted anyway, so the data here is
119 usually garbage and useless.
120 We have to clean this, because the next communication with
121 the TPM would be rubbish, if there is still some old data
122 in the Read FIFO.
123 */
124 i = 0;
125 do {
126 status = inb(chip->vendor->base + RDFIFO);
127 status = inb(chip->vendor->base + STAT);
128 i++;
129 if (i == TPM_MAX_TRIES)
130 return -EIO;
131 } while ((status & (1 << STAT_RDA)) != 0);
132 return 0;
133}
134
135static int wait(struct tpm_chip *chip, int wait_for_bit)
136{
137 int status;
138 int i;
139 for (i = 0; i < TPM_MAX_TRIES; i++) {
140 status = inb(chip->vendor->base + STAT);
141 /* check the status-register if wait_for_bit is set */
142 if (status & 1 << wait_for_bit)
143 break;
144 msleep(TPM_MSLEEP_TIME);
145 }
146 if (i == TPM_MAX_TRIES) { /* timeout occurs */
147 if (wait_for_bit == STAT_XFE)
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800148 dev_err(chip->dev, "Timeout in wait(STAT_XFE)\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700149 if (wait_for_bit == STAT_RDA)
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800150 dev_err(chip->dev, "Timeout in wait(STAT_RDA)\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700151 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++;
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800173 dev_info(chip->dev, "Granting WTX (%02d / %02d)\n",
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700174 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{
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800184 dev_info(chip->dev, "Aborting WTX\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700185 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) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800209 dev_err(chip->dev,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700210 "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)) {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800224 dev_err(chip->dev, "Error handling on vendor layer!\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700225 return -EIO;
226 }
227
228 for (i = 0; i < size; i++)
229 buf[i] = buf[i + 6];
230
231 size = size - 6;
232 return size;
233 }
234
235 if (buf[1] == TPM_CTRL_WTX) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800236 dev_info(chip->dev, "WTX-package received\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700237 if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
238 tpm_wtx(chip);
239 goto recv_begin;
240 } else {
241 tpm_wtx_abort(chip);
242 goto recv_begin;
243 }
244 }
245
246 if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800247 dev_info(chip->dev, "WTX-abort acknowledged\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700248 return size;
249 }
250
251 if (buf[1] == TPM_CTRL_ERROR) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800252 dev_err(chip->dev, "ERROR-package received:\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700253 if (buf[4] == TPM_INF_NAK)
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800254 dev_err(chip->dev,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700255 "-> Negative acknowledgement"
256 " - retransmit command!\n");
257 return -EIO;
258 }
259 return -EIO;
260}
261
262static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
263{
264 int i;
265 int ret;
266 u8 count_high, count_low, count_4, count_3, count_2, count_1;
267
268 /* Disabling Reset, LP and IRQC */
269 outb(RESET_LP_IRQC_DISABLE, chip->vendor->base + CMD);
270
271 ret = empty_fifo(chip, 1);
272 if (ret) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800273 dev_err(chip->dev, "Timeout while clearing FIFO\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700274 return -EIO;
275 }
276
277 ret = wait(chip, STAT_XFE);
278 if (ret)
279 return -EIO;
280
281 count_4 = (count & 0xff000000) >> 24;
282 count_3 = (count & 0x00ff0000) >> 16;
283 count_2 = (count & 0x0000ff00) >> 8;
284 count_1 = (count & 0x000000ff);
285 count_high = ((count + 6) & 0xffffff00) >> 8;
286 count_low = ((count + 6) & 0x000000ff);
287
288 /* Sending Header */
289 wait_and_send(chip, TPM_VL_VER);
290 wait_and_send(chip, TPM_CTRL_DATA);
291 wait_and_send(chip, count_high);
292 wait_and_send(chip, count_low);
293
294 /* Sending Data Header */
295 wait_and_send(chip, TPM_VL_VER);
296 wait_and_send(chip, TPM_VL_CHANNEL_TPM);
297 wait_and_send(chip, count_4);
298 wait_and_send(chip, count_3);
299 wait_and_send(chip, count_2);
300 wait_and_send(chip, count_1);
301
302 /* Sending Data */
303 for (i = 0; i < count; i++) {
304 wait_and_send(chip, buf[i]);
305 }
306 return count;
307}
308
309static void tpm_inf_cancel(struct tpm_chip *chip)
310{
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700311 /*
312 Since we are using the legacy mode to communicate
313 with the TPM, we have no cancel functions, but have
314 a workaround for interrupting the TPM through WTX.
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700315 */
316}
317
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800318static u8 tpm_inf_status(struct tpm_chip *chip)
319{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800320 return inb(chip->vendor->base + STAT);
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800321}
322
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700323static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
324static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
325static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
326static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
327
328static struct attribute *inf_attrs[] = {
329 &dev_attr_pubek.attr,
330 &dev_attr_pcrs.attr,
331 &dev_attr_caps.attr,
332 &dev_attr_cancel.attr,
333 NULL,
334};
335
336static struct attribute_group inf_attr_grp = {.attrs = inf_attrs };
337
338static struct file_operations inf_ops = {
339 .owner = THIS_MODULE,
340 .llseek = no_llseek,
341 .open = tpm_open,
342 .read = tpm_read,
343 .write = tpm_write,
344 .release = tpm_release,
345};
346
347static struct tpm_vendor_specific tpm_inf = {
348 .recv = tpm_inf_recv,
349 .send = tpm_inf_send,
350 .cancel = tpm_inf_cancel,
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800351 .status = tpm_inf_status,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700352 .req_complete_mask = 0,
353 .req_complete_val = 0,
354 .attr_group = &inf_attr_grp,
355 .miscdev = {.fops = &inf_ops,},
356};
357
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700358static const struct pnp_device_id tpm_pnp_tbl[] = {
359 /* Infineon TPMs */
360 {"IFX0101", 0},
361 {"IFX0102", 0},
362 {"", 0}
363};
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800364
Marcel Selhorste8a65012005-09-03 15:54:20 -0700365MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700366
Marcel Selhorste8a65012005-09-03 15:54:20 -0700367static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev,
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800368 const struct pnp_device_id *dev_id)
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700369{
370 int rc = 0;
371 u8 iol, ioh;
372 int vendorid[2];
373 int version[2];
374 int productid[2];
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700375 char chipname[20];
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700376
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800377 /* read IO-ports through PnP */
378 if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) &&
379 !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
380 TPM_INF_ADDR = pnp_port_start(dev, 0);
381 TPM_INF_DATA = (TPM_INF_ADDR + 1);
382 TPM_INF_BASE = pnp_port_start(dev, 1);
383 TPM_INF_PORT_LEN = pnp_port_len(dev, 1);
384 if (!TPM_INF_PORT_LEN)
385 return -EINVAL;
386 dev_info(&dev->dev, "Found %s with ID %s\n",
387 dev->name, dev_id->id);
388 if (!((TPM_INF_BASE >> 8) & 0xff))
389 return -EINVAL;
390 /* publish my base address and request region */
391 tpm_inf.base = TPM_INF_BASE;
392 if (request_region
393 (tpm_inf.base, TPM_INF_PORT_LEN, "tpm_infineon0") == NULL) {
394 release_region(tpm_inf.base, TPM_INF_PORT_LEN);
395 return -EINVAL;
396 }
Marcel Selhorste8a65012005-09-03 15:54:20 -0700397 } else {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800398 return -EINVAL;
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700399 }
400
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700401 /* query chip for its vendor, its version number a.s.o. */
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700402 outb(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
403 outb(IDVENL, TPM_INF_ADDR);
404 vendorid[1] = inb(TPM_INF_DATA);
405 outb(IDVENH, TPM_INF_ADDR);
406 vendorid[0] = inb(TPM_INF_DATA);
407 outb(IDPDL, TPM_INF_ADDR);
408 productid[1] = inb(TPM_INF_DATA);
409 outb(IDPDH, TPM_INF_ADDR);
410 productid[0] = inb(TPM_INF_DATA);
411 outb(CHIP_ID1, TPM_INF_ADDR);
412 version[1] = inb(TPM_INF_DATA);
413 outb(CHIP_ID2, TPM_INF_ADDR);
414 version[0] = inb(TPM_INF_DATA);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700415
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700416 switch ((productid[0] << 8) | productid[1]) {
417 case 6:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700418 snprintf(chipname, sizeof(chipname), " (SLD 9630 TT 1.1)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700419 break;
420 case 11:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700421 snprintf(chipname, sizeof(chipname), " (SLB 9635 TT 1.2)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700422 break;
423 default:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700424 snprintf(chipname, sizeof(chipname), " (unknown chip)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700425 break;
426 }
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700427
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700428 if ((vendorid[0] << 8 | vendorid[1]) == (TPM_INFINEON_DEV_VEN_VALUE)) {
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700429
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700430 /* configure TPM with IO-ports */
431 outb(IOLIMH, TPM_INF_ADDR);
432 outb(((tpm_inf.base >> 8) & 0xff), TPM_INF_DATA);
433 outb(IOLIML, TPM_INF_ADDR);
434 outb((tpm_inf.base & 0xff), TPM_INF_DATA);
435
436 /* control if IO-ports are set correctly */
437 outb(IOLIMH, TPM_INF_ADDR);
438 ioh = inb(TPM_INF_DATA);
439 outb(IOLIML, TPM_INF_ADDR);
440 iol = inb(TPM_INF_DATA);
441
442 if ((ioh << 8 | iol) != tpm_inf.base) {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800443 dev_err(&dev->dev,
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700444 "Could not set IO-ports to %04x\n",
445 tpm_inf.base);
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800446 release_region(tpm_inf.base, TPM_INF_PORT_LEN);
447 return -EIO;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700448 }
449
450 /* activate register */
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700451 outb(TPM_DAR, TPM_INF_ADDR);
452 outb(0x01, TPM_INF_DATA);
453 outb(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700454
455 /* disable RESET, LP and IRQC */
456 outb(RESET_LP_IRQC_DISABLE, tpm_inf.base + CMD);
457
458 /* Finally, we're done, print some infos */
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800459 dev_info(&dev->dev, "TPM found: "
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700460 "config base 0x%x, "
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700461 "io base 0x%x, "
462 "chip version %02x%02x, "
463 "vendor id %x%x (Infineon), "
464 "product id %02x%02x"
465 "%s\n",
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700466 TPM_INF_ADDR,
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800467 TPM_INF_BASE,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700468 version[0], version[1],
469 vendorid[0], vendorid[1],
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700470 productid[0], productid[1], chipname);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700471
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800472 rc = tpm_register_hardware(&dev->dev, &tpm_inf);
473 if (rc < 0) {
474 release_region(tpm_inf.base, TPM_INF_PORT_LEN);
475 return -ENODEV;
476 }
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700477 return 0;
478 } else {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800479 dev_info(&dev->dev, "No Infineon TPM found!\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700480 return -ENODEV;
481 }
482}
483
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800484static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev)
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800485{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800486 struct tpm_chip *chip = pnp_get_drvdata(dev);
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800487
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800488 if (chip) {
489 release_region(chip->vendor->base, TPM_INF_PORT_LEN);
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800490 tpm_remove_hardware(chip->dev);
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800491 }
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800492}
493
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800494static struct pnp_driver tpm_inf_pnp = {
495 .name = "tpm_inf_pnp",
496 .driver = {
497 .owner = THIS_MODULE,
498 .suspend = tpm_pm_suspend,
499 .resume = tpm_pm_resume,
500 },
501 .id_table = tpm_pnp_tbl,
502 .probe = tpm_inf_pnp_probe,
503 .remove = tpm_inf_pnp_remove,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700504};
505
506static int __init init_inf(void)
507{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800508 return pnp_register_driver(&tpm_inf_pnp);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700509}
510
511static void __exit cleanup_inf(void)
512{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800513 pnp_unregister_driver(&tpm_inf_pnp);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700514}
515
516module_init(init_inf);
517module_exit(cleanup_inf);
518
519MODULE_AUTHOR("Marcel Selhorst <selhorst@crypto.rub.de>");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700520MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800521MODULE_VERSION("1.6");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700522MODULE_LICENSE("GPL");