+diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
+index 692573e..da96c8e 100644
+--- a/drivers/char/tpm/Kconfig
++++ b/drivers/char/tpm/Kconfig
+@@ -23,7 +23,7 @@ if TCG_TPM
+
+ config TCG_TIS
+ tristate "TPM Interface Specification 1.2 Interface"
+- depends on PNP
++ depends on ACPI
+ ---help---
+ If you have a TPM security chip that is compliant with the
+ TCG TIS 1.2 TPM specification say Yes and it will be accessible
+diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
+index 3244389..ccdd828 100644
+--- a/drivers/char/tpm/tpm.c
++++ b/drivers/char/tpm/tpm.c
+@@ -429,134 +429,148 @@ out:
+ #define TPM_DIGEST_SIZE 20
+ #define TPM_ERROR_SIZE 10
+ #define TPM_RET_CODE_IDX 6
+-#define TPM_GET_CAP_RET_SIZE_IDX 10
+-#define TPM_GET_CAP_RET_UINT32_1_IDX 14
+-#define TPM_GET_CAP_RET_UINT32_2_IDX 18
+-#define TPM_GET_CAP_RET_UINT32_3_IDX 22
+-#define TPM_GET_CAP_RET_UINT32_4_IDX 26
+-#define TPM_GET_CAP_PERM_DISABLE_IDX 16
+-#define TPM_GET_CAP_PERM_INACTIVE_IDX 18
+-#define TPM_GET_CAP_RET_BOOL_1_IDX 14
+-#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16
+-
+-#define TPM_CAP_IDX 13
+-#define TPM_CAP_SUBCAP_IDX 21
+
+ enum tpm_capabilities {
+- TPM_CAP_FLAG = 4,
+- TPM_CAP_PROP = 5,
++ TPM_CAP_FLAG = cpu_to_be32(4),
++ TPM_CAP_PROP = cpu_to_be32(5),
++ CAP_VERSION_1_1 = cpu_to_be32(0x06),
++ CAP_VERSION_1_2 = cpu_to_be32(0x1A)
+ };
+
+ enum tpm_sub_capabilities {
+- TPM_CAP_PROP_PCR = 0x1,
+- TPM_CAP_PROP_MANUFACTURER = 0x3,
+- TPM_CAP_FLAG_PERM = 0x8,
+- TPM_CAP_FLAG_VOL = 0x9,
+- TPM_CAP_PROP_OWNER = 0x11,
+- TPM_CAP_PROP_TIS_TIMEOUT = 0x15,
+- TPM_CAP_PROP_TIS_DURATION = 0x20,
+-};
+-
+-/*
+- * This is a semi generic GetCapability command for use
+- * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG
+- * and their associated sub_capabilities.
+- */
++ TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
++ TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
++ TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
++ TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
++ TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
++ TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
++ TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
+
+-static const u8 tpm_cap[] = {
+- 0, 193, /* TPM_TAG_RQU_COMMAND */
+- 0, 0, 0, 22, /* length */
+- 0, 0, 0, 101, /* TPM_ORD_GetCapability */
+- 0, 0, 0, 0, /* TPM_CAP_<TYPE> */
+- 0, 0, 0, 4, /* TPM_CAP_SUB_<TYPE> size */
+- 0, 0, 1, 0 /* TPM_CAP_SUB_<TYPE> */
+ };
+
+-static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,
+- char *desc)
++static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
++ int len, const char *desc)
+ {
+ int err;
+
+- len = tpm_transmit(chip, data, len);
++ len = tpm_transmit(chip,(u8 *) cmd, len);
+ if (len < 0)
+ return len;
+ if (len == TPM_ERROR_SIZE) {
+- err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)));
++ err = be32_to_cpu(cmd->header.out.return_code);
+ dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
+ return err;
+ }
+ return 0;
+ }
+
++#define TPM_INTERNAL_RESULT_SIZE 200
++#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
++#define TPM_ORD_GET_CAP cpu_to_be32(101)
++
++static const struct tpm_input_header tpm_getcap_header = {
++ .tag = TPM_TAG_RQU_COMMAND,
++ .length = cpu_to_be32(22),
++ .ordinal = TPM_ORD_GET_CAP
++};
++
++ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
++ const char *desc)
++{
++ struct tpm_cmd_t tpm_cmd;
++ int rc;
++ struct tpm_chip *chip = dev_get_drvdata(dev);
++
++ tpm_cmd.header.in = tpm_getcap_header;
++ if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
++ tpm_cmd.params.getcap_in.cap = subcap_id;
++ /*subcap field not necessary */
++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
++ tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
++ } else {
++ if (subcap_id == TPM_CAP_FLAG_PERM ||
++ subcap_id == TPM_CAP_FLAG_VOL)
++ tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG;
++ else
++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
++ tpm_cmd.params.getcap_in.subcap = subcap_id;
++ }
++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
++ if (!rc)
++ *cap = tpm_cmd.params.getcap_out.cap;
++ return rc;
++}
++
+ void tpm_gen_interrupt(struct tpm_chip *chip)
+ {
+- u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
++ struct tpm_cmd_t tpm_cmd;
+ ssize_t rc;
+
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
++ tpm_cmd.header.in = tpm_getcap_header;
++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
++ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+
+- rc = transmit_cmd(chip, data, sizeof(data),
++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ "attempting to determine the timeouts");
+ }
+ EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
+
+ void tpm_get_timeouts(struct tpm_chip *chip)
+ {
+- u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
++ struct tpm_cmd_t tpm_cmd;
++ struct timeout_t *timeout_cap;
++ struct duration_t *duration_cap;
+ ssize_t rc;
+ u32 timeout;
+
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
++ tpm_cmd.header.in = tpm_getcap_header;
++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
++ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+
+- rc = transmit_cmd(chip, data, sizeof(data),
++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ "attempting to determine the timeouts");
+ if (rc)
+ goto duration;
+
+- if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
++ if (be32_to_cpu(tpm_cmd.header.out.length)
+ != 4 * sizeof(u32))
+ goto duration;
+
++ timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
+ /* Don't overwrite default if value is 0 */
+- timeout =
+- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
++ timeout = be32_to_cpu(timeout_cap->a);
+ if (timeout)
+ chip->vendor.timeout_a = usecs_to_jiffies(timeout);
+- timeout =
+- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
++ timeout = be32_to_cpu(timeout_cap->b);
+ if (timeout)
+ chip->vendor.timeout_b = usecs_to_jiffies(timeout);
+- timeout =
+- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
++ timeout = be32_to_cpu(timeout_cap->c);
+ if (timeout)
+ chip->vendor.timeout_c = usecs_to_jiffies(timeout);
+- timeout =
+- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
++ timeout = be32_to_cpu(timeout_cap->d);
+ if (timeout)
+ chip->vendor.timeout_d = usecs_to_jiffies(timeout);
+
+ duration:
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;
++ tpm_cmd.header.in = tpm_getcap_header;
++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
++ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
+
+- rc = transmit_cmd(chip, data, sizeof(data),
++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ "attempting to determine the durations");
+ if (rc)
+ return;
+
+- if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
++ if (be32_to_cpu(tpm_cmd.header.out.return_code)
+ != 3 * sizeof(u32))
+ return;
+-
++ duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
+ chip->vendor.duration[TPM_SHORT] =
+- usecs_to_jiffies(be32_to_cpu
+- (*((__be32 *) (data +
+- TPM_GET_CAP_RET_UINT32_1_IDX))));
++ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+ /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
+ * value wrong and apparently reports msecs rather than usecs. So we
+ * fix up the resulting too-small TPM_SHORT value to make things work.
+@@ -565,13 +579,9 @@ duration:
+ chip->vendor.duration[TPM_SHORT] = HZ;
+
+ chip->vendor.duration[TPM_MEDIUM] =
+- usecs_to_jiffies(be32_to_cpu
+- (*((__be32 *) (data +
+- TPM_GET_CAP_RET_UINT32_2_IDX))));
++ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+ chip->vendor.duration[TPM_LONG] =
+- usecs_to_jiffies(be32_to_cpu
+- (*((__be32 *) (data +
+- TPM_GET_CAP_RET_UINT32_3_IDX))));
++ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+ }
+ EXPORT_SYMBOL_GPL(tpm_get_timeouts);
+
+@@ -587,36 +597,18 @@ void tpm_continue_selftest(struct tpm_chip *chip)
+ }
+ EXPORT_SYMBOL_GPL(tpm_continue_selftest);
+
+-#define TPM_INTERNAL_RESULT_SIZE 200
+-
+ ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
+ char *buf)
+ {
+- u8 *data;
++ cap_t cap;
+ ssize_t rc;
+
+- struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+-
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_FLAG;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
+-
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+- "attemtping to determine the permanent enabled state");
+- if (rc) {
+- kfree(data);
++ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
++ "attempting to determine the permanent enabled state");
++ if (rc)
+ return 0;
+- }
+
+- rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]);
+-
+- kfree(data);
++ rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
+ return rc;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_enabled);
+@@ -624,31 +616,15 @@ EXPORT_SYMBOL_GPL(tpm_show_enabled);
+ ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
+ char *buf)
+ {
+- u8 *data;
++ cap_t cap;
+ ssize_t rc;
+
+- struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+-
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_FLAG;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
+-
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+- "attemtping to determine the permanent active state");
+- if (rc) {
+- kfree(data);
++ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
++ "attempting to determine the permanent active state");
++ if (rc)
+ return 0;
+- }
+-
+- rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]);
+
+- kfree(data);
++ rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
+ return rc;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_active);
+@@ -656,31 +632,15 @@ EXPORT_SYMBOL_GPL(tpm_show_active);
+ ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
+ char *buf)
+ {
+- u8 *data;
++ cap_t cap;
+ ssize_t rc;
+
+- struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+-
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER;
+-
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+- "attempting to determine the owner state");
+- if (rc) {
+- kfree(data);
++ rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
++ "attempting to determine the owner state");
++ if (rc)
+ return 0;
+- }
+
+- rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]);
+-
+- kfree(data);
++ rc = sprintf(buf, "%d\n", cap.owned);
+ return rc;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_owned);
+@@ -688,116 +648,180 @@ EXPORT_SYMBOL_GPL(tpm_show_owned);
+ ssize_t tpm_show_temp_deactivated(struct device * dev,
+ struct device_attribute * attr, char *buf)
+ {
+- u8 *data;
++ cap_t cap;
+ ssize_t rc;
+
+- struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
++ rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
++ "attempting to determine the temporary state");
++ if (rc)
++ return 0;
+
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
++ rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
++
++/*
++ * tpm_chip_find_get - return tpm_chip for given chip number
++ */
++static struct tpm_chip *tpm_chip_find_get(int chip_num)
++{
++ struct tpm_chip *pos, *chip = NULL;
+
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_FLAG;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL;
++ rcu_read_lock();
++ list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
++ if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
++ continue;
+
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+- "attempting to determine the temporary state");
+- if (rc) {
+- kfree(data);
+- return 0;
++ if (try_module_get(pos->dev->driver->owner)) {
++ chip = pos;
++ break;
++ }
+ }
++ rcu_read_unlock();
++ return chip;
++}
+
+- rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]);
++#define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
++#define READ_PCR_RESULT_SIZE 30
++static struct tpm_input_header pcrread_header = {
++ .tag = TPM_TAG_RQU_COMMAND,
++ .length = cpu_to_be32(14),
++ .ordinal = TPM_ORDINAL_PCRREAD
++};
+
+- kfree(data);
++int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
++{
++ int rc;
++ struct tpm_cmd_t cmd;
++
++ cmd.header.in = pcrread_header;
++ cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
++ BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE);
++ rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
++ "attempting to read a pcr value");
++
++ if (rc == 0)
++ memcpy(res_buf, cmd.params.pcrread_out.pcr_result,
++ TPM_DIGEST_SIZE);
+ return rc;
+ }
+-EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
+
+-static const u8 pcrread[] = {
+- 0, 193, /* TPM_TAG_RQU_COMMAND */
+- 0, 0, 0, 14, /* length */
+- 0, 0, 0, 21, /* TPM_ORD_PcrRead */
+- 0, 0, 0, 0 /* PCR index */
++/**
++ * tpm_pcr_read - read a pcr value
++ * @chip_num: tpm idx # or ANY
++ * @pcr_idx: pcr idx to retrieve
++ * @res_buf: TPM_PCR value
++ * size of res_buf is 20 bytes (or NULL if you don't care)
++ *
++ * The TPM driver should be built-in, but for whatever reason it
++ * isn't, protect against the chip disappearing, by incrementing
++ * the module usage count.
++ */
++int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
++{
++ struct tpm_chip *chip;
++ int rc;
++
++ chip = tpm_chip_find_get(chip_num);
++ if (chip == NULL)
++ return -ENODEV;
++ rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
++ module_put(chip->dev->driver->owner);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(tpm_pcr_read);
++
++/**
++ * tpm_pcr_extend - extend pcr value with hash
++ * @chip_num: tpm idx # or AN&
++ * @pcr_idx: pcr idx to extend
++ * @hash: hash value used to extend pcr value
++ *
++ * The TPM driver should be built-in, but for whatever reason it
++ * isn't, protect against the chip disappearing, by incrementing
++ * the module usage count.
++ */
++#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
++#define EXTEND_PCR_SIZE 34
++static struct tpm_input_header pcrextend_header = {
++ .tag = TPM_TAG_RQU_COMMAND,
++ .length = cpu_to_be32(34),
++ .ordinal = TPM_ORD_PCR_EXTEND
+ };
+
++int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
++{
++ struct tpm_cmd_t cmd;
++ int rc;
++ struct tpm_chip *chip;
++
++ chip = tpm_chip_find_get(chip_num);
++ if (chip == NULL)
++ return -ENODEV;
++
++ cmd.header.in = pcrextend_header;
++ BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE);
++ cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
++ memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
++ rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
++ "attempting extend a PCR value");
++
++ module_put(chip->dev->driver->owner);
++ return rc;
++}
++EXPORT_SYMBOL_GPL(tpm_pcr_extend);
++
+ ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+- u8 *data;
++ cap_t cap;
++ u8 digest[TPM_DIGEST_SIZE];
+ ssize_t rc;
+ int i, j, num_pcrs;
+- __be32 index;
+ char *str = buf;
+-
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+-
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR;
+
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
++ rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
+ "attempting to determine the number of PCRS");
+- if (rc) {
+- kfree(data);
++ if (rc)
+ return 0;
+- }
+
+- num_pcrs = be32_to_cpu(*((__be32 *) (data + 14)));
++ num_pcrs = be32_to_cpu(cap.num_pcrs);
+ for (i = 0; i < num_pcrs; i++) {
+- memcpy(data, pcrread, sizeof(pcrread));
+- index = cpu_to_be32(i);
+- memcpy(data + 10, &index, 4);
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+- "attempting to read a PCR");
++ rc = __tpm_pcr_read(chip, i, digest);
+ if (rc)
+- goto out;
++ break;
+ str += sprintf(str, "PCR-%02d: ", i);
+ for (j = 0; j < TPM_DIGEST_SIZE; j++)
+- str += sprintf(str, "%02X ", *(data + 10 + j));
++ str += sprintf(str, "%02X ", digest[j]);
+ str += sprintf(str, "\n");
+ }
+-out:
+- kfree(data);
+ return str - buf;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_pcrs);
+
+ #define READ_PUBEK_RESULT_SIZE 314
+-static const u8 readpubek[] = {
+- 0, 193, /* TPM_TAG_RQU_COMMAND */
+- 0, 0, 0, 30, /* length */
+- 0, 0, 0, 124, /* TPM_ORD_ReadPubek */
++#define TPM_ORD_READPUBEK cpu_to_be32(124)
++struct tpm_input_header tpm_readpubek_header = {
++ .tag = TPM_TAG_RQU_COMMAND,
++ .length = cpu_to_be32(30),
++ .ordinal = TPM_ORD_READPUBEK
+ };
+
+ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+ u8 *data;
++ struct tpm_cmd_t tpm_cmd;
+ ssize_t err;
+ int i, rc;
+ char *str = buf;
+
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+
+- data = kzalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, readpubek, sizeof(readpubek));
+-
+- err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE,
++ tpm_cmd.header.in = tpm_readpubek_header;
++ err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
+ "attempting to read the PUBEK");
+ if (err)
+ goto out;
+@@ -812,7 +836,7 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
+ 256 byte modulus
+ ignore checksum 20 bytes
+ */
+-
++ data = tpm_cmd.params.readpubek_out_buffer;
+ str +=
+ sprintf(str,
+ "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
+@@ -832,65 +856,33 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
+ }
+ out:
+ rc = str - buf;
+- kfree(data);
+ return rc;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_pubek);
+
+-#define CAP_VERSION_1_1 6
+-#define CAP_VERSION_1_2 0x1A
+-#define CAP_VERSION_IDX 13
+-static const u8 cap_version[] = {
+- 0, 193, /* TPM_TAG_RQU_COMMAND */
+- 0, 0, 0, 18, /* length */
+- 0, 0, 0, 101, /* TPM_ORD_GetCapability */
+- 0, 0, 0, 0,
+- 0, 0, 0, 0
+-};
+
+ ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+- u8 *data;
++ cap_t cap;
+ ssize_t rc;
+ char *str = buf;
+
+- struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+-
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
+-
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
++ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+ "attempting to determine the manufacturer");
+- if (rc) {
+- kfree(data);
++ if (rc)
+ return 0;
+- }
+-
+ str += sprintf(str, "Manufacturer: 0x%x\n",
+- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
++ be32_to_cpu(cap.manufacturer_id));
+
+- memcpy(data, cap_version, sizeof(cap_version));
+- data[CAP_VERSION_IDX] = CAP_VERSION_1_1;
+- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+- "attempting to determine the 1.1 version");
++ rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
++ "attempting to determine the 1.1 version");
+ if (rc)
+- goto out;
+-
++ return 0;
+ str += sprintf(str,
+ "TCG version: %d.%d\nFirmware version: %d.%d\n",
+- (int) data[14], (int) data[15], (int) data[16],
+- (int) data[17]);
+-
+-out:
+- kfree(data);
++ cap.tpm_version.Major, cap.tpm_version.Minor,
++ cap.tpm_version.revMajor, cap.tpm_version.revMinor);
+ return str - buf;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_caps);
+@@ -898,51 +890,25 @@ EXPORT_SYMBOL_GPL(tpm_show_caps);
+ ssize_t tpm_show_caps_1_2(struct device * dev,
+ struct device_attribute * attr, char *buf)
+ {
+- u8 *data;
+- ssize_t len;
++ cap_t cap;
++ ssize_t rc;
+ char *str = buf;
+
+- struct tpm_chip *chip = dev_get_drvdata(dev);
+- if (chip == NULL)
+- return -ENODEV;
+-
+- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
+- if (!data)
+- return -ENOMEM;
+-
+- memcpy(data, tpm_cap, sizeof(tpm_cap));
+- data[TPM_CAP_IDX] = TPM_CAP_PROP;
+- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
+-
+- len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
+- if (len <= TPM_ERROR_SIZE) {
+- dev_dbg(chip->dev, "A TPM error (%d) occurred "
+- "attempting to determine the manufacturer\n",
+- be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
+- kfree(data);
++ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
++ "attempting to determine the manufacturer");
++ if (rc)
+ return 0;
+- }
+-
+ str += sprintf(str, "Manufacturer: 0x%x\n",
+- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
+-
+- memcpy(data, cap_version, sizeof(cap_version));
+- data[CAP_VERSION_IDX] = CAP_VERSION_1_2;
+-
+- len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
+- if (len <= TPM_ERROR_SIZE) {
+- dev_err(chip->dev, "A TPM error (%d) occurred "
+- "attempting to determine the 1.2 version\n",
+- be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
+- goto out;
+- }
++ be32_to_cpu(cap.manufacturer_id));
++ rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
++ "attempting to determine the 1.2 version");
++ if (rc)
++ return 0;
+ str += sprintf(str,
+ "TCG version: %d.%d\nFirmware version: %d.%d\n",
+- (int) data[16], (int) data[17], (int) data[18],
+- (int) data[19]);
+-
+-out:
+- kfree(data);
++ cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
++ cap.tpm_version_1_2.revMajor,
++ cap.tpm_version_1_2.revMinor);
+ return str - buf;
+ }
+ EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
+@@ -961,72 +927,63 @@ EXPORT_SYMBOL_GPL(tpm_store_cancel);
+
+ /*
+ * Device file system interface to the TPM
++ *
++ * It's assured that the chip will be opened just once,
++ * by the check of is_open variable, which is protected
++ * by driver_lock.
+ */
+ int tpm_open(struct inode *inode, struct file *file)
+ {
+- int rc = 0, minor = iminor(inode);
++ int minor = iminor(inode);
+ struct tpm_chip *chip = NULL, *pos;
+
+- lock_kernel();
+- spin_lock(&driver_lock);
+-
+- list_for_each_entry(pos, &tpm_chip_list, list) {
++ rcu_read_lock();
++ list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+ if (pos->vendor.miscdev.minor == minor) {
+ chip = pos;
++ get_device(chip->dev);
+ break;
+ }
+ }
++ rcu_read_unlock();
+
+- if (chip == NULL) {
+- rc = -ENODEV;
+- goto err_out;
+- }
++ if (!chip)
++ return -ENODEV;
+
+- if (chip->num_opens) {
++ if (test_and_set_bit(0, &chip->is_open)) {
+ dev_dbg(chip->dev, "Another process owns this TPM\n");
+- rc = -EBUSY;
+- goto err_out;
++ put_device(chip->dev);
++ return -EBUSY;
+ }
+
+- chip->num_opens++;
+- get_device(chip->dev);
+-
+- spin_unlock(&driver_lock);
+-
+ chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+ if (chip->data_buffer == NULL) {
+- chip->num_opens--;
++ clear_bit(0, &chip->is_open);
+ put_device(chip->dev);
+- unlock_kernel();
+ return -ENOMEM;
+ }
+
+ atomic_set(&chip->data_pending, 0);
+
+ file->private_data = chip;
+- unlock_kernel();
+ return 0;
+-
+-err_out:
+- spin_unlock(&driver_lock);
+- unlock_kernel();
+- return rc;
+ }
+ EXPORT_SYMBOL_GPL(tpm_open);
+
++/*
++ * Called on file close
++ */
+ int tpm_release(struct inode *inode, struct file *file)
+ {
+ struct tpm_chip *chip = file->private_data;
+
++ del_singleshot_timer_sync(&chip->user_read_timer);
+ flush_scheduled_work();
+- spin_lock(&driver_lock);
+ file->private_data = NULL;
+- del_singleshot_timer_sync(&chip->user_read_timer);
+ atomic_set(&chip->data_pending, 0);
+- chip->num_opens--;
+- put_device(chip->dev);
+ kfree(chip->data_buffer);
+- spin_unlock(&driver_lock);
++ clear_bit(0, &chip->is_open);
++ put_device(chip->dev);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(tpm_release);
+@@ -1100,13 +1057,11 @@ void tpm_remove_hardware(struct device *dev)
+ }
+
+ spin_lock(&driver_lock);
+-
+- list_del(&chip->list);
+-
++ list_del_rcu(&chip->list);
+ spin_unlock(&driver_lock);
++ synchronize_rcu();
+
+ misc_deregister(&chip->vendor.miscdev);
+-
+ sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
+ tpm_bios_log_teardown(chip->bios_dir);
+
+@@ -1151,25 +1106,33 @@ int tpm_pm_resume(struct device *dev)
+ }
+ EXPORT_SYMBOL_GPL(tpm_pm_resume);
+
++/* In case vendor provided release function, call it too.*/
++
++void tpm_dev_vendor_release(struct tpm_chip *chip)
++{
++ if (chip->vendor.release)
++ chip->vendor.release(chip->dev);
++
++ clear_bit(chip->dev_num, dev_mask);
++ kfree(chip->vendor.miscdev.name);
++}
++EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
++
++
+ /*
+ * Once all references to platform device are down to 0,
+ * release all allocated structures.
+- * In case vendor provided release function,
+- * call it too.
+ */
+-static void tpm_dev_release(struct device *dev)
++void tpm_dev_release(struct device *dev)
+ {
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+- if (chip->vendor.release)
+- chip->vendor.release(dev);
++ tpm_dev_vendor_release(chip);
+
+ chip->release(dev);
+-
+- clear_bit(chip->dev_num, dev_mask);
+- kfree(chip->vendor.miscdev.name);
+ kfree(chip);
+ }
++EXPORT_SYMBOL_GPL(tpm_dev_release);
+
+ /*
+ * Called from tpm_<specific>.c probe function only for devices
+@@ -1178,8 +1141,8 @@ static void tpm_dev_release(struct device *dev)
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+-struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific
+- *entry)
++struct tpm_chip *tpm_register_hardware(struct device *dev,
++ const struct tpm_vendor_specific *entry)
+ {
+ #define DEVNAME_SIZE 7
+
+@@ -1190,11 +1153,8 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL);
+
+- if (chip == NULL || devname == NULL) {
+- kfree(chip);
+- kfree(devname);
+- return NULL;
+- }
++ if (chip == NULL || devname == NULL)
++ goto out_free;
+
+ mutex_init(&chip->buffer_mutex);
+ mutex_init(&chip->tpm_mutex);
+@@ -1211,8 +1171,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
+
+ if (chip->dev_num >= TPM_NUM_DEVICES) {
+ dev_err(dev, "No available tpm device numbers\n");
+- kfree(chip);
+- return NULL;
++ goto out_free;
+ } else if (chip->dev_num == 0)
+ chip->vendor.miscdev.minor = TPM_MINOR;
+ else
+@@ -1238,22 +1197,26 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
+ return NULL;
+ }
+
+- spin_lock(&driver_lock);
+-
+- list_add(&chip->list, &tpm_chip_list);
+-
+- spin_unlock(&driver_lock);
+-
+ if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) {
+- list_del(&chip->list);
+ misc_deregister(&chip->vendor.miscdev);
+ put_device(chip->dev);
++
+ return NULL;
+ }
+
+ chip->bios_dir = tpm_bios_log_setup(devname);
+
++ /* Make chip available */
++ spin_lock(&driver_lock);
++ list_add_rcu(&chip->list, &tpm_chip_list);
++ spin_unlock(&driver_lock);
++
+ return chip;
++
++out_free:
++ kfree(chip);
++ kfree(devname);
++ return NULL;
+ }
+ EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
+index 557550a..8e00b4d 100644
+--- a/drivers/char/tpm/tpm.h
++++ b/drivers/char/tpm/tpm.h
+@@ -26,6 +26,7 @@
+ #include <linux/miscdevice.h>
+ #include <linux/platform_device.h>
+ #include <linux/io.h>
++#include <linux/tpm.h>
+
+ enum tpm_timeout {
+ TPM_TIMEOUT = 5, /* msecs */
+@@ -90,7 +91,7 @@ struct tpm_chip {
+ struct device *dev; /* Device stuff */
+
+ int dev_num; /* /dev/tpm# */
+- int num_opens; /* only one allowed */
++ unsigned long is_open; /* only one allowed */
+ int time_expired;
+
+ /* Data passed to and from the tpm via the read/write calls */
+@@ -107,9 +108,6 @@ struct tpm_chip {
+ struct dentry **bios_dir;
+
+ struct list_head list;
+-#ifdef CONFIG_XEN
+- void *priv;
+-#endif
+ void (*release) (struct device *);
+ };
+
+@@ -126,18 +124,147 @@ static inline void tpm_write_index(int base, int index, int value)
+ outb(index, base);
+ outb(value & 0xFF, base+1);
+ }
++struct tpm_input_header {
++ __be16 tag;
++ __be32 length;
++ __be32 ordinal;
++}__attribute__((packed));
+
+-#ifdef CONFIG_XEN
+-static inline void *chip_get_private(const struct tpm_chip *chip)
+-{
+- return chip->priv;
+-}
++struct tpm_output_header {
++ __be16 tag;
++ __be32 length;
++ __be32 return_code;
++}__attribute__((packed));
+
+-static inline void chip_set_private(struct tpm_chip *chip, void *priv)
+-{
+- chip->priv = priv;
+-}
+-#endif
++struct stclear_flags_t {
++ __be16 tag;
++ u8 deactivated;
++ u8 disableForceClear;
++ u8 physicalPresence;
++ u8 physicalPresenceLock;
++ u8 bGlobalLock;
++}__attribute__((packed));
++
++struct tpm_version_t {
++ u8 Major;
++ u8 Minor;
++ u8 revMajor;
++ u8 revMinor;
++}__attribute__((packed));
++
++struct tpm_version_1_2_t {
++ __be16 tag;
++ u8 Major;
++ u8 Minor;
++ u8 revMajor;
++ u8 revMinor;
++}__attribute__((packed));
++
++struct timeout_t {
++ __be32 a;
++ __be32 b;
++ __be32 c;
++ __be32 d;
++}__attribute__((packed));
++
++struct duration_t {
++ __be32 tpm_short;
++ __be32 tpm_medium;
++ __be32 tpm_long;
++}__attribute__((packed));
++
++struct permanent_flags_t {
++ __be16 tag;
++ u8 disable;
++ u8 ownership;
++ u8 deactivated;
++ u8 readPubek;
++ u8 disableOwnerClear;
++ u8 allowMaintenance;
++ u8 physicalPresenceLifetimeLock;
++ u8 physicalPresenceHWEnable;
++ u8 physicalPresenceCMDEnable;
++ u8 CEKPUsed;
++ u8 TPMpost;
++ u8 TPMpostLock;
++ u8 FIPS;
++ u8 operator;
++ u8 enableRevokeEK;
++ u8 nvLocked;
++ u8 readSRKPub;
++ u8 tpmEstablished;
++ u8 maintenanceDone;
++ u8 disableFullDALogicInfo;
++}__attribute__((packed));
++
++typedef union {
++ struct permanent_flags_t perm_flags;
++ struct stclear_flags_t stclear_flags;
++ bool owned;
++ __be32 num_pcrs;
++ struct tpm_version_t tpm_version;
++ struct tpm_version_1_2_t tpm_version_1_2;
++ __be32 manufacturer_id;
++ struct timeout_t timeout;
++ struct duration_t duration;
++} cap_t;
++
++struct tpm_getcap_params_in {
++ __be32 cap;
++ __be32 subcap_size;
++ __be32 subcap;
++}__attribute__((packed));
++
++struct tpm_getcap_params_out {
++ __be32 cap_size;
++ cap_t cap;
++}__attribute__((packed));
++
++struct tpm_readpubek_params_out {
++ u8 algorithm[4];
++ u8 encscheme[2];
++ u8 sigscheme[2];
++ u8 parameters[12]; /*assuming RSA*/
++ __be32 keysize;
++ u8 modulus[256];
++ u8 checksum[20];
++}__attribute__((packed));
++
++typedef union {
++ struct tpm_input_header in;
++ struct tpm_output_header out;
++} tpm_cmd_header;
++
++#define TPM_DIGEST_SIZE 20
++struct tpm_pcrread_out {
++ u8 pcr_result[TPM_DIGEST_SIZE];
++}__attribute__((packed));
++
++struct tpm_pcrread_in {
++ __be32 pcr_idx;
++}__attribute__((packed));
++
++struct tpm_pcrextend_in {
++ __be32 pcr_idx;
++ u8 hash[TPM_DIGEST_SIZE];
++}__attribute__((packed));
++
++typedef union {
++ struct tpm_getcap_params_out getcap_out;
++ struct tpm_readpubek_params_out readpubek_out;
++ u8 readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)];
++ struct tpm_getcap_params_in getcap_in;
++ struct tpm_pcrread_in pcrread_in;
++ struct tpm_pcrread_out pcrread_out;
++ struct tpm_pcrextend_in pcrextend_in;
++} tpm_cmd_params;
++
++struct tpm_cmd_t {
++ tpm_cmd_header header;
++ tpm_cmd_params params;
++}__attribute__((packed));
++
++ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
+
+ extern void tpm_get_timeouts(struct tpm_chip *);
+ extern void tpm_gen_interrupt(struct tpm_chip *);
+@@ -147,6 +274,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *,
+ const struct tpm_vendor_specific *);
+ extern int tpm_open(struct inode *, struct file *);
+ extern int tpm_release(struct inode *, struct file *);
++extern void tpm_dev_vendor_release(struct tpm_chip *);
+ extern ssize_t tpm_write(struct file *, const char __user *, size_t,
+ loff_t *);
+ extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
+diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
+index d0e7926..c64a1bc 100644
+--- a/drivers/char/tpm/tpm_atmel.c
++++ b/drivers/char/tpm/tpm_atmel.c
+@@ -168,12 +168,22 @@ static void atml_plat_remove(void)
+ }
+ }
+
+-static struct device_driver atml_drv = {
+- .name = "tpm_atmel",
+- .bus = &platform_bus_type,
+- .owner = THIS_MODULE,
+- .suspend = tpm_pm_suspend,
+- .resume = tpm_pm_resume,
++static int tpm_atml_suspend(struct platform_device *dev, pm_message_t msg)
++{
++ return tpm_pm_suspend(&dev->dev, msg);
++}
++
++static int tpm_atml_resume(struct platform_device *dev)
++{
++ return tpm_pm_resume(&dev->dev);
++}
++static struct platform_driver atml_drv = {
++ .driver = {
++ .name = "tpm_atmel",
++ .owner = THIS_MODULE,
++ },
++ .suspend = tpm_atml_suspend,
++ .resume = tpm_atml_resume,
+ };
+
+ static int __init init_atmel(void)
+@@ -184,7 +194,7 @@ static int __init init_atmel(void)
+ unsigned long base;
+ struct tpm_chip *chip;
+
+- rc = driver_register(&atml_drv);
++ rc = platform_driver_register(&atml_drv);
+ if (rc)
+ return rc;
+
+@@ -223,13 +233,13 @@ err_rel_reg:
+ atmel_release_region(base,
+ region_size);
+ err_unreg_drv:
+- driver_unregister(&atml_drv);
++ platform_driver_unregister(&atml_drv);
+ return rc;
+ }
+
+ static void __exit cleanup_atmel(void)
+ {
+- driver_unregister(&atml_drv);
++ platform_driver_unregister(&atml_drv);
+ atml_plat_remove();
+ }
+
+diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c
+index 68f052b..0c2f55a 100644
+--- a/drivers/char/tpm/tpm_bios.c
++++ b/drivers/char/tpm/tpm_bios.c
+@@ -23,8 +23,6 @@
+ #include <linux/security.h>
+ #include <linux/module.h>
+ #include <acpi/acpi.h>
+-#include <acpi/actypes.h>
+-#include <acpi/actbl.h>
+ #include "tpm.h"
+
+ #define TCG_EVENT_NAME_LEN_MAX 255
+@@ -214,7 +212,8 @@ static int get_event_name(char *dest, struct tcpa_event *event,
+ unsigned char * event_entry)
+ {
+ const char *name = "";
+- char data[40] = "";
++ /* 41 so there is room for 40 data and 1 nul */
++ char data[41] = "";
+ int i, n_len = 0, d_len = 0;
+ struct tcpa_pc_event *pc_event;
+
+diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
+index 726ee8a..ecba494 100644
+--- a/drivers/char/tpm/tpm_infineon.c
++++ b/drivers/char/tpm/tpm_infineon.c
+@@ -4,7 +4,7 @@
+ * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module
+ * Specifications at www.trustedcomputinggroup.org
+ *
+- * Copyright (C) 2005, Marcel Selhorst <selhorst@crypto.rub.de>
++ * Copyright (C) 2005, Marcel Selhorst <m.selhorst@sirrix.com>
+ * Sirrix AG - security technologies, http://www.sirrix.com and
+ * Applied Data Security Group, Ruhr-University Bochum, Germany
+ * Project-Homepage: http://www.prosec.rub.de/tpm
+@@ -636,7 +636,7 @@ static void __exit cleanup_inf(void)
+ module_init(init_inf);
+ module_exit(cleanup_inf);
+
+-MODULE_AUTHOR("Marcel Selhorst <selhorst@crypto.rub.de>");
++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_LICENSE("GPL");
+diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
+index ab18c1e..70efba2 100644
+--- a/drivers/char/tpm/tpm_nsc.c
++++ b/drivers/char/tpm/tpm_nsc.c
+@@ -273,12 +273,23 @@ static void tpm_nsc_remove(struct device *dev)
+ }
+ }
+
+-static struct device_driver nsc_drv = {
+- .name = "tpm_nsc",
+- .bus = &platform_bus_type,
+- .owner = THIS_MODULE,
+- .suspend = tpm_pm_suspend,
+- .resume = tpm_pm_resume,
++static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg)
++{
++ return tpm_pm_suspend(&dev->dev, msg);
++}
++
++static int tpm_nsc_resume(struct platform_device *dev)
++{
++ return tpm_pm_resume(&dev->dev);
++}
++
++static struct platform_driver nsc_drv = {
++ .suspend = tpm_nsc_suspend,
++ .resume = tpm_nsc_resume,
++ .driver = {
++ .name = "tpm_nsc",
++ .owner = THIS_MODULE,
++ },
+ };
+
+ static int __init init_nsc(void)
+@@ -297,7 +308,7 @@ static int __init init_nsc(void)
+ return -ENODEV;
+ }
+
+- err = driver_register(&nsc_drv);
++ err = platform_driver_register(&nsc_drv);
+ if (err)
+ return err;
+
+@@ -308,17 +319,15 @@ static int __init init_nsc(void)
+ /* enable the DPM module */
+ tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01);
+
+- pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
++ pdev = platform_device_alloc("tpm_nscl0", -1);
+ if (!pdev) {
+ rc = -ENOMEM;
+ goto err_unreg_drv;
+ }
+
+- pdev->name = "tpm_nscl0";
+- pdev->id = -1;
+ pdev->num_resources = 0;
++ pdev->dev.driver = &nsc_drv.driver;
+ pdev->dev.release = tpm_nsc_remove;
+- pdev->dev.driver = &nsc_drv;
+
+ if ((rc = platform_device_register(pdev)) < 0)
+ goto err_free_dev;
+@@ -377,7 +386,7 @@ err_unreg_dev:
+ err_free_dev:
+ kfree(pdev);
+ err_unreg_drv:
+- driver_unregister(&nsc_drv);
++ platform_driver_unregister(&nsc_drv);
+ return rc;
+ }
+
+@@ -390,7 +399,7 @@ static void __exit cleanup_nsc(void)
+ pdev = NULL;
+ }
+
+- driver_unregister(&nsc_drv);
++ platform_driver_unregister(&nsc_drv);
+ }
+
+ module_init(init_nsc);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
-index ed1879c..b83b305 100644
+index ed1879c..22b2a69 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
-@@ -293,7 +293,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+@@ -21,9 +21,12 @@
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+-#include <linux/pnp.h>
+ #include <linux/interrupt.h>
+ #include <linux/wait.h>
++
++#include <linux/acpi.h>
++#include <acpi/acpi_bus.h>
++
+ #include "tpm.h"
+
+ #define TPM_HEADER_SIZE 10
+@@ -77,6 +80,14 @@ enum tis_defaults {
+ static LIST_HEAD(tis_chips);
+ static DEFINE_SPINLOCK(tis_lock);
+
++struct tpm_data {
++ unsigned long tpm_phys_address;
++ void __iomem *tpm_address;
++ int tpm_size;
++ int tpm_irq;
++ int itpm;
++};
++
+ static int check_locality(struct tpm_chip *chip, int l)
+ {
+ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+@@ -267,6 +278,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+ int rc, status, burstcnt;
+ size_t count = 0;
+ u32 ordinal;
++ struct tpm_data *tpm = to_acpi_device(chip->dev)->driver_data;
+
+ if (request_locality(chip, 0) < 0)
+ return -EBUSY;
+@@ -293,7 +305,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
&chip->vendor.int_queue);
status = tpm_tis_status(chip);
- if ((status & TPM_STS_DATA_EXPECT) == 0) {
-+ if ((status & TPM_STS_VALID) == 0) {
++ if (!tpm->itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO;
goto out_err;
}
-@@ -430,7 +430,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
- return IRQ_HANDLED;
- }
-
--static int interrupts = 1;
-+static int interrupts = 0;
+@@ -434,12 +446,17 @@ static int interrupts = 1;
module_param(interrupts, bool, 0444);
MODULE_PARM_DESC(interrupts, "Enable interrupts");
-@@ -450,19 +450,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
++static int itpm;
++module_param(itpm, bool, 0444);
++MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
++
+ static int tpm_tis_init(struct device *dev, resource_size_t start,
+ resource_size_t len, unsigned int irq)
+ {
+ u32 vendor, intfcaps, intmask;
+ int rc, i;
+ struct tpm_chip *chip;
++ struct tpm_data *tpm = to_acpi_device(dev)->driver_data;
+
+ if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
+ return -ENODEV;
+@@ -450,6 +467,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
goto out_err;
}
-- if (request_locality(chip, 0) != 0) {
-- rc = -ENODEV;
-- goto out_err;
-- }
--
-- vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
++ /* Default timeouts */
++ chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
++ chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
++ chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
++ chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
++
+ if (request_locality(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+@@ -457,16 +480,15 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
+
+ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+
+- /* Default timeouts */
+- chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+- chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+- chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+- chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
-
- /* Default timeouts */
- chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
- chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ dev_info(dev,
+- "1.2 TPM (device-id 0x%X, rev-id %d)\n",
++ "1.2 TPM (%04X:%04X rev %d)\n", vendor & 0xffff,
+ vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
-+ if (request_locality(chip, 0) != 0) {
-+ rc = -ENODEV;
-+ goto out_err;
++ if (itpm || vendor == 0x10208086) {
++ dev_info(dev, "Intel iTPM workaround enabled\n");
++ tpm->itpm = 1;
+ }
+
-+ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+ /* Figure out the capabilities */
+ intfcaps =
+ ioread32(chip->vendor.iobase +
+@@ -474,23 +496,23 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
+ dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
+ intfcaps);
+ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+- dev_dbg(dev, "\tBurst Count Static\n");
++ dev_dbg(dev, " Burst Count Static\n");
+ if (intfcaps & TPM_INTF_CMD_READY_INT)
+- dev_dbg(dev, "\tCommand Ready Int Support\n");
++ dev_dbg(dev, " Command Ready Int Support\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+- dev_dbg(dev, "\tInterrupt Edge Falling\n");
++ dev_dbg(dev, " Interrupt Edge Falling\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+- dev_dbg(dev, "\tInterrupt Edge Rising\n");
++ dev_dbg(dev, " Interrupt Edge Rising\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+- dev_dbg(dev, "\tInterrupt Level Low\n");
++ dev_dbg(dev, " Interrupt Level Low\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+- dev_dbg(dev, "\tInterrupt Level High\n");
++ dev_dbg(dev, " Interrupt Level High\n");
+ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
+- dev_dbg(dev, "\tLocality Change Int Support\n");
++ dev_dbg(dev, " Locality Change Int Support\n");
+ if (intfcaps & TPM_INTF_STS_VALID_INT)
+- dev_dbg(dev, "\tSts Valid Int Support\n");
++ dev_dbg(dev, " Sts Valid Int Support\n");
+ if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
+- dev_dbg(dev, "\tData Avail Int Support\n");
++ dev_dbg(dev, " Data Avail Int Support\n");
+
+ /* INTERRUPT Setup */
+ init_waitqueue_head(&chip->vendor.read_queue);
+@@ -590,34 +612,114 @@ out_err:
+ return rc;
+ }
+
+-static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
+- const struct pnp_device_id *pnp_id)
++static acpi_status tpm_resources(struct acpi_resource *res, void *data)
+ {
+- resource_size_t start, len;
+- unsigned int irq = 0;
++ struct acpi_device *device = data;
++ struct device *dev = &device->dev;
++ struct tpm_data *tpm = device->driver_data;
++ acpi_status status;
++ struct acpi_resource_address64 addr;
+
- dev_info(dev,
- "1.2 TPM (device-id 0x%X, rev-id %d)\n",
- vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
-@@ -653,7 +653,7 @@ static struct device_driver tis_drv = {
++ status = acpi_resource_to_address64(res, &addr);
++
++ if (ACPI_SUCCESS(status)) {
++ dev_info(dev, "found 0x%llx(0x%llx)\n",
++ (long long)addr.minimum,
++ (long long)addr.address_length);
++ tpm->tpm_phys_address = addr.minimum;
++ tpm->tpm_address = ioremap(addr.minimum, addr.address_length);
++ tpm->tpm_size = addr.address_length;
++ } else if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
++ struct acpi_resource_fixed_memory32 *fixmem32;
++
++ fixmem32 = &res->data.fixed_memory32;
++ if (!fixmem32)
++ return AE_NO_MEMORY;
++
++ dev_info(dev, "found 0x%llx(0x%llx)\n",
++ (long long)fixmem32->address,
++ (long long)TIS_MEM_LEN);
++ tpm->tpm_phys_address = fixmem32->address;
++ tpm->tpm_address = ioremap(fixmem32->address, TIS_MEM_LEN);
++ tpm->tpm_size = fixmem32->address_length;
++ } else if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
++ struct acpi_resource_extended_irq *irq;
++
++ irq = &res->data.extended_irq;
++ if (irq->interrupt_count > 0 && irq->interrupts[0] > 0) {
++ dev_info(dev, "IRQ %d (%d, %d)\n",
++ irq->interrupts[0],
++ irq->triggering, irq->polarity);
++ tpm->tpm_irq = irq->interrupts[0];
++ }
++ }
++
++ return AE_OK;
++}
++
++static int tpm_tis_acpi_add(struct acpi_device *device)
++{
++ acpi_status result;
++ struct tpm_data *tpm = kzalloc(sizeof(*tpm), GFP_KERNEL);
++ int rc = -ENOMEM;
++
++ if (!tpm)
++ goto out;
+
+- start = pnp_mem_start(pnp_dev, 0);
+- len = pnp_mem_len(pnp_dev, 0);
++ device->driver_data = tpm;
+
+- if (pnp_irq_valid(pnp_dev, 0))
+- irq = pnp_irq(pnp_dev, 0);
+- else
++ result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
++ tpm_resources, device);
++
++ rc = -ENODEV;
++ if (ACPI_FAILURE(result))
++ goto out_free;
++
++ if (!tpm->tpm_address) {
++ dev_err(&device->dev, "no address found in _CRS\n");
++ goto out_free;
++ }
++
++ if (!tpm->tpm_irq) {
++ dev_err(&device->dev, "no IRQ found in _CRS, polling mode\n");
+ interrupts = 0;
++ }
+
+- return tpm_tis_init(&pnp_dev->dev, start, len, irq);
++ return tpm_tis_init(&device->dev, tpm->tpm_phys_address, tpm->tpm_size,
++ tpm->tpm_irq);
++out_free:
++ kfree(tpm);
++out:
++ return rc;
+ }
+
+-static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
++static int tpm_tis_acpi_remove(struct acpi_device *device, int type)
+ {
+- return tpm_pm_suspend(&dev->dev, msg);
++ struct tpm_chip *chip = dev_get_drvdata(&device->dev);
++ tpm_remove_hardware(&device->dev);
++ iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(chip->vendor.iobase +
++ TPM_INT_ENABLE(chip->vendor.locality)),
++ chip->vendor.iobase +
++ TPM_INT_ENABLE(chip->vendor.locality));
++ release_locality(chip, chip->vendor.locality, 1);
++ if (chip->vendor.irq)
++ free_irq(chip->vendor.irq, chip);
++ iounmap(chip->vendor.iobase);
++ kfree(device->driver_data);
++ return 0;
++}
++
++static int tpm_tis_acpi_suspend(struct acpi_device *dev, pm_message_t state)
++{
++ return tpm_pm_suspend(&dev->dev, state);
+ }
+
+-static int tpm_tis_pnp_resume(struct pnp_dev *dev)
++static int tpm_tis_acpi_resume(struct acpi_device *dev)
+ {
+ return tpm_pm_resume(&dev->dev);
+ }
+
+-static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
++static struct acpi_device_id tpm_tis_acpi_tbl[] __devinitdata = {
+ {"PNP0C31", 0}, /* TPM */
+ {"ATM1200", 0}, /* Atmel */
+ {"IFX0102", 0}, /* Infineon */
+@@ -625,30 +727,45 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
+ {"BCM0102", 0}, /* Broadcom */
+ {"NSC1200", 0}, /* National */
+ {"ICO0102", 0}, /* Intel */
++ {"INTC0102", 0}, /* TPM spec says to look for this in _CID */
+ /* Add new here */
+ {"", 0}, /* User Specified */
+ {"", 0} /* Terminator */
+ };
++MODULE_DEVICE_TABLE(acpi, tpm_tis_acpi_tbl);
+
+-static struct pnp_driver tis_pnp_driver = {
++static struct acpi_driver tis_acpi_driver = {
+ .name = "tpm_tis",
+- .id_table = tpm_pnp_tbl,
+- .probe = tpm_tis_pnp_init,
+- .suspend = tpm_tis_pnp_suspend,
+- .resume = tpm_tis_pnp_resume,
++ .ids = tpm_tis_acpi_tbl,
++ .ops = {
++ .add = tpm_tis_acpi_add,
++ .remove = tpm_tis_acpi_remove,
++ .suspend = tpm_tis_acpi_suspend,
++ .resume = tpm_tis_acpi_resume,
++ },
+ };
+
+-#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2
+-module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
+- sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
++#define TIS_HID_USR_IDX (ARRAY_SIZE(tpm_tis_acpi_tbl) - 2)
++module_param_string(hid, tpm_tis_acpi_tbl[TIS_HID_USR_IDX].id,
++ sizeof(tpm_tis_acpi_tbl[TIS_HID_USR_IDX].id), 0444);
+ MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
+
+-static struct device_driver tis_drv = {
+- .name = "tpm_tis",
+- .bus = &platform_bus_type,
+- .owner = THIS_MODULE,
+- .suspend = tpm_pm_suspend,
+- .resume = tpm_pm_resume,
++static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
++{
++ return tpm_pm_suspend(&dev->dev, msg);
++}
++
++static int tpm_tis_resume(struct platform_device *dev)
++{
++ return tpm_pm_resume(&dev->dev);
++}
++static struct platform_driver tis_drv = {
++ .driver = {
++ .name = "tpm_tis",
++ .owner = THIS_MODULE,
++ },
++ .suspend = tpm_tis_suspend,
++ .resume = tpm_tis_resume,
+ };
static struct platform_device *pdev;
+@@ -661,19 +778,19 @@ static int __init init_tis(void)
+ int rc;
+
+ if (force) {
+- rc = driver_register(&tis_drv);
++ rc = platform_driver_register(&tis_drv);
+ if (rc < 0)
+ return rc;
+ if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0)))
+ return PTR_ERR(pdev);
+ if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) {
+ platform_device_unregister(pdev);
+- driver_unregister(&tis_drv);
++ platform_driver_unregister(&tis_drv);
+ }
+ return rc;
+ }
+
+- return pnp_register_driver(&tis_pnp_driver);
++ return acpi_bus_register_driver(&tis_acpi_driver);
+ }
+
+ static void __exit cleanup_tis(void)
+@@ -683,6 +800,7 @@ static void __exit cleanup_tis(void)
+ spin_lock(&tis_lock);
+ list_for_each_entry_safe(i, j, &tis_chips, list) {
+ chip = to_tpm_chip(i);
++ tpm_remove_hardware(chip->dev);
+ iowrite32(~TPM_GLOBAL_INT_ENABLE &
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.
+@@ -694,14 +812,14 @@ static void __exit cleanup_tis(void)
+ free_irq(chip->vendor.irq, chip);
+ iounmap(i->iobase);
+ list_del(&i->list);
+- tpm_remove_hardware(chip->dev);
+ }
+ spin_unlock(&tis_lock);
++
+ if (force) {
+ platform_device_unregister(pdev);
+- driver_unregister(&tis_drv);
++ platform_driver_unregister(&tis_drv);
+ } else
+- pnp_unregister_driver(&tis_pnp_driver);
++ acpi_bus_unregister_driver(&tis_acpi_driver);
+ }
--static int force;
-+static int force = 1;
- module_param(force, bool, 0444);
- MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
- static int __init init_tis(void)
+ module_init(init_tis);