blob: 90cae8f9fe21767417c91c041105d99c862d4a61 [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;
Marcel Selhorst8c9e8772006-02-17 13:52:41 -080036static int TPM_INF_ADDR_LEN;
Andrew Mortonb888c872005-10-30 15:03:28 -080037static int TPM_INF_PORT_LEN;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070038
39/* TPM header definitions */
40enum infineon_tpm_header {
41 TPM_VL_VER = 0x01,
42 TPM_VL_CHANNEL_CONTROL = 0x07,
43 TPM_VL_CHANNEL_PERSONALISATION = 0x0A,
44 TPM_VL_CHANNEL_TPM = 0x0B,
45 TPM_VL_CONTROL = 0x00,
46 TPM_INF_NAK = 0x15,
47 TPM_CTRL_WTX = 0x10,
48 TPM_CTRL_WTX_ABORT = 0x18,
49 TPM_CTRL_WTX_ABORT_ACK = 0x18,
50 TPM_CTRL_ERROR = 0x20,
51 TPM_CTRL_CHAININGACK = 0x40,
52 TPM_CTRL_CHAINING = 0x80,
53 TPM_CTRL_DATA = 0x04,
54 TPM_CTRL_DATA_CHA = 0x84,
55 TPM_CTRL_DATA_CHA_ACK = 0xC4
56};
57
58enum infineon_tpm_register {
59 WRFIFO = 0x00,
60 RDFIFO = 0x01,
61 STAT = 0x02,
62 CMD = 0x03
63};
64
65enum infineon_tpm_command_bits {
66 CMD_DIS = 0x00,
67 CMD_LP = 0x01,
68 CMD_RES = 0x02,
69 CMD_IRQC = 0x06
70};
71
72enum infineon_tpm_status_bits {
73 STAT_XFE = 0x00,
74 STAT_LPA = 0x01,
75 STAT_FOK = 0x02,
76 STAT_TOK = 0x03,
77 STAT_IRQA = 0x06,
78 STAT_RDA = 0x07
79};
80
81/* some outgoing values */
82enum infineon_tpm_values {
83 CHIP_ID1 = 0x20,
84 CHIP_ID2 = 0x21,
Andrew Morton3dcce8e2005-07-27 11:45:14 -070085 TPM_DAR = 0x30,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070086 RESET_LP_IRQC_DISABLE = 0x41,
87 ENABLE_REGISTER_PAIR = 0x55,
88 IOLIMH = 0x60,
89 IOLIML = 0x61,
90 DISABLE_REGISTER_PAIR = 0xAA,
91 IDVENL = 0xF1,
92 IDVENH = 0xF2,
93 IDPDL = 0xF3,
94 IDPDH = 0xF4
95};
96
97static int number_of_wtx;
98
99static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo)
100{
101 int status;
102 int check = 0;
103 int i;
104
105 if (clear_wrfifo) {
106 for (i = 0; i < 4096; i++) {
Marcel Selhorste496f5402006-04-22 02:38:42 -0700107 status = inb(chip->vendor.base + WRFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700108 if (status == 0xff) {
109 if (check == 5)
110 break;
111 else
112 check++;
113 }
114 }
115 }
116 /* Note: The values which are currently in the FIFO of the TPM
117 are thrown away since there is no usage for them. Usually,
118 this has nothing to say, since the TPM will give its answer
119 immediately or will be aborted anyway, so the data here is
120 usually garbage and useless.
121 We have to clean this, because the next communication with
122 the TPM would be rubbish, if there is still some old data
123 in the Read FIFO.
124 */
125 i = 0;
126 do {
Marcel Selhorste496f5402006-04-22 02:38:42 -0700127 status = inb(chip->vendor.base + RDFIFO);
128 status = inb(chip->vendor.base + STAT);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700129 i++;
130 if (i == TPM_MAX_TRIES)
131 return -EIO;
132 } while ((status & (1 << STAT_RDA)) != 0);
133 return 0;
134}
135
136static int wait(struct tpm_chip *chip, int wait_for_bit)
137{
138 int status;
139 int i;
140 for (i = 0; i < TPM_MAX_TRIES; i++) {
Marcel Selhorste496f5402006-04-22 02:38:42 -0700141 status = inb(chip->vendor.base + STAT);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700142 /* check the status-register if wait_for_bit is set */
143 if (status & 1 << wait_for_bit)
144 break;
145 msleep(TPM_MSLEEP_TIME);
146 }
147 if (i == TPM_MAX_TRIES) { /* timeout occurs */
148 if (wait_for_bit == STAT_XFE)
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800149 dev_err(chip->dev, "Timeout in wait(STAT_XFE)\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700150 if (wait_for_bit == STAT_RDA)
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800151 dev_err(chip->dev, "Timeout in wait(STAT_RDA)\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700152 return -EIO;
153 }
154 return 0;
155};
156
157static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
158{
159 wait(chip, STAT_XFE);
Marcel Selhorste496f5402006-04-22 02:38:42 -0700160 outb(sendbyte, chip->vendor.base + WRFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700161}
162
163 /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more
164 calculation time, it sends a WTX-package, which has to be acknowledged
165 or aborted. This usually occurs if you are hammering the TPM with key
166 creation. Set the maximum number of WTX-packages in the definitions
167 above, if the number is reached, the waiting-time will be denied
168 and the TPM command has to be resend.
169 */
170
171static void tpm_wtx(struct tpm_chip *chip)
172{
173 number_of_wtx++;
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800174 dev_info(chip->dev, "Granting WTX (%02d / %02d)\n",
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700175 number_of_wtx, TPM_MAX_WTX_PACKAGES);
176 wait_and_send(chip, TPM_VL_VER);
177 wait_and_send(chip, TPM_CTRL_WTX);
178 wait_and_send(chip, 0x00);
179 wait_and_send(chip, 0x00);
180 msleep(TPM_WTX_MSLEEP_TIME);
181}
182
183static void tpm_wtx_abort(struct tpm_chip *chip)
184{
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800185 dev_info(chip->dev, "Aborting WTX\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700186 wait_and_send(chip, TPM_VL_VER);
187 wait_and_send(chip, TPM_CTRL_WTX_ABORT);
188 wait_and_send(chip, 0x00);
189 wait_and_send(chip, 0x00);
190 number_of_wtx = 0;
191 msleep(TPM_WTX_MSLEEP_TIME);
192}
193
194static int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count)
195{
196 int i;
197 int ret;
198 u32 size = 0;
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800199 number_of_wtx = 0;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700200
201recv_begin:
202 /* start receiving header */
203 for (i = 0; i < 4; i++) {
204 ret = wait(chip, STAT_RDA);
205 if (ret)
206 return -EIO;
Marcel Selhorste496f5402006-04-22 02:38:42 -0700207 buf[i] = inb(chip->vendor.base + RDFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700208 }
209
210 if (buf[0] != TPM_VL_VER) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800211 dev_err(chip->dev,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700212 "Wrong transport protocol implementation!\n");
213 return -EIO;
214 }
215
216 if (buf[1] == TPM_CTRL_DATA) {
217 /* size of the data received */
218 size = ((buf[2] << 8) | buf[3]);
219
220 for (i = 0; i < size; i++) {
221 wait(chip, STAT_RDA);
Marcel Selhorste496f5402006-04-22 02:38:42 -0700222 buf[i] = inb(chip->vendor.base + RDFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700223 }
224
225 if ((size == 0x6D00) && (buf[1] == 0x80)) {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800226 dev_err(chip->dev, "Error handling on vendor layer!\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700227 return -EIO;
228 }
229
230 for (i = 0; i < size; i++)
231 buf[i] = buf[i + 6];
232
233 size = size - 6;
234 return size;
235 }
236
237 if (buf[1] == TPM_CTRL_WTX) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800238 dev_info(chip->dev, "WTX-package received\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700239 if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
240 tpm_wtx(chip);
241 goto recv_begin;
242 } else {
243 tpm_wtx_abort(chip);
244 goto recv_begin;
245 }
246 }
247
248 if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800249 dev_info(chip->dev, "WTX-abort acknowledged\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700250 return size;
251 }
252
253 if (buf[1] == TPM_CTRL_ERROR) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800254 dev_err(chip->dev, "ERROR-package received:\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700255 if (buf[4] == TPM_INF_NAK)
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800256 dev_err(chip->dev,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700257 "-> Negative acknowledgement"
258 " - retransmit command!\n");
259 return -EIO;
260 }
261 return -EIO;
262}
263
264static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
265{
266 int i;
267 int ret;
268 u8 count_high, count_low, count_4, count_3, count_2, count_1;
269
270 /* Disabling Reset, LP and IRQC */
Marcel Selhorste496f5402006-04-22 02:38:42 -0700271 outb(RESET_LP_IRQC_DISABLE, chip->vendor.base + CMD);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700272
273 ret = empty_fifo(chip, 1);
274 if (ret) {
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800275 dev_err(chip->dev, "Timeout while clearing FIFO\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700276 return -EIO;
277 }
278
279 ret = wait(chip, STAT_XFE);
280 if (ret)
281 return -EIO;
282
283 count_4 = (count & 0xff000000) >> 24;
284 count_3 = (count & 0x00ff0000) >> 16;
285 count_2 = (count & 0x0000ff00) >> 8;
286 count_1 = (count & 0x000000ff);
287 count_high = ((count + 6) & 0xffffff00) >> 8;
288 count_low = ((count + 6) & 0x000000ff);
289
290 /* Sending Header */
291 wait_and_send(chip, TPM_VL_VER);
292 wait_and_send(chip, TPM_CTRL_DATA);
293 wait_and_send(chip, count_high);
294 wait_and_send(chip, count_low);
295
296 /* Sending Data Header */
297 wait_and_send(chip, TPM_VL_VER);
298 wait_and_send(chip, TPM_VL_CHANNEL_TPM);
299 wait_and_send(chip, count_4);
300 wait_and_send(chip, count_3);
301 wait_and_send(chip, count_2);
302 wait_and_send(chip, count_1);
303
304 /* Sending Data */
305 for (i = 0; i < count; i++) {
306 wait_and_send(chip, buf[i]);
307 }
308 return count;
309}
310
311static void tpm_inf_cancel(struct tpm_chip *chip)
312{
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700313 /*
314 Since we are using the legacy mode to communicate
315 with the TPM, we have no cancel functions, but have
316 a workaround for interrupting the TPM through WTX.
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700317 */
318}
319
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800320static u8 tpm_inf_status(struct tpm_chip *chip)
321{
Marcel Selhorste496f5402006-04-22 02:38:42 -0700322 return inb(chip->vendor.base + STAT);
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800323}
324
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700325static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
326static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
327static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
328static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
329
330static struct attribute *inf_attrs[] = {
331 &dev_attr_pubek.attr,
332 &dev_attr_pcrs.attr,
333 &dev_attr_caps.attr,
334 &dev_attr_cancel.attr,
335 NULL,
336};
337
338static struct attribute_group inf_attr_grp = {.attrs = inf_attrs };
339
340static struct file_operations inf_ops = {
341 .owner = THIS_MODULE,
342 .llseek = no_llseek,
343 .open = tpm_open,
344 .read = tpm_read,
345 .write = tpm_write,
346 .release = tpm_release,
347};
348
Marcel Selhorste496f5402006-04-22 02:38:42 -0700349static const struct tpm_vendor_specific tpm_inf = {
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700350 .recv = tpm_inf_recv,
351 .send = tpm_inf_send,
352 .cancel = tpm_inf_cancel,
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800353 .status = tpm_inf_status,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700354 .req_complete_mask = 0,
355 .req_complete_val = 0,
356 .attr_group = &inf_attr_grp,
357 .miscdev = {.fops = &inf_ops,},
358};
359
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700360static const struct pnp_device_id tpm_pnp_tbl[] = {
361 /* Infineon TPMs */
362 {"IFX0101", 0},
363 {"IFX0102", 0},
364 {"", 0}
365};
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800366
Marcel Selhorste8a65012005-09-03 15:54:20 -0700367MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700368
Marcel Selhorste8a65012005-09-03 15:54:20 -0700369static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev,
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800370 const struct pnp_device_id *dev_id)
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700371{
372 int rc = 0;
373 u8 iol, ioh;
374 int vendorid[2];
375 int version[2];
376 int productid[2];
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700377 char chipname[20];
Marcel Selhorste496f5402006-04-22 02:38:42 -0700378 struct tpm_chip *chip;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700379
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800380 /* read IO-ports through PnP */
381 if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) &&
382 !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
383 TPM_INF_ADDR = pnp_port_start(dev, 0);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800384 TPM_INF_ADDR_LEN = pnp_port_len(dev, 0);
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800385 TPM_INF_DATA = (TPM_INF_ADDR + 1);
386 TPM_INF_BASE = pnp_port_start(dev, 1);
387 TPM_INF_PORT_LEN = pnp_port_len(dev, 1);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800388 if ((TPM_INF_PORT_LEN < 4) || (TPM_INF_ADDR_LEN < 2)) {
389 rc = -EINVAL;
390 goto err_last;
391 }
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800392 dev_info(&dev->dev, "Found %s with ID %s\n",
393 dev->name, dev_id->id);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800394 if (!((TPM_INF_BASE >> 8) & 0xff)) {
395 rc = -EINVAL;
396 goto err_last;
397 }
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800398 /* publish my base address and request region */
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800399 if (request_region
Marcel Selhorste496f5402006-04-22 02:38:42 -0700400 (TPM_INF_BASE, TPM_INF_PORT_LEN, "tpm_infineon0") == NULL) {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800401 rc = -EINVAL;
402 goto err_last;
403 }
Marcel Selhorste496f5402006-04-22 02:38:42 -0700404 if (request_region
405 (TPM_INF_ADDR, TPM_INF_ADDR_LEN, "tpm_infineon0") == NULL) {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800406 rc = -EINVAL;
407 goto err_last;
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800408 }
Marcel Selhorste8a65012005-09-03 15:54:20 -0700409 } else {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800410 rc = -EINVAL;
411 goto err_last;
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700412 }
413
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700414 /* query chip for its vendor, its version number a.s.o. */
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700415 outb(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
416 outb(IDVENL, TPM_INF_ADDR);
417 vendorid[1] = inb(TPM_INF_DATA);
418 outb(IDVENH, TPM_INF_ADDR);
419 vendorid[0] = inb(TPM_INF_DATA);
420 outb(IDPDL, TPM_INF_ADDR);
421 productid[1] = inb(TPM_INF_DATA);
422 outb(IDPDH, TPM_INF_ADDR);
423 productid[0] = inb(TPM_INF_DATA);
424 outb(CHIP_ID1, TPM_INF_ADDR);
425 version[1] = inb(TPM_INF_DATA);
426 outb(CHIP_ID2, TPM_INF_ADDR);
427 version[0] = inb(TPM_INF_DATA);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700428
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700429 switch ((productid[0] << 8) | productid[1]) {
430 case 6:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700431 snprintf(chipname, sizeof(chipname), " (SLD 9630 TT 1.1)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700432 break;
433 case 11:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700434 snprintf(chipname, sizeof(chipname), " (SLB 9635 TT 1.2)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700435 break;
436 default:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700437 snprintf(chipname, sizeof(chipname), " (unknown chip)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700438 break;
439 }
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700440
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700441 if ((vendorid[0] << 8 | vendorid[1]) == (TPM_INFINEON_DEV_VEN_VALUE)) {
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700442
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700443 /* configure TPM with IO-ports */
444 outb(IOLIMH, TPM_INF_ADDR);
Marcel Selhorste496f5402006-04-22 02:38:42 -0700445 outb(((TPM_INF_BASE >> 8) & 0xff), TPM_INF_DATA);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700446 outb(IOLIML, TPM_INF_ADDR);
Marcel Selhorste496f5402006-04-22 02:38:42 -0700447 outb((TPM_INF_BASE & 0xff), TPM_INF_DATA);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700448
449 /* control if IO-ports are set correctly */
450 outb(IOLIMH, TPM_INF_ADDR);
451 ioh = inb(TPM_INF_DATA);
452 outb(IOLIML, TPM_INF_ADDR);
453 iol = inb(TPM_INF_DATA);
454
Marcel Selhorste496f5402006-04-22 02:38:42 -0700455 if ((ioh << 8 | iol) != TPM_INF_BASE) {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800456 dev_err(&dev->dev,
Marcel Selhorste496f5402006-04-22 02:38:42 -0700457 "Could not set IO-ports to 0x%x\n",
458 TPM_INF_BASE);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800459 rc = -EIO;
460 goto err_release_region;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700461 }
462
463 /* activate register */
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700464 outb(TPM_DAR, TPM_INF_ADDR);
465 outb(0x01, TPM_INF_DATA);
466 outb(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700467
468 /* disable RESET, LP and IRQC */
Marcel Selhorste496f5402006-04-22 02:38:42 -0700469 outb(RESET_LP_IRQC_DISABLE, TPM_INF_BASE + CMD);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700470
471 /* Finally, we're done, print some infos */
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800472 dev_info(&dev->dev, "TPM found: "
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700473 "config base 0x%x, "
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700474 "io base 0x%x, "
Marcel Selhorste496f5402006-04-22 02:38:42 -0700475 "chip version 0x%02x%02x, "
476 "vendor id 0x%x%x (Infineon), "
477 "product id 0x%02x%02x"
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700478 "%s\n",
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700479 TPM_INF_ADDR,
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800480 TPM_INF_BASE,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700481 version[0], version[1],
482 vendorid[0], vendorid[1],
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700483 productid[0], productid[1], chipname);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700484
Marcel Selhorste496f5402006-04-22 02:38:42 -0700485 if (!(chip = tpm_register_hardware(&dev->dev, &tpm_inf))) {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800486 goto err_release_region;
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800487 }
Marcel Selhorste496f5402006-04-22 02:38:42 -0700488 chip->vendor.base = TPM_INF_BASE;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700489 return 0;
490 } else {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800491 rc = -ENODEV;
492 goto err_release_region;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700493 }
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800494
495err_release_region:
Marcel Selhorste496f5402006-04-22 02:38:42 -0700496 release_region(TPM_INF_BASE, TPM_INF_PORT_LEN);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800497 release_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN);
498
499err_last:
500 return rc;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700501}
502
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800503static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev)
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800504{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800505 struct tpm_chip *chip = pnp_get_drvdata(dev);
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800506
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800507 if (chip) {
Marcel Selhorste496f5402006-04-22 02:38:42 -0700508 release_region(TPM_INF_BASE, TPM_INF_PORT_LEN);
509 release_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN);
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800510 tpm_remove_hardware(chip->dev);
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800511 }
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800512}
513
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800514static struct pnp_driver tpm_inf_pnp = {
515 .name = "tpm_inf_pnp",
516 .driver = {
517 .owner = THIS_MODULE,
518 .suspend = tpm_pm_suspend,
519 .resume = tpm_pm_resume,
520 },
521 .id_table = tpm_pnp_tbl,
522 .probe = tpm_inf_pnp_probe,
523 .remove = tpm_inf_pnp_remove,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700524};
525
526static int __init init_inf(void)
527{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800528 return pnp_register_driver(&tpm_inf_pnp);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700529}
530
531static void __exit cleanup_inf(void)
532{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800533 pnp_unregister_driver(&tpm_inf_pnp);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700534}
535
536module_init(init_inf);
537module_exit(cleanup_inf);
538
539MODULE_AUTHOR("Marcel Selhorst <selhorst@crypto.rub.de>");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700540MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");
Marcel Selhorste496f5402006-04-22 02:38:42 -0700541MODULE_VERSION("1.8");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700542MODULE_LICENSE("GPL");