1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Description: |
4 | * Device Driver for the Infineon Technologies |
5 | * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module |
6 | * Specifications at www.trustedcomputinggroup.org |
7 | * |
8 | * Copyright (C) 2005, Marcel Selhorst <tpmdd@selhorst.net> |
9 | * Sirrix AG - security technologies <tpmdd@sirrix.com> and |
10 | * Applied Data Security Group, Ruhr-University Bochum, Germany |
11 | * Project-Homepage: http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ |
12 | */ |
13 | |
14 | #include <linux/init.h> |
15 | #include <linux/pnp.h> |
16 | #include "tpm.h" |
17 | |
18 | /* Infineon specific definitions */ |
19 | /* maximum number of WTX-packages */ |
20 | #define TPM_MAX_WTX_PACKAGES 50 |
21 | /* msleep-Time for WTX-packages */ |
22 | #define TPM_WTX_MSLEEP_TIME 20 |
23 | /* msleep-Time --> Interval to check status register */ |
24 | #define TPM_MSLEEP_TIME 3 |
25 | /* gives number of max. msleep()-calls before throwing timeout */ |
26 | #define TPM_MAX_TRIES 5000 |
27 | #define TPM_INFINEON_DEV_VEN_VALUE 0x15D1 |
28 | |
29 | #define TPM_INF_IO_PORT 0x0 |
30 | #define TPM_INF_IO_MEM 0x1 |
31 | |
32 | #define TPM_INF_ADDR 0x0 |
33 | #define TPM_INF_DATA 0x1 |
34 | |
35 | struct tpm_inf_dev { |
36 | int iotype; |
37 | |
38 | void __iomem *mem_base; /* MMIO ioremap'd addr */ |
39 | unsigned long map_base; /* phys MMIO base */ |
40 | unsigned long map_size; /* MMIO region size */ |
41 | unsigned int index_off; /* index register offset */ |
42 | |
43 | unsigned int data_regs; /* Data registers */ |
44 | unsigned int data_size; |
45 | |
46 | unsigned int config_port; /* IO Port config index reg */ |
47 | unsigned int config_size; |
48 | }; |
49 | |
50 | static struct tpm_inf_dev tpm_dev; |
51 | |
52 | static inline void tpm_data_out(unsigned char data, unsigned char offset) |
53 | { |
54 | if (tpm_dev.iotype == TPM_INF_IO_PORT) |
55 | outb(value: data, port: tpm_dev.data_regs + offset); |
56 | else |
57 | writeb(val: data, addr: tpm_dev.mem_base + tpm_dev.data_regs + offset); |
58 | } |
59 | |
60 | static inline unsigned char tpm_data_in(unsigned char offset) |
61 | { |
62 | if (tpm_dev.iotype == TPM_INF_IO_PORT) |
63 | return inb(port: tpm_dev.data_regs + offset); |
64 | else |
65 | return readb(addr: tpm_dev.mem_base + tpm_dev.data_regs + offset); |
66 | } |
67 | |
68 | static inline void tpm_config_out(unsigned char data, unsigned char offset) |
69 | { |
70 | if (tpm_dev.iotype == TPM_INF_IO_PORT) |
71 | outb(value: data, port: tpm_dev.config_port + offset); |
72 | else |
73 | writeb(val: data, addr: tpm_dev.mem_base + tpm_dev.index_off + offset); |
74 | } |
75 | |
76 | static inline unsigned char tpm_config_in(unsigned char offset) |
77 | { |
78 | if (tpm_dev.iotype == TPM_INF_IO_PORT) |
79 | return inb(port: tpm_dev.config_port + offset); |
80 | else |
81 | return readb(addr: tpm_dev.mem_base + tpm_dev.index_off + offset); |
82 | } |
83 | |
84 | /* TPM header definitions */ |
85 | enum { |
86 | TPM_VL_VER = 0x01, |
87 | TPM_VL_CHANNEL_CONTROL = 0x07, |
88 | TPM_VL_CHANNEL_PERSONALISATION = 0x0A, |
89 | TPM_VL_CHANNEL_TPM = 0x0B, |
90 | TPM_VL_CONTROL = 0x00, |
91 | TPM_INF_NAK = 0x15, |
92 | TPM_CTRL_WTX = 0x10, |
93 | TPM_CTRL_WTX_ABORT = 0x18, |
94 | TPM_CTRL_WTX_ABORT_ACK = 0x18, |
95 | TPM_CTRL_ERROR = 0x20, |
96 | TPM_CTRL_CHAININGACK = 0x40, |
97 | TPM_CTRL_CHAINING = 0x80, |
98 | TPM_CTRL_DATA = 0x04, |
99 | TPM_CTRL_DATA_CHA = 0x84, |
100 | TPM_CTRL_DATA_CHA_ACK = 0xC4 |
101 | }; |
102 | |
103 | enum infineon_tpm_register { |
104 | WRFIFO = 0x00, |
105 | RDFIFO = 0x01, |
106 | STAT = 0x02, |
107 | CMD = 0x03 |
108 | }; |
109 | |
110 | enum infineon_tpm_command_bits { |
111 | CMD_DIS = 0x00, |
112 | CMD_LP = 0x01, |
113 | CMD_RES = 0x02, |
114 | CMD_IRQC = 0x06 |
115 | }; |
116 | |
117 | enum infineon_tpm_status_bits { |
118 | STAT_XFE = 0x00, |
119 | STAT_LPA = 0x01, |
120 | STAT_FOK = 0x02, |
121 | STAT_TOK = 0x03, |
122 | STAT_IRQA = 0x06, |
123 | STAT_RDA = 0x07 |
124 | }; |
125 | |
126 | /* some outgoing values */ |
127 | enum infineon_tpm_values { |
128 | CHIP_ID1 = 0x20, |
129 | CHIP_ID2 = 0x21, |
130 | TPM_DAR = 0x30, |
131 | RESET_LP_IRQC_DISABLE = 0x41, |
132 | ENABLE_REGISTER_PAIR = 0x55, |
133 | IOLIMH = 0x60, |
134 | IOLIML = 0x61, |
135 | DISABLE_REGISTER_PAIR = 0xAA, |
136 | IDVENL = 0xF1, |
137 | IDVENH = 0xF2, |
138 | IDPDL = 0xF3, |
139 | IDPDH = 0xF4 |
140 | }; |
141 | |
142 | static int number_of_wtx; |
143 | |
144 | static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo) |
145 | { |
146 | int status; |
147 | int check = 0; |
148 | int i; |
149 | |
150 | if (clear_wrfifo) { |
151 | for (i = 0; i < 4096; i++) { |
152 | status = tpm_data_in(offset: WRFIFO); |
153 | if (status == 0xff) { |
154 | if (check == 5) |
155 | break; |
156 | else |
157 | check++; |
158 | } |
159 | } |
160 | } |
161 | /* Note: The values which are currently in the FIFO of the TPM |
162 | are thrown away since there is no usage for them. Usually, |
163 | this has nothing to say, since the TPM will give its answer |
164 | immediately or will be aborted anyway, so the data here is |
165 | usually garbage and useless. |
166 | We have to clean this, because the next communication with |
167 | the TPM would be rubbish, if there is still some old data |
168 | in the Read FIFO. |
169 | */ |
170 | i = 0; |
171 | do { |
172 | status = tpm_data_in(offset: RDFIFO); |
173 | status = tpm_data_in(offset: STAT); |
174 | i++; |
175 | if (i == TPM_MAX_TRIES) |
176 | return -EIO; |
177 | } while ((status & (1 << STAT_RDA)) != 0); |
178 | return 0; |
179 | } |
180 | |
181 | static int wait(struct tpm_chip *chip, int wait_for_bit) |
182 | { |
183 | int status; |
184 | int i; |
185 | for (i = 0; i < TPM_MAX_TRIES; i++) { |
186 | status = tpm_data_in(offset: STAT); |
187 | /* check the status-register if wait_for_bit is set */ |
188 | if (status & 1 << wait_for_bit) |
189 | break; |
190 | tpm_msleep(TPM_MSLEEP_TIME); |
191 | } |
192 | if (i == TPM_MAX_TRIES) { /* timeout occurs */ |
193 | if (wait_for_bit == STAT_XFE) |
194 | dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n" ); |
195 | if (wait_for_bit == STAT_RDA) |
196 | dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n" ); |
197 | return -EIO; |
198 | } |
199 | return 0; |
200 | }; |
201 | |
202 | static void wait_and_send(struct tpm_chip *chip, u8 sendbyte) |
203 | { |
204 | wait(chip, wait_for_bit: STAT_XFE); |
205 | tpm_data_out(data: sendbyte, offset: WRFIFO); |
206 | } |
207 | |
208 | /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more |
209 | calculation time, it sends a WTX-package, which has to be acknowledged |
210 | or aborted. This usually occurs if you are hammering the TPM with key |
211 | creation. Set the maximum number of WTX-packages in the definitions |
212 | above, if the number is reached, the waiting-time will be denied |
213 | and the TPM command has to be resend. |
214 | */ |
215 | |
216 | static void tpm_wtx(struct tpm_chip *chip) |
217 | { |
218 | number_of_wtx++; |
219 | dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n" , |
220 | number_of_wtx, TPM_MAX_WTX_PACKAGES); |
221 | wait_and_send(chip, sendbyte: TPM_VL_VER); |
222 | wait_and_send(chip, sendbyte: TPM_CTRL_WTX); |
223 | wait_and_send(chip, sendbyte: 0x00); |
224 | wait_and_send(chip, sendbyte: 0x00); |
225 | tpm_msleep(TPM_WTX_MSLEEP_TIME); |
226 | } |
227 | |
228 | static void tpm_wtx_abort(struct tpm_chip *chip) |
229 | { |
230 | dev_info(&chip->dev, "Aborting WTX\n" ); |
231 | wait_and_send(chip, sendbyte: TPM_VL_VER); |
232 | wait_and_send(chip, sendbyte: TPM_CTRL_WTX_ABORT); |
233 | wait_and_send(chip, sendbyte: 0x00); |
234 | wait_and_send(chip, sendbyte: 0x00); |
235 | number_of_wtx = 0; |
236 | tpm_msleep(TPM_WTX_MSLEEP_TIME); |
237 | } |
238 | |
239 | static int tpm_inf_recv(struct tpm_chip *chip, u8 * buf, size_t count) |
240 | { |
241 | int i; |
242 | int ret; |
243 | u32 size = 0; |
244 | number_of_wtx = 0; |
245 | |
246 | recv_begin: |
247 | /* start receiving header */ |
248 | for (i = 0; i < 4; i++) { |
249 | ret = wait(chip, wait_for_bit: STAT_RDA); |
250 | if (ret) |
251 | return -EIO; |
252 | buf[i] = tpm_data_in(offset: RDFIFO); |
253 | } |
254 | |
255 | if (buf[0] != TPM_VL_VER) { |
256 | dev_err(&chip->dev, |
257 | "Wrong transport protocol implementation!\n" ); |
258 | return -EIO; |
259 | } |
260 | |
261 | if (buf[1] == TPM_CTRL_DATA) { |
262 | /* size of the data received */ |
263 | size = ((buf[2] << 8) | buf[3]); |
264 | |
265 | for (i = 0; i < size; i++) { |
266 | wait(chip, wait_for_bit: STAT_RDA); |
267 | buf[i] = tpm_data_in(offset: RDFIFO); |
268 | } |
269 | |
270 | if ((size == 0x6D00) && (buf[1] == 0x80)) { |
271 | dev_err(&chip->dev, "Error handling on vendor layer!\n" ); |
272 | return -EIO; |
273 | } |
274 | |
275 | for (i = 0; i < size; i++) |
276 | buf[i] = buf[i + 6]; |
277 | |
278 | size = size - 6; |
279 | return size; |
280 | } |
281 | |
282 | if (buf[1] == TPM_CTRL_WTX) { |
283 | dev_info(&chip->dev, "WTX-package received\n" ); |
284 | if (number_of_wtx < TPM_MAX_WTX_PACKAGES) { |
285 | tpm_wtx(chip); |
286 | goto recv_begin; |
287 | } else { |
288 | tpm_wtx_abort(chip); |
289 | goto recv_begin; |
290 | } |
291 | } |
292 | |
293 | if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) { |
294 | dev_info(&chip->dev, "WTX-abort acknowledged\n" ); |
295 | return size; |
296 | } |
297 | |
298 | if (buf[1] == TPM_CTRL_ERROR) { |
299 | dev_err(&chip->dev, "ERROR-package received:\n" ); |
300 | if (buf[4] == TPM_INF_NAK) |
301 | dev_err(&chip->dev, |
302 | "-> Negative acknowledgement" |
303 | " - retransmit command!\n" ); |
304 | return -EIO; |
305 | } |
306 | return -EIO; |
307 | } |
308 | |
309 | static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) |
310 | { |
311 | int i; |
312 | int ret; |
313 | u8 count_high, count_low, count_4, count_3, count_2, count_1; |
314 | |
315 | /* Disabling Reset, LP and IRQC */ |
316 | tpm_data_out(data: RESET_LP_IRQC_DISABLE, offset: CMD); |
317 | |
318 | ret = empty_fifo(chip, clear_wrfifo: 1); |
319 | if (ret) { |
320 | dev_err(&chip->dev, "Timeout while clearing FIFO\n" ); |
321 | return -EIO; |
322 | } |
323 | |
324 | ret = wait(chip, wait_for_bit: STAT_XFE); |
325 | if (ret) |
326 | return -EIO; |
327 | |
328 | count_4 = (count & 0xff000000) >> 24; |
329 | count_3 = (count & 0x00ff0000) >> 16; |
330 | count_2 = (count & 0x0000ff00) >> 8; |
331 | count_1 = (count & 0x000000ff); |
332 | count_high = ((count + 6) & 0xffffff00) >> 8; |
333 | count_low = ((count + 6) & 0x000000ff); |
334 | |
335 | /* Sending Header */ |
336 | wait_and_send(chip, sendbyte: TPM_VL_VER); |
337 | wait_and_send(chip, sendbyte: TPM_CTRL_DATA); |
338 | wait_and_send(chip, sendbyte: count_high); |
339 | wait_and_send(chip, sendbyte: count_low); |
340 | |
341 | /* Sending Data Header */ |
342 | wait_and_send(chip, sendbyte: TPM_VL_VER); |
343 | wait_and_send(chip, sendbyte: TPM_VL_CHANNEL_TPM); |
344 | wait_and_send(chip, sendbyte: count_4); |
345 | wait_and_send(chip, sendbyte: count_3); |
346 | wait_and_send(chip, sendbyte: count_2); |
347 | wait_and_send(chip, sendbyte: count_1); |
348 | |
349 | /* Sending Data */ |
350 | for (i = 0; i < count; i++) { |
351 | wait_and_send(chip, sendbyte: buf[i]); |
352 | } |
353 | return 0; |
354 | } |
355 | |
356 | static void tpm_inf_cancel(struct tpm_chip *chip) |
357 | { |
358 | /* |
359 | Since we are using the legacy mode to communicate |
360 | with the TPM, we have no cancel functions, but have |
361 | a workaround for interrupting the TPM through WTX. |
362 | */ |
363 | } |
364 | |
365 | static u8 tpm_inf_status(struct tpm_chip *chip) |
366 | { |
367 | return tpm_data_in(offset: STAT); |
368 | } |
369 | |
370 | static const struct tpm_class_ops tpm_inf = { |
371 | .recv = tpm_inf_recv, |
372 | .send = tpm_inf_send, |
373 | .cancel = tpm_inf_cancel, |
374 | .status = tpm_inf_status, |
375 | .req_complete_mask = 0, |
376 | .req_complete_val = 0, |
377 | }; |
378 | |
379 | static const struct pnp_device_id tpm_inf_pnp_tbl[] = { |
380 | /* Infineon TPMs */ |
381 | {"IFX0101" , 0}, |
382 | {"IFX0102" , 0}, |
383 | {"" , 0} |
384 | }; |
385 | |
386 | MODULE_DEVICE_TABLE(pnp, tpm_inf_pnp_tbl); |
387 | |
388 | static int tpm_inf_pnp_probe(struct pnp_dev *dev, |
389 | const struct pnp_device_id *dev_id) |
390 | { |
391 | int rc = 0; |
392 | u8 iol, ioh; |
393 | int vendorid[2]; |
394 | int version[2]; |
395 | int productid[2]; |
396 | const char *chipname; |
397 | struct tpm_chip *chip; |
398 | |
399 | /* read IO-ports through PnP */ |
400 | if (pnp_port_valid(dev, bar: 0) && pnp_port_valid(dev, bar: 1) && |
401 | !(pnp_port_flags(dev, bar: 0) & IORESOURCE_DISABLED)) { |
402 | |
403 | tpm_dev.iotype = TPM_INF_IO_PORT; |
404 | |
405 | tpm_dev.config_port = pnp_port_start(dev, bar: 0); |
406 | tpm_dev.config_size = pnp_port_len(dev, bar: 0); |
407 | tpm_dev.data_regs = pnp_port_start(dev, bar: 1); |
408 | tpm_dev.data_size = pnp_port_len(dev, bar: 1); |
409 | if ((tpm_dev.data_size < 4) || (tpm_dev.config_size < 2)) { |
410 | rc = -EINVAL; |
411 | goto err_last; |
412 | } |
413 | dev_info(&dev->dev, "Found %s with ID %s\n" , |
414 | dev->name, dev_id->id); |
415 | if (!((tpm_dev.data_regs >> 8) & 0xff)) { |
416 | rc = -EINVAL; |
417 | goto err_last; |
418 | } |
419 | /* publish my base address and request region */ |
420 | if (request_region(tpm_dev.data_regs, tpm_dev.data_size, |
421 | "tpm_infineon0" ) == NULL) { |
422 | rc = -EINVAL; |
423 | goto err_last; |
424 | } |
425 | if (request_region(tpm_dev.config_port, tpm_dev.config_size, |
426 | "tpm_infineon0" ) == NULL) { |
427 | release_region(tpm_dev.data_regs, tpm_dev.data_size); |
428 | rc = -EINVAL; |
429 | goto err_last; |
430 | } |
431 | } else if (pnp_mem_valid(dev, bar: 0) && |
432 | !(pnp_mem_flags(dev, bar: 0) & IORESOURCE_DISABLED)) { |
433 | |
434 | tpm_dev.iotype = TPM_INF_IO_MEM; |
435 | |
436 | tpm_dev.map_base = pnp_mem_start(dev, bar: 0); |
437 | tpm_dev.map_size = pnp_mem_len(dev, bar: 0); |
438 | |
439 | dev_info(&dev->dev, "Found %s with ID %s\n" , |
440 | dev->name, dev_id->id); |
441 | |
442 | /* publish my base address and request region */ |
443 | if (request_mem_region(tpm_dev.map_base, tpm_dev.map_size, |
444 | "tpm_infineon0" ) == NULL) { |
445 | rc = -EINVAL; |
446 | goto err_last; |
447 | } |
448 | |
449 | tpm_dev.mem_base = ioremap(offset: tpm_dev.map_base, size: tpm_dev.map_size); |
450 | if (tpm_dev.mem_base == NULL) { |
451 | release_mem_region(tpm_dev.map_base, tpm_dev.map_size); |
452 | rc = -EINVAL; |
453 | goto err_last; |
454 | } |
455 | |
456 | /* |
457 | * The only known MMIO based Infineon TPM system provides |
458 | * a single large mem region with the device config |
459 | * registers at the default TPM_ADDR. The data registers |
460 | * seem like they could be placed anywhere within the MMIO |
461 | * region, but lets just put them at zero offset. |
462 | */ |
463 | tpm_dev.index_off = TPM_ADDR; |
464 | tpm_dev.data_regs = 0x0; |
465 | } else { |
466 | rc = -EINVAL; |
467 | goto err_last; |
468 | } |
469 | |
470 | /* query chip for its vendor, its version number a.s.o. */ |
471 | tpm_config_out(data: ENABLE_REGISTER_PAIR, TPM_INF_ADDR); |
472 | tpm_config_out(data: IDVENL, TPM_INF_ADDR); |
473 | vendorid[1] = tpm_config_in(TPM_INF_DATA); |
474 | tpm_config_out(data: IDVENH, TPM_INF_ADDR); |
475 | vendorid[0] = tpm_config_in(TPM_INF_DATA); |
476 | tpm_config_out(data: IDPDL, TPM_INF_ADDR); |
477 | productid[1] = tpm_config_in(TPM_INF_DATA); |
478 | tpm_config_out(data: IDPDH, TPM_INF_ADDR); |
479 | productid[0] = tpm_config_in(TPM_INF_DATA); |
480 | tpm_config_out(data: CHIP_ID1, TPM_INF_ADDR); |
481 | version[1] = tpm_config_in(TPM_INF_DATA); |
482 | tpm_config_out(data: CHIP_ID2, TPM_INF_ADDR); |
483 | version[0] = tpm_config_in(TPM_INF_DATA); |
484 | |
485 | switch ((productid[0] << 8) | productid[1]) { |
486 | case 6: |
487 | chipname = " (SLD 9630 TT 1.1)" ; |
488 | break; |
489 | case 11: |
490 | chipname = " (SLB 9635 TT 1.2)" ; |
491 | break; |
492 | default: |
493 | chipname = " (unknown chip)" ; |
494 | break; |
495 | } |
496 | |
497 | if ((vendorid[0] << 8 | vendorid[1]) == (TPM_INFINEON_DEV_VEN_VALUE)) { |
498 | |
499 | /* configure TPM with IO-ports */ |
500 | tpm_config_out(data: IOLIMH, TPM_INF_ADDR); |
501 | tpm_config_out(data: (tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA); |
502 | tpm_config_out(data: IOLIML, TPM_INF_ADDR); |
503 | tpm_config_out(data: (tpm_dev.data_regs & 0xff), TPM_INF_DATA); |
504 | |
505 | /* control if IO-ports are set correctly */ |
506 | tpm_config_out(data: IOLIMH, TPM_INF_ADDR); |
507 | ioh = tpm_config_in(TPM_INF_DATA); |
508 | tpm_config_out(data: IOLIML, TPM_INF_ADDR); |
509 | iol = tpm_config_in(TPM_INF_DATA); |
510 | |
511 | if ((ioh << 8 | iol) != tpm_dev.data_regs) { |
512 | dev_err(&dev->dev, |
513 | "Could not set IO-data registers to 0x%x\n" , |
514 | tpm_dev.data_regs); |
515 | rc = -EIO; |
516 | goto err_release_region; |
517 | } |
518 | |
519 | /* activate register */ |
520 | tpm_config_out(data: TPM_DAR, TPM_INF_ADDR); |
521 | tpm_config_out(data: 0x01, TPM_INF_DATA); |
522 | tpm_config_out(data: DISABLE_REGISTER_PAIR, TPM_INF_ADDR); |
523 | |
524 | /* disable RESET, LP and IRQC */ |
525 | tpm_data_out(data: RESET_LP_IRQC_DISABLE, offset: CMD); |
526 | |
527 | /* Finally, we're done, print some infos */ |
528 | dev_info(&dev->dev, "TPM found: " |
529 | "config base 0x%lx, " |
530 | "data base 0x%lx, " |
531 | "chip version 0x%02x%02x, " |
532 | "vendor id 0x%x%x (Infineon), " |
533 | "product id 0x%02x%02x" |
534 | "%s\n" , |
535 | tpm_dev.iotype == TPM_INF_IO_PORT ? |
536 | tpm_dev.config_port : |
537 | tpm_dev.map_base + tpm_dev.index_off, |
538 | tpm_dev.iotype == TPM_INF_IO_PORT ? |
539 | tpm_dev.data_regs : |
540 | tpm_dev.map_base + tpm_dev.data_regs, |
541 | version[0], version[1], |
542 | vendorid[0], vendorid[1], |
543 | productid[0], productid[1], chipname); |
544 | |
545 | chip = tpmm_chip_alloc(pdev: &dev->dev, ops: &tpm_inf); |
546 | if (IS_ERR(ptr: chip)) { |
547 | rc = PTR_ERR(ptr: chip); |
548 | goto err_release_region; |
549 | } |
550 | |
551 | rc = tpm_chip_register(chip); |
552 | if (rc) |
553 | goto err_release_region; |
554 | |
555 | return 0; |
556 | } else { |
557 | rc = -ENODEV; |
558 | goto err_release_region; |
559 | } |
560 | |
561 | err_release_region: |
562 | if (tpm_dev.iotype == TPM_INF_IO_PORT) { |
563 | release_region(tpm_dev.data_regs, tpm_dev.data_size); |
564 | release_region(tpm_dev.config_port, tpm_dev.config_size); |
565 | } else { |
566 | iounmap(addr: tpm_dev.mem_base); |
567 | release_mem_region(tpm_dev.map_base, tpm_dev.map_size); |
568 | } |
569 | |
570 | err_last: |
571 | return rc; |
572 | } |
573 | |
574 | static void tpm_inf_pnp_remove(struct pnp_dev *dev) |
575 | { |
576 | struct tpm_chip *chip = pnp_get_drvdata(pdev: dev); |
577 | |
578 | tpm_chip_unregister(chip); |
579 | |
580 | if (tpm_dev.iotype == TPM_INF_IO_PORT) { |
581 | release_region(tpm_dev.data_regs, tpm_dev.data_size); |
582 | release_region(tpm_dev.config_port, |
583 | tpm_dev.config_size); |
584 | } else { |
585 | iounmap(addr: tpm_dev.mem_base); |
586 | release_mem_region(tpm_dev.map_base, tpm_dev.map_size); |
587 | } |
588 | } |
589 | |
590 | #ifdef CONFIG_PM_SLEEP |
591 | static int tpm_inf_resume(struct device *dev) |
592 | { |
593 | /* Re-configure TPM after suspending */ |
594 | tpm_config_out(data: ENABLE_REGISTER_PAIR, TPM_INF_ADDR); |
595 | tpm_config_out(data: IOLIMH, TPM_INF_ADDR); |
596 | tpm_config_out(data: (tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA); |
597 | tpm_config_out(data: IOLIML, TPM_INF_ADDR); |
598 | tpm_config_out(data: (tpm_dev.data_regs & 0xff), TPM_INF_DATA); |
599 | /* activate register */ |
600 | tpm_config_out(data: TPM_DAR, TPM_INF_ADDR); |
601 | tpm_config_out(data: 0x01, TPM_INF_DATA); |
602 | tpm_config_out(data: DISABLE_REGISTER_PAIR, TPM_INF_ADDR); |
603 | /* disable RESET, LP and IRQC */ |
604 | tpm_data_out(data: RESET_LP_IRQC_DISABLE, offset: CMD); |
605 | return tpm_pm_resume(dev); |
606 | } |
607 | #endif |
608 | static SIMPLE_DEV_PM_OPS(tpm_inf_pm, tpm_pm_suspend, tpm_inf_resume); |
609 | |
610 | static struct pnp_driver tpm_inf_pnp_driver = { |
611 | .name = "tpm_inf_pnp" , |
612 | .id_table = tpm_inf_pnp_tbl, |
613 | .probe = tpm_inf_pnp_probe, |
614 | .remove = tpm_inf_pnp_remove, |
615 | .driver = { |
616 | .pm = &tpm_inf_pm, |
617 | } |
618 | }; |
619 | |
620 | module_pnp_driver(tpm_inf_pnp_driver); |
621 | |
622 | MODULE_AUTHOR("Marcel Selhorst <tpmdd@sirrix.com>" ); |
623 | MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2" ); |
624 | MODULE_VERSION("1.9.2" ); |
625 | MODULE_LICENSE("GPL" ); |
626 | |