blob: 6c488e635fdd07ec9e5f885d8780a18595108b2d [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 *
Rajiv Andradecbb2d5e2012-04-24 11:19:58 -03007 * Copyright (C) 2005, Marcel Selhorst <tpmdd@selhorst.net>
8 * Sirrix AG - security technologies <tpmdd@sirrix.com> and
Marcel Selhorstebb81fd2005-07-27 11:45:12 -07009 * Applied Data Security Group, Ruhr-University Bochum, Germany
Justin P. Mattock631dd1a2010-10-18 11:03:14 +020010 * Project-Homepage: http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070011 *
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
Randy Dunlap397c7182006-04-22 02:39:18 -070018#include <linux/init.h>
Marcel Selhorstf9abb022005-08-05 11:59:33 -070019#include <linux/pnp.h>
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070020#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
Marcel Selhorstf9abb022005-08-05 11:59:33 -070031#define TPM_INFINEON_DEV_VEN_VALUE 0x15D1
32
Alex Williamsond954e8e2007-05-08 00:25:55 -070033#define TPM_INF_IO_PORT 0x0
34#define TPM_INF_IO_MEM 0x1
35
36#define TPM_INF_ADDR 0x0
37#define TPM_INF_DATA 0x1
38
39struct tpm_inf_dev {
40 int iotype;
41
Marcel Selhorst93716b92010-02-10 13:56:32 -080042 void __iomem *mem_base; /* MMIO ioremap'd addr */
43 unsigned long map_base; /* phys MMIO base */
44 unsigned long map_size; /* MMIO region size */
45 unsigned int index_off; /* index register offset */
Alex Williamsond954e8e2007-05-08 00:25:55 -070046
Marcel Selhorst93716b92010-02-10 13:56:32 -080047 unsigned int data_regs; /* Data registers */
Alex Williamsond954e8e2007-05-08 00:25:55 -070048 unsigned int data_size;
49
50 unsigned int config_port; /* IO Port config index reg */
51 unsigned int config_size;
52};
53
54static struct tpm_inf_dev tpm_dev;
55
56static inline void tpm_data_out(unsigned char data, unsigned char offset)
57{
58 if (tpm_dev.iotype == TPM_INF_IO_PORT)
59 outb(data, tpm_dev.data_regs + offset);
60 else
61 writeb(data, tpm_dev.mem_base + tpm_dev.data_regs + offset);
62}
63
64static inline unsigned char tpm_data_in(unsigned char offset)
65{
66 if (tpm_dev.iotype == TPM_INF_IO_PORT)
67 return inb(tpm_dev.data_regs + offset);
68 else
69 return readb(tpm_dev.mem_base + tpm_dev.data_regs + offset);
70}
71
72static inline void tpm_config_out(unsigned char data, unsigned char offset)
73{
74 if (tpm_dev.iotype == TPM_INF_IO_PORT)
75 outb(data, tpm_dev.config_port + offset);
76 else
77 writeb(data, tpm_dev.mem_base + tpm_dev.index_off + offset);
78}
79
80static inline unsigned char tpm_config_in(unsigned char offset)
81{
82 if (tpm_dev.iotype == TPM_INF_IO_PORT)
83 return inb(tpm_dev.config_port + offset);
84 else
85 return readb(tpm_dev.mem_base + tpm_dev.index_off + offset);
86}
Marcel Selhorstebb81fd2005-07-27 11:45:12 -070087
88/* TPM header definitions */
89enum infineon_tpm_header {
90 TPM_VL_VER = 0x01,
91 TPM_VL_CHANNEL_CONTROL = 0x07,
92 TPM_VL_CHANNEL_PERSONALISATION = 0x0A,
93 TPM_VL_CHANNEL_TPM = 0x0B,
94 TPM_VL_CONTROL = 0x00,
95 TPM_INF_NAK = 0x15,
96 TPM_CTRL_WTX = 0x10,
97 TPM_CTRL_WTX_ABORT = 0x18,
98 TPM_CTRL_WTX_ABORT_ACK = 0x18,
99 TPM_CTRL_ERROR = 0x20,
100 TPM_CTRL_CHAININGACK = 0x40,
101 TPM_CTRL_CHAINING = 0x80,
102 TPM_CTRL_DATA = 0x04,
103 TPM_CTRL_DATA_CHA = 0x84,
104 TPM_CTRL_DATA_CHA_ACK = 0xC4
105};
106
107enum infineon_tpm_register {
108 WRFIFO = 0x00,
109 RDFIFO = 0x01,
110 STAT = 0x02,
111 CMD = 0x03
112};
113
114enum infineon_tpm_command_bits {
115 CMD_DIS = 0x00,
116 CMD_LP = 0x01,
117 CMD_RES = 0x02,
118 CMD_IRQC = 0x06
119};
120
121enum infineon_tpm_status_bits {
122 STAT_XFE = 0x00,
123 STAT_LPA = 0x01,
124 STAT_FOK = 0x02,
125 STAT_TOK = 0x03,
126 STAT_IRQA = 0x06,
127 STAT_RDA = 0x07
128};
129
130/* some outgoing values */
131enum infineon_tpm_values {
132 CHIP_ID1 = 0x20,
133 CHIP_ID2 = 0x21,
Andrew Morton3dcce8e2005-07-27 11:45:14 -0700134 TPM_DAR = 0x30,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700135 RESET_LP_IRQC_DISABLE = 0x41,
136 ENABLE_REGISTER_PAIR = 0x55,
137 IOLIMH = 0x60,
138 IOLIML = 0x61,
139 DISABLE_REGISTER_PAIR = 0xAA,
140 IDVENL = 0xF1,
141 IDVENH = 0xF2,
142 IDPDL = 0xF3,
143 IDPDH = 0xF4
144};
145
146static int number_of_wtx;
147
148static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo)
149{
150 int status;
151 int check = 0;
152 int i;
153
154 if (clear_wrfifo) {
155 for (i = 0; i < 4096; i++) {
Alex Williamsond954e8e2007-05-08 00:25:55 -0700156 status = tpm_data_in(WRFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700157 if (status == 0xff) {
158 if (check == 5)
159 break;
160 else
161 check++;
162 }
163 }
164 }
165 /* Note: The values which are currently in the FIFO of the TPM
166 are thrown away since there is no usage for them. Usually,
167 this has nothing to say, since the TPM will give its answer
168 immediately or will be aborted anyway, so the data here is
169 usually garbage and useless.
170 We have to clean this, because the next communication with
171 the TPM would be rubbish, if there is still some old data
172 in the Read FIFO.
173 */
174 i = 0;
175 do {
Alex Williamsond954e8e2007-05-08 00:25:55 -0700176 status = tpm_data_in(RDFIFO);
177 status = tpm_data_in(STAT);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700178 i++;
179 if (i == TPM_MAX_TRIES)
180 return -EIO;
181 } while ((status & (1 << STAT_RDA)) != 0);
182 return 0;
183}
184
185static int wait(struct tpm_chip *chip, int wait_for_bit)
186{
187 int status;
188 int i;
189 for (i = 0; i < TPM_MAX_TRIES; i++) {
Alex Williamsond954e8e2007-05-08 00:25:55 -0700190 status = tpm_data_in(STAT);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700191 /* check the status-register if wait_for_bit is set */
192 if (status & 1 << wait_for_bit)
193 break;
194 msleep(TPM_MSLEEP_TIME);
195 }
196 if (i == TPM_MAX_TRIES) { /* timeout occurs */
197 if (wait_for_bit == STAT_XFE)
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800198 dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700199 if (wait_for_bit == STAT_RDA)
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800200 dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700201 return -EIO;
202 }
203 return 0;
204};
205
206static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
207{
208 wait(chip, STAT_XFE);
Alex Williamsond954e8e2007-05-08 00:25:55 -0700209 tpm_data_out(sendbyte, WRFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700210}
211
212 /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more
213 calculation time, it sends a WTX-package, which has to be acknowledged
214 or aborted. This usually occurs if you are hammering the TPM with key
215 creation. Set the maximum number of WTX-packages in the definitions
216 above, if the number is reached, the waiting-time will be denied
217 and the TPM command has to be resend.
218 */
219
220static void tpm_wtx(struct tpm_chip *chip)
221{
222 number_of_wtx++;
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800223 dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n",
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700224 number_of_wtx, TPM_MAX_WTX_PACKAGES);
225 wait_and_send(chip, TPM_VL_VER);
226 wait_and_send(chip, TPM_CTRL_WTX);
227 wait_and_send(chip, 0x00);
228 wait_and_send(chip, 0x00);
229 msleep(TPM_WTX_MSLEEP_TIME);
230}
231
232static void tpm_wtx_abort(struct tpm_chip *chip)
233{
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800234 dev_info(chip->pdev, "Aborting WTX\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700235 wait_and_send(chip, TPM_VL_VER);
236 wait_and_send(chip, TPM_CTRL_WTX_ABORT);
237 wait_and_send(chip, 0x00);
238 wait_and_send(chip, 0x00);
239 number_of_wtx = 0;
240 msleep(TPM_WTX_MSLEEP_TIME);
241}
242
243static int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count)
244{
245 int i;
246 int ret;
247 u32 size = 0;
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800248 number_of_wtx = 0;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700249
250recv_begin:
251 /* start receiving header */
252 for (i = 0; i < 4; i++) {
253 ret = wait(chip, STAT_RDA);
254 if (ret)
255 return -EIO;
Alex Williamsond954e8e2007-05-08 00:25:55 -0700256 buf[i] = tpm_data_in(RDFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700257 }
258
259 if (buf[0] != TPM_VL_VER) {
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800260 dev_err(chip->pdev,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700261 "Wrong transport protocol implementation!\n");
262 return -EIO;
263 }
264
265 if (buf[1] == TPM_CTRL_DATA) {
266 /* size of the data received */
267 size = ((buf[2] << 8) | buf[3]);
268
269 for (i = 0; i < size; i++) {
270 wait(chip, STAT_RDA);
Alex Williamsond954e8e2007-05-08 00:25:55 -0700271 buf[i] = tpm_data_in(RDFIFO);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700272 }
273
274 if ((size == 0x6D00) && (buf[1] == 0x80)) {
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800275 dev_err(chip->pdev, "Error handling on vendor layer!\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700276 return -EIO;
277 }
278
279 for (i = 0; i < size; i++)
280 buf[i] = buf[i + 6];
281
282 size = size - 6;
283 return size;
284 }
285
286 if (buf[1] == TPM_CTRL_WTX) {
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800287 dev_info(chip->pdev, "WTX-package received\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700288 if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
289 tpm_wtx(chip);
290 goto recv_begin;
291 } else {
292 tpm_wtx_abort(chip);
293 goto recv_begin;
294 }
295 }
296
297 if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800298 dev_info(chip->pdev, "WTX-abort acknowledged\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700299 return size;
300 }
301
302 if (buf[1] == TPM_CTRL_ERROR) {
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800303 dev_err(chip->pdev, "ERROR-package received:\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700304 if (buf[4] == TPM_INF_NAK)
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800305 dev_err(chip->pdev,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700306 "-> Negative acknowledgement"
307 " - retransmit command!\n");
308 return -EIO;
309 }
310 return -EIO;
311}
312
313static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
314{
315 int i;
316 int ret;
317 u8 count_high, count_low, count_4, count_3, count_2, count_1;
318
319 /* Disabling Reset, LP and IRQC */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700320 tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700321
322 ret = empty_fifo(chip, 1);
323 if (ret) {
Jarkko Sakkinen71ed8482014-12-12 11:46:36 -0800324 dev_err(chip->pdev, "Timeout while clearing FIFO\n");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700325 return -EIO;
326 }
327
328 ret = wait(chip, STAT_XFE);
329 if (ret)
330 return -EIO;
331
332 count_4 = (count & 0xff000000) >> 24;
333 count_3 = (count & 0x00ff0000) >> 16;
334 count_2 = (count & 0x0000ff00) >> 8;
335 count_1 = (count & 0x000000ff);
336 count_high = ((count + 6) & 0xffffff00) >> 8;
337 count_low = ((count + 6) & 0x000000ff);
338
339 /* Sending Header */
340 wait_and_send(chip, TPM_VL_VER);
341 wait_and_send(chip, TPM_CTRL_DATA);
342 wait_and_send(chip, count_high);
343 wait_and_send(chip, count_low);
344
345 /* Sending Data Header */
346 wait_and_send(chip, TPM_VL_VER);
347 wait_and_send(chip, TPM_VL_CHANNEL_TPM);
348 wait_and_send(chip, count_4);
349 wait_and_send(chip, count_3);
350 wait_and_send(chip, count_2);
351 wait_and_send(chip, count_1);
352
353 /* Sending Data */
354 for (i = 0; i < count; i++) {
355 wait_and_send(chip, buf[i]);
356 }
357 return count;
358}
359
360static void tpm_inf_cancel(struct tpm_chip *chip)
361{
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700362 /*
363 Since we are using the legacy mode to communicate
364 with the TPM, we have no cancel functions, but have
365 a workaround for interrupting the TPM through WTX.
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700366 */
367}
368
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800369static u8 tpm_inf_status(struct tpm_chip *chip)
370{
Alex Williamsond954e8e2007-05-08 00:25:55 -0700371 return tpm_data_in(STAT);
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800372}
373
Jason Gunthorpe01ad1fa2013-11-26 13:30:43 -0700374static const struct tpm_class_ops tpm_inf = {
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700375 .recv = tpm_inf_recv,
376 .send = tpm_inf_send,
377 .cancel = tpm_inf_cancel,
Kylene Jo Hallb4ed3e32005-10-30 15:03:23 -0800378 .status = tpm_inf_status,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700379 .req_complete_mask = 0,
380 .req_complete_val = 0,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700381};
382
Marcel Selhorst93716b92010-02-10 13:56:32 -0800383static const struct pnp_device_id tpm_inf_pnp_tbl[] = {
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700384 /* Infineon TPMs */
385 {"IFX0101", 0},
386 {"IFX0102", 0},
387 {"", 0}
388};
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800389
Marcel Selhorst93716b92010-02-10 13:56:32 -0800390MODULE_DEVICE_TABLE(pnp, tpm_inf_pnp_tbl);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700391
Bill Pembertonafc6d362012-11-19 13:22:42 -0500392static int tpm_inf_pnp_probe(struct pnp_dev *dev,
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800393 const struct pnp_device_id *dev_id)
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700394{
395 int rc = 0;
396 u8 iol, ioh;
397 int vendorid[2];
398 int version[2];
399 int productid[2];
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700400 char chipname[20];
Marcel Selhorste496f5402006-04-22 02:38:42 -0700401 struct tpm_chip *chip;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700402
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800403 /* read IO-ports through PnP */
404 if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) &&
405 !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) {
Alex Williamsond954e8e2007-05-08 00:25:55 -0700406
Marcel Selhorst93716b92010-02-10 13:56:32 -0800407 tpm_dev.iotype = TPM_INF_IO_PORT;
Alex Williamsond954e8e2007-05-08 00:25:55 -0700408
409 tpm_dev.config_port = pnp_port_start(dev, 0);
410 tpm_dev.config_size = pnp_port_len(dev, 0);
411 tpm_dev.data_regs = pnp_port_start(dev, 1);
412 tpm_dev.data_size = pnp_port_len(dev, 1);
413 if ((tpm_dev.data_size < 4) || (tpm_dev.config_size < 2)) {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800414 rc = -EINVAL;
415 goto err_last;
416 }
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800417 dev_info(&dev->dev, "Found %s with ID %s\n",
418 dev->name, dev_id->id);
Alex Williamsond954e8e2007-05-08 00:25:55 -0700419 if (!((tpm_dev.data_regs >> 8) & 0xff)) {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800420 rc = -EINVAL;
421 goto err_last;
422 }
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800423 /* publish my base address and request region */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700424 if (request_region(tpm_dev.data_regs, tpm_dev.data_size,
425 "tpm_infineon0") == NULL) {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800426 rc = -EINVAL;
427 goto err_last;
428 }
Alex Williamsond954e8e2007-05-08 00:25:55 -0700429 if (request_region(tpm_dev.config_port, tpm_dev.config_size,
430 "tpm_infineon0") == NULL) {
431 release_region(tpm_dev.data_regs, tpm_dev.data_size);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800432 rc = -EINVAL;
433 goto err_last;
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800434 }
Alex Williamsond954e8e2007-05-08 00:25:55 -0700435 } else if (pnp_mem_valid(dev, 0) &&
Marcel Selhorst93716b92010-02-10 13:56:32 -0800436 !(pnp_mem_flags(dev, 0) & IORESOURCE_DISABLED)) {
Alex Williamsond954e8e2007-05-08 00:25:55 -0700437
Marcel Selhorst93716b92010-02-10 13:56:32 -0800438 tpm_dev.iotype = TPM_INF_IO_MEM;
Alex Williamsond954e8e2007-05-08 00:25:55 -0700439
440 tpm_dev.map_base = pnp_mem_start(dev, 0);
441 tpm_dev.map_size = pnp_mem_len(dev, 0);
442
443 dev_info(&dev->dev, "Found %s with ID %s\n",
444 dev->name, dev_id->id);
445
446 /* publish my base address and request region */
447 if (request_mem_region(tpm_dev.map_base, tpm_dev.map_size,
448 "tpm_infineon0") == NULL) {
449 rc = -EINVAL;
450 goto err_last;
451 }
452
453 tpm_dev.mem_base = ioremap(tpm_dev.map_base, tpm_dev.map_size);
454 if (tpm_dev.mem_base == NULL) {
455 release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
456 rc = -EINVAL;
457 goto err_last;
458 }
459
460 /*
461 * The only known MMIO based Infineon TPM system provides
462 * a single large mem region with the device config
463 * registers at the default TPM_ADDR. The data registers
464 * seem like they could be placed anywhere within the MMIO
465 * region, but lets just put them at zero offset.
466 */
467 tpm_dev.index_off = TPM_ADDR;
468 tpm_dev.data_regs = 0x0;
Marcel Selhorste8a65012005-09-03 15:54:20 -0700469 } else {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800470 rc = -EINVAL;
471 goto err_last;
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700472 }
473
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700474 /* query chip for its vendor, its version number a.s.o. */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700475 tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
476 tpm_config_out(IDVENL, TPM_INF_ADDR);
477 vendorid[1] = tpm_config_in(TPM_INF_DATA);
478 tpm_config_out(IDVENH, TPM_INF_ADDR);
479 vendorid[0] = tpm_config_in(TPM_INF_DATA);
480 tpm_config_out(IDPDL, TPM_INF_ADDR);
481 productid[1] = tpm_config_in(TPM_INF_DATA);
482 tpm_config_out(IDPDH, TPM_INF_ADDR);
483 productid[0] = tpm_config_in(TPM_INF_DATA);
484 tpm_config_out(CHIP_ID1, TPM_INF_ADDR);
485 version[1] = tpm_config_in(TPM_INF_DATA);
486 tpm_config_out(CHIP_ID2, TPM_INF_ADDR);
487 version[0] = tpm_config_in(TPM_INF_DATA);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700488
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700489 switch ((productid[0] << 8) | productid[1]) {
490 case 6:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700491 snprintf(chipname, sizeof(chipname), " (SLD 9630 TT 1.1)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700492 break;
493 case 11:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700494 snprintf(chipname, sizeof(chipname), " (SLB 9635 TT 1.2)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700495 break;
496 default:
Marcel Selhorste8a65012005-09-03 15:54:20 -0700497 snprintf(chipname, sizeof(chipname), " (unknown chip)");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700498 break;
499 }
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700500
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700501 if ((vendorid[0] << 8 | vendorid[1]) == (TPM_INFINEON_DEV_VEN_VALUE)) {
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700502
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700503 /* configure TPM with IO-ports */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700504 tpm_config_out(IOLIMH, TPM_INF_ADDR);
505 tpm_config_out((tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA);
506 tpm_config_out(IOLIML, TPM_INF_ADDR);
507 tpm_config_out((tpm_dev.data_regs & 0xff), TPM_INF_DATA);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700508
509 /* control if IO-ports are set correctly */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700510 tpm_config_out(IOLIMH, TPM_INF_ADDR);
511 ioh = tpm_config_in(TPM_INF_DATA);
512 tpm_config_out(IOLIML, TPM_INF_ADDR);
513 iol = tpm_config_in(TPM_INF_DATA);
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700514
Alex Williamsond954e8e2007-05-08 00:25:55 -0700515 if ((ioh << 8 | iol) != tpm_dev.data_regs) {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800516 dev_err(&dev->dev,
Alex Williamsond954e8e2007-05-08 00:25:55 -0700517 "Could not set IO-data registers to 0x%x\n",
518 tpm_dev.data_regs);
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800519 rc = -EIO;
520 goto err_release_region;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700521 }
522
523 /* activate register */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700524 tpm_config_out(TPM_DAR, TPM_INF_ADDR);
525 tpm_config_out(0x01, TPM_INF_DATA);
526 tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700527
528 /* disable RESET, LP and IRQC */
Alex Williamsond954e8e2007-05-08 00:25:55 -0700529 tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700530
531 /* Finally, we're done, print some infos */
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800532 dev_info(&dev->dev, "TPM found: "
Alex Williamsond954e8e2007-05-08 00:25:55 -0700533 "config base 0x%lx, "
534 "data base 0x%lx, "
Marcel Selhorste496f5402006-04-22 02:38:42 -0700535 "chip version 0x%02x%02x, "
536 "vendor id 0x%x%x (Infineon), "
537 "product id 0x%02x%02x"
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700538 "%s\n",
Alex Williamsond954e8e2007-05-08 00:25:55 -0700539 tpm_dev.iotype == TPM_INF_IO_PORT ?
Marcel Selhorst93716b92010-02-10 13:56:32 -0800540 tpm_dev.config_port :
541 tpm_dev.map_base + tpm_dev.index_off,
Alex Williamsond954e8e2007-05-08 00:25:55 -0700542 tpm_dev.iotype == TPM_INF_IO_PORT ?
Marcel Selhorst93716b92010-02-10 13:56:32 -0800543 tpm_dev.data_regs :
544 tpm_dev.map_base + tpm_dev.data_regs,
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700545 version[0], version[1],
546 vendorid[0], vendorid[1],
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700547 productid[0], productid[1], chipname);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700548
Jarkko Sakkinenafb5abc2014-12-12 11:46:34 -0800549 chip = tpmm_chip_alloc(&dev->dev, &tpm_inf);
550 if (IS_ERR(chip)) {
551 rc = PTR_ERR(chip);
552 goto err_release_region;
553 }
554
555 rc = tpm_chip_register(chip);
556 if (rc)
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800557 goto err_release_region;
Alex Williamsond954e8e2007-05-08 00:25:55 -0700558
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700559 return 0;
560 } else {
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800561 rc = -ENODEV;
562 goto err_release_region;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700563 }
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800564
565err_release_region:
Alex Williamsond954e8e2007-05-08 00:25:55 -0700566 if (tpm_dev.iotype == TPM_INF_IO_PORT) {
567 release_region(tpm_dev.data_regs, tpm_dev.data_size);
568 release_region(tpm_dev.config_port, tpm_dev.config_size);
569 } else {
570 iounmap(tpm_dev.mem_base);
571 release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
572 }
Marcel Selhorst8c9e8772006-02-17 13:52:41 -0800573
574err_last:
575 return rc;
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700576}
577
Bill Pemberton39af33f2012-11-19 13:26:26 -0500578static void tpm_inf_pnp_remove(struct pnp_dev *dev)
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800579{
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800580 struct tpm_chip *chip = pnp_get_drvdata(dev);
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800581
Jarkko Sakkinenafb5abc2014-12-12 11:46:34 -0800582 tpm_chip_unregister(chip);
583
584 if (tpm_dev.iotype == TPM_INF_IO_PORT) {
585 release_region(tpm_dev.data_regs, tpm_dev.data_size);
586 release_region(tpm_dev.config_port,
587 tpm_dev.config_size);
588 } else {
589 iounmap(tpm_dev.mem_base);
590 release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800591 }
Kylene Jo Halle659a3f2005-10-30 15:03:24 -0800592}
593
Peter Huewe6b377292015-03-16 22:26:21 +0100594#ifdef CONFIG_PM_SLEEP
595static int tpm_inf_resume(struct device *dev)
Marcel Selhorst93716b92010-02-10 13:56:32 -0800596{
597 /* Re-configure TPM after suspending */
598 tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
599 tpm_config_out(IOLIMH, TPM_INF_ADDR);
600 tpm_config_out((tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA);
601 tpm_config_out(IOLIML, TPM_INF_ADDR);
602 tpm_config_out((tpm_dev.data_regs & 0xff), TPM_INF_DATA);
603 /* activate register */
604 tpm_config_out(TPM_DAR, TPM_INF_ADDR);
605 tpm_config_out(0x01, TPM_INF_DATA);
606 tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
607 /* disable RESET, LP and IRQC */
608 tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
Peter Huewe6b377292015-03-16 22:26:21 +0100609 return tpm_pm_resume(dev);
Marcel Selhorst93716b92010-02-10 13:56:32 -0800610}
Peter Huewe6b377292015-03-16 22:26:21 +0100611#endif
612static SIMPLE_DEV_PM_OPS(tpm_inf_pm, tpm_pm_suspend, tpm_inf_resume);
Marcel Selhorst93716b92010-02-10 13:56:32 -0800613
Randy Dunlape1d42c92008-02-06 01:38:06 -0800614static struct pnp_driver tpm_inf_pnp_driver = {
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800615 .name = "tpm_inf_pnp",
Marcel Selhorst93716b92010-02-10 13:56:32 -0800616 .id_table = tpm_inf_pnp_tbl,
Marcel Selhorst1b8333b2005-10-30 15:03:27 -0800617 .probe = tpm_inf_pnp_probe,
Peter Huewe6b377292015-03-16 22:26:21 +0100618 .remove = tpm_inf_pnp_remove,
619 .driver = {
620 .pm = &tpm_inf_pm,
621 }
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700622};
623
Peter Huewe15516602015-03-16 21:46:31 +0100624module_pnp_driver(tpm_inf_pnp_driver);
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700625
Rajiv Andradecbb2d5e2012-04-24 11:19:58 -0300626MODULE_AUTHOR("Marcel Selhorst <tpmdd@sirrix.com>");
Marcel Selhorstf9abb022005-08-05 11:59:33 -0700627MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");
Marcel Selhorst93716b92010-02-10 13:56:32 -0800628MODULE_VERSION("1.9.2");
Marcel Selhorstebb81fd2005-07-27 11:45:12 -0700629MODULE_LICENSE("GPL");