Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93716b94 authored by Marcel Selhorst's avatar Marcel Selhorst Committed by Linus Torvalds
Browse files

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: default avatarMarcel 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f79f1185
Loading
Loading
Loading
Loading
+57 −22
Original line number Diff line number Diff line
@@ -406,14 +406,14 @@ static const struct tpm_vendor_specific tpm_inf = {
	.miscdev = {.fops = &inf_ops,},
};

static const struct pnp_device_id tpm_pnp_tbl[] = {
static const struct pnp_device_id tpm_inf_pnp_tbl[] = {
	/* Infineon TPMs */
	{"IFX0101", 0},
	{"IFX0102", 0},
	{"", 0}
};

MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
MODULE_DEVICE_TABLE(pnp, tpm_inf_pnp_tbl);

static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev,
				       const struct pnp_device_id *dev_id)
@@ -607,20 +607,55 @@ static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev)
			iounmap(tpm_dev.mem_base);
			release_mem_region(tpm_dev.map_base, tpm_dev.map_size);
		}
		tpm_dev_vendor_release(chip);
		tpm_remove_hardware(chip->dev);
	}
}

static int tpm_inf_pnp_suspend(struct pnp_dev *dev, pm_message_t pm_state)
{
	struct tpm_chip *chip = pnp_get_drvdata(dev);
	int rc;
	if (chip) {
		u8 savestate[] = {
			0, 193,	/* TPM_TAG_RQU_COMMAND */
			0, 0, 0, 10,	/* blob length (in bytes) */
			0, 0, 0, 152	/* TPM_ORD_SaveState */
		};
		dev_info(&dev->dev, "saving TPM state\n");
		rc = tpm_inf_send(chip, savestate, sizeof(savestate));
		if (rc < 0) {
			dev_err(&dev->dev, "error while saving TPM state\n");
			return rc;
		}
	}
	return 0;
}

static int tpm_inf_pnp_resume(struct pnp_dev *dev)
{
	/* Re-configure TPM after suspending */
	tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR);
	tpm_config_out(IOLIMH, TPM_INF_ADDR);
	tpm_config_out((tpm_dev.data_regs >> 8) & 0xff, TPM_INF_DATA);
	tpm_config_out(IOLIML, TPM_INF_ADDR);
	tpm_config_out((tpm_dev.data_regs & 0xff), TPM_INF_DATA);
	/* activate register */
	tpm_config_out(TPM_DAR, TPM_INF_ADDR);
	tpm_config_out(0x01, TPM_INF_DATA);
	tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR);
	/* disable RESET, LP and IRQC */
	tpm_data_out(RESET_LP_IRQC_DISABLE, CMD);
	return tpm_pm_resume(&dev->dev);
}

static struct pnp_driver tpm_inf_pnp_driver = {
	.name = "tpm_inf_pnp",
	.driver = {
		.owner = THIS_MODULE,
		.suspend = tpm_pm_suspend,
		.resume = tpm_pm_resume,
	},
	.id_table = tpm_pnp_tbl,
	.id_table = tpm_inf_pnp_tbl,
	.probe = tpm_inf_pnp_probe,
	.remove = __devexit_p(tpm_inf_pnp_remove),
	.suspend = tpm_inf_pnp_suspend,
	.resume = tpm_inf_pnp_resume,
	.remove = __devexit_p(tpm_inf_pnp_remove)
};

static int __init init_inf(void)
@@ -638,5 +673,5 @@ module_exit(cleanup_inf);

MODULE_AUTHOR("Marcel Selhorst <m.selhorst@sirrix.com>");
MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");
MODULE_VERSION("1.9");
MODULE_VERSION("1.9.2");
MODULE_LICENSE("GPL");