Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tpm_infineon: fix suspend/resume handler for pnp_driver

When suspending, tpm_infineon calls the generic suspend function of the
TPM framework. However, the TPM framework does not return and the system
hangs upon suspend. When sending the necessary command "TPM_SaveState"
directly within the driver, suspending and resuming works fine.

Signed-off-by: Marcel Selhorst <m.selhorst@sirrix.com>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Debora Velarde <debora@linux.vnet.ibm.com>
Cc: Rajiv Andrade <srajiv@linux.vnet.ibm.com>
Cc: <stable@kernel.org> [2.6.32.x]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Marcel Selhorst and committed by
Linus Torvalds
93716b94 f79f1185

+57 -22
+57 -22
drivers/char/tpm/tpm_infineon.c
··· 39 39 struct tpm_inf_dev { 40 40 int iotype; 41 41 42 - 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 */ 42 + 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 */ 46 46 47 - unsigned int data_regs; /* Data registers */ 47 + unsigned int data_regs; /* Data registers */ 48 48 unsigned int data_size; 49 49 50 50 unsigned int config_port; /* IO Port config index reg */ ··· 406 406 .miscdev = {.fops = &inf_ops,}, 407 407 }; 408 408 409 - static const struct pnp_device_id tpm_pnp_tbl[] = { 409 + static const struct pnp_device_id tpm_inf_pnp_tbl[] = { 410 410 /* Infineon TPMs */ 411 411 {"IFX0101", 0}, 412 412 {"IFX0102", 0}, 413 413 {"", 0} 414 414 }; 415 415 416 - MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl); 416 + MODULE_DEVICE_TABLE(pnp, tpm_inf_pnp_tbl); 417 417 418 418 static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, 419 419 const struct pnp_device_id *dev_id) ··· 430 430 if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && 431 431 !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) { 432 432 433 - tpm_dev.iotype = TPM_INF_IO_PORT; 433 + tpm_dev.iotype = TPM_INF_IO_PORT; 434 434 435 435 tpm_dev.config_port = pnp_port_start(dev, 0); 436 436 tpm_dev.config_size = pnp_port_len(dev, 0); ··· 459 459 goto err_last; 460 460 } 461 461 } else if (pnp_mem_valid(dev, 0) && 462 - !(pnp_mem_flags(dev, 0) & IORESOURCE_DISABLED)) { 462 + !(pnp_mem_flags(dev, 0) & IORESOURCE_DISABLED)) { 463 463 464 - tpm_dev.iotype = TPM_INF_IO_MEM; 464 + tpm_dev.iotype = TPM_INF_IO_MEM; 465 465 466 466 tpm_dev.map_base = pnp_mem_start(dev, 0); 467 467 tpm_dev.map_size = pnp_mem_len(dev, 0); ··· 563 563 "product id 0x%02x%02x" 564 564 "%s\n", 565 565 tpm_dev.iotype == TPM_INF_IO_PORT ? 566 - tpm_dev.config_port : 567 - tpm_dev.map_base + tpm_dev.index_off, 566 + tpm_dev.config_port : 567 + tpm_dev.map_base + tpm_dev.index_off, 568 568 tpm_dev.iotype == TPM_INF_IO_PORT ? 569 - tpm_dev.data_regs : 570 - tpm_dev.map_base + tpm_dev.data_regs, 569 + tpm_dev.data_regs : 570 + tpm_dev.map_base + tpm_dev.data_regs, 571 571 version[0], version[1], 572 572 vendorid[0], vendorid[1], 573 573 productid[0], productid[1], chipname); ··· 607 607 iounmap(tpm_dev.mem_base); 608 608 release_mem_region(tpm_dev.map_base, tpm_dev.map_size); 609 609 } 610 + tpm_dev_vendor_release(chip); 610 611 tpm_remove_hardware(chip->dev); 611 612 } 612 613 } 613 614 615 + static int tpm_inf_pnp_suspend(struct pnp_dev *dev, pm_message_t pm_state) 616 + { 617 + struct tpm_chip *chip = pnp_get_drvdata(dev); 618 + int rc; 619 + if (chip) { 620 + u8 savestate[] = { 621 + 0, 193, /* TPM_TAG_RQU_COMMAND */ 622 + 0, 0, 0, 10, /* blob length (in bytes) */ 623 + 0, 0, 0, 152 /* TPM_ORD_SaveState */ 624 + }; 625 + dev_info(&dev->dev, "saving TPM state\n"); 626 + rc = tpm_inf_send(chip, savestate, sizeof(savestate)); 627 + if (rc < 0) { 628 + dev_err(&dev->dev, "error while saving TPM state\n"); 629 + return rc; 630 + } 631 + } 632 + return 0; 633 + } 634 + 635 + static int tpm_inf_pnp_resume(struct pnp_dev *dev) 636 + { 637 + /* Re-configure TPM after suspending */ 638 + tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR); 639 + tpm_config_out(IOLIMH, TPM_INF_ADDR); 640 + tpm_config_out((tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA); 641 + tpm_config_out(IOLIML, TPM_INF_ADDR); 642 + tpm_config_out((tpm_dev.data_regs & 0xff), TPM_INF_DATA); 643 + /* activate register */ 644 + tpm_config_out(TPM_DAR, TPM_INF_ADDR); 645 + tpm_config_out(0x01, TPM_INF_DATA); 646 + tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR); 647 + /* disable RESET, LP and IRQC */ 648 + tpm_data_out(RESET_LP_IRQC_DISABLE, CMD); 649 + return tpm_pm_resume(&dev->dev); 650 + } 651 + 614 652 static struct pnp_driver tpm_inf_pnp_driver = { 615 653 .name = "tpm_inf_pnp", 616 - .driver = { 617 - .owner = THIS_MODULE, 618 - .suspend = tpm_pm_suspend, 619 - .resume = tpm_pm_resume, 620 - }, 621 - .id_table = tpm_pnp_tbl, 654 + .id_table = tpm_inf_pnp_tbl, 622 655 .probe = tpm_inf_pnp_probe, 623 - .remove = __devexit_p(tpm_inf_pnp_remove), 656 + .suspend = tpm_inf_pnp_suspend, 657 + .resume = tpm_inf_pnp_resume, 658 + .remove = __devexit_p(tpm_inf_pnp_remove) 624 659 }; 625 660 626 661 static int __init init_inf(void) ··· 673 638 674 639 MODULE_AUTHOR("Marcel Selhorst <m.selhorst@sirrix.com>"); 675 640 MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); 676 - MODULE_VERSION("1.9"); 641 + MODULE_VERSION("1.9.2"); 677 642 MODULE_LICENSE("GPL");