/*
  * Copyright (C) 2005, 2006 IBM Corporation
+ * Copyright (C) 2014 Intel Corporation
  *
  * Authors:
  * Leendert van Doorn <leendert@watson.ibm.com>
        TIS_LONG_TIMEOUT = 2000,        /* 2 sec */
 };
 
+
+/* Some timeout values are needed before it is known whether the chip is
+ * TPM 1.0 or TPM 2.0.
+ */
+#define TIS_TIMEOUT_A_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
+#define TIS_TIMEOUT_B_MAX      max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
+#define TIS_TIMEOUT_C_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
+#define TIS_TIMEOUT_D_MAX      max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
+
 #define        TPM_ACCESS(l)                   (0x0000 | ((l) << 12))
 #define        TPM_INT_ENABLE(l)               (0x0008 | ((l) << 12))
 #define        TPM_INT_VECTOR(l)               (0x000C | ((l) << 12))
 #define        TPM_INT_STATUS(l)               (0x0010 | ((l) << 12))
 #define        TPM_INTF_CAPS(l)                (0x0014 | ((l) << 12))
 #define        TPM_STS(l)                      (0x0018 | ((l) << 12))
+#define        TPM_STS3(l)                     (0x001b | ((l) << 12))
 #define        TPM_DATA_FIFO(l)                (0x0024 | ((l) << 12))
 
 #define        TPM_DID_VID(l)                  (0x0F00 | ((l) << 12))
 {
        int rc;
        u32 ordinal;
+       unsigned long dur;
 
        rc = tpm_tis_send_data(chip, buf, len);
        if (rc < 0)
 
        if (chip->vendor.irq) {
                ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+
+               if (chip->flags & TPM_CHIP_FLAG_TPM2)
+                       dur = tpm2_calc_ordinal_duration(chip, ordinal);
+               else
+                       dur = tpm_calc_ordinal_duration(chip, ordinal);
+
                if (wait_for_tpm_stat
-                   (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
-                    tpm_calc_ordinal_duration(chip, ordinal),
+                   (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
                     &chip->vendor.read_queue, false) < 0) {
                        rc = -ETIME;
                        goto out_err;
                return PTR_ERR(chip);
 
        chip->vendor.priv = priv;
+#ifdef CONFIG_ACPI
        chip->acpi_dev_handle = acpi_dev_handle;
+#endif
 
        chip->vendor.iobase = devm_ioremap(dev, start, len);
        if (!chip->vendor.iobase)
                return -EIO;
 
-       /* 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);
+       /* Maximum timeouts */
+       chip->vendor.timeout_a = TIS_TIMEOUT_A_MAX;
+       chip->vendor.timeout_b = TIS_TIMEOUT_B_MAX;
+       chip->vendor.timeout_c = TIS_TIMEOUT_C_MAX;
+       chip->vendor.timeout_d = TIS_TIMEOUT_D_MAX;
 
        if (wait_startup(chip, 0) != 0) {
                rc = -ENODEV;
                goto out_err;
        }
 
+       /* Every TPM 2.x command has a higher ordinal than TPM 1.x commands.
+        * Therefore, we can use an idempotent TPM 2.x command to probe TPM 2.x.
+        */
+       rc = tpm2_gen_interrupt(chip, true);
+       if (rc == 0 || rc == TPM2_RC_INITIALIZE)
+               chip->flags |= TPM_CHIP_FLAG_TPM2;
+
        vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
        chip->vendor.manufacturer_id = vendor;
 
-       dev_info(dev,
-                "1.2 TPM (device-id 0x%X, rev-id %d)\n",
+       dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+                (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
                 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
        if (!itpm) {
                        chip->vendor.probed_irq = 0;
 
                        /* Generate Interrupts */
-                       tpm_gen_interrupt(chip);
+                       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+                               tpm2_gen_interrupt(chip, false);
+                       else
+                               tpm_gen_interrupt(chip);
 
                        chip->vendor.irq = chip->vendor.probed_irq;
 
                }
        }
 
-       if (tpm_get_timeouts(chip)) {
-               dev_err(dev, "Could not get TPM timeouts and durations\n");
-               rc = -ENODEV;
-               goto out_err;
-       }
+       if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+               chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
+               chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
+               chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
+               chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
+               chip->vendor.duration[TPM_SHORT] =
+                       msecs_to_jiffies(TPM2_DURATION_SHORT);
+               chip->vendor.duration[TPM_MEDIUM] =
+                       msecs_to_jiffies(TPM2_DURATION_MEDIUM);
+               chip->vendor.duration[TPM_LONG] =
+                       msecs_to_jiffies(TPM2_DURATION_LONG);
+
+               rc = tpm2_do_selftest(chip);
+               if (rc == TPM2_RC_INITIALIZE) {
+                       dev_warn(dev, "Firmware has not started TPM\n");
+                       rc  = tpm2_startup(chip, TPM2_SU_CLEAR);
+                       if (!rc)
+                               rc = tpm2_do_selftest(chip);
+               }
 
-       if (tpm_do_selftest(chip)) {
-               dev_err(dev, "TPM self test failed\n");
-               rc = -ENODEV;
-               goto out_err;
+               if (rc) {
+                       dev_err(dev, "TPM self test failed\n");
+                       if (rc > 0)
+                               rc = -ENODEV;
+                       goto out_err;
+               }
+       } else {
+               if (tpm_get_timeouts(chip)) {
+                       dev_err(dev, "Could not get TPM timeouts and durations\n");
+                       rc = -ENODEV;
+                       goto out_err;
+               }
+
+               if (tpm_do_selftest(chip)) {
+                       dev_err(dev, "TPM self test failed\n");
+                       rc = -ENODEV;
+                       goto out_err;
+               }
        }
 
        return tpm_chip_register(chip);
 static int tpm_tis_resume(struct device *dev)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       int ret;
+       int ret = 0;
 
        if (chip->vendor.irq)
                tpm_tis_reenable_interrupts(chip);
 
-       ret = tpm_pm_resume(dev);
-       if (!ret)
-               tpm_do_selftest(chip);
+       if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+               /* NOP if firmware properly does this. */
+               tpm2_startup(chip, TPM2_SU_STATE);
+
+               ret = tpm2_shutdown(chip, TPM2_SU_STATE);
+               if (!ret)
+                       ret = tpm2_do_selftest(chip);
+       } else {
+               ret = tpm_pm_resume(dev);
+               if (!ret)
+                       tpm_do_selftest(chip);
+       }
 
        return ret;
 }