tpm_tis: Do not fall back to a hardcoded address for TPM2
[cascardo/linux.git] / drivers / char / tpm / tpm_tis.c
index 65f7eec..2ccad8a 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/wait.h>
 #include <linux/acpi.h>
 #include <linux/freezer.h>
-#include <acpi/actbl2.h>
 #include "tpm.h"
 
 enum tis_access {
@@ -69,7 +68,11 @@ enum tis_defaults {
 struct tpm_info {
        unsigned long start;
        unsigned long len;
-       unsigned int irq;
+       /* irq > 0 means: use irq $irq;
+        * irq = 0 means: autoprobe for an irq;
+        * irq = -1 means: no irq support
+        */
+       int irq;
 };
 
 static struct tpm_info tis_default_info = {
@@ -118,39 +121,11 @@ static inline int is_itpm(struct acpi_device *dev)
 {
        return has_hid(dev, "INTC0102");
 }
-
-static inline int is_fifo(struct acpi_device *dev)
-{
-       struct acpi_table_tpm2 *tbl;
-       acpi_status st;
-
-       /* TPM 1.2 FIFO */
-       if (!has_hid(dev, "MSFT0101"))
-               return 1;
-
-       st = acpi_get_table(ACPI_SIG_TPM2, 1,
-                           (struct acpi_table_header **) &tbl);
-       if (ACPI_FAILURE(st)) {
-               dev_err(&dev->dev, "failed to get TPM2 ACPI table\n");
-               return 0;
-       }
-
-       if (le32_to_cpu(tbl->start_method) != TPM2_START_FIFO)
-               return 0;
-
-       /* TPM 2.0 FIFO */
-       return 1;
-}
 #else
 static inline int is_itpm(struct acpi_device *dev)
 {
        return 0;
 }
-
-static inline int is_fifo(struct acpi_device *dev)
-{
-       return 1;
-}
 #endif
 
 /* Before we attempt to access the TPM we must see that the valid bit is set.
@@ -401,7 +376,7 @@ static void disable_interrupts(struct tpm_chip *chip)
        iowrite32(intmask,
                  chip->vendor.iobase +
                  TPM_INT_ENABLE(chip->vendor.locality));
-       free_irq(chip->vendor.irq, chip);
+       devm_free_irq(chip->pdev, chip->vendor.irq, chip);
        chip->vendor.irq = 0;
 }
 
@@ -461,11 +436,8 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
        chip->vendor.irq = irq;
        if (!priv->irq_tested)
                msleep(1);
-       if (!priv->irq_tested) {
+       if (!priv->irq_tested)
                disable_interrupts(chip);
-               dev_err(chip->pdev,
-                       FW_BUG "TPM interrupt not working, polling instead\n");
-       }
        priv->irq_tested = true;
        return rc;
 }
@@ -570,26 +542,6 @@ static const struct tpm_class_ops tpm_tis = {
        .req_canceled = tpm_tis_req_canceled,
 };
 
-static irqreturn_t tis_int_probe(int irq, void *dev_id)
-{
-       struct tpm_chip *chip = dev_id;
-       u32 interrupt;
-
-       interrupt = ioread32(chip->vendor.iobase +
-                            TPM_INT_STATUS(chip->vendor.locality));
-
-       if (interrupt == 0)
-               return IRQ_NONE;
-
-       chip->vendor.probed_irq = irq;
-
-       /* Clear interrupts handled with TPM_EOI */
-       iowrite32(interrupt,
-                 chip->vendor.iobase +
-                 TPM_INT_STATUS(chip->vendor.locality));
-       return IRQ_HANDLED;
-}
-
 static irqreturn_t tis_int_handler(int dummy, void *dev_id)
 {
        struct tpm_chip *chip = dev_id;
@@ -622,6 +574,84 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
        return IRQ_HANDLED;
 }
 
+/* Register the IRQ and issue a command that will cause an interrupt. If an
+ * irq is seen then leave the chip setup for IRQ operation, otherwise reverse
+ * everything and leave in polling mode. Returns 0 on success.
+ */
+static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
+                                   int flags, int irq)
+{
+       struct priv_data *priv = chip->vendor.priv;
+       u8 original_int_vec;
+
+       if (devm_request_irq(chip->pdev, irq, tis_int_handler, flags,
+                            chip->devname, chip) != 0) {
+               dev_info(chip->pdev, "Unable to request irq: %d for probe\n",
+                        irq);
+               return -1;
+       }
+       chip->vendor.irq = irq;
+
+       original_int_vec = ioread8(chip->vendor.iobase +
+                                  TPM_INT_VECTOR(chip->vendor.locality));
+       iowrite8(irq,
+                chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality));
+
+       /* Clear all existing */
+       iowrite32(ioread32(chip->vendor.iobase +
+                          TPM_INT_STATUS(chip->vendor.locality)),
+                 chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality));
+
+       /* Turn on */
+       iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+                 chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
+
+       priv->irq_tested = false;
+
+       /* Generate an interrupt by having the core call through to
+        * tpm_tis_send
+        */
+       if (chip->flags & TPM_CHIP_FLAG_TPM2)
+               tpm2_gen_interrupt(chip);
+       else
+               tpm_gen_interrupt(chip);
+
+       /* tpm_tis_send will either confirm the interrupt is working or it
+        * will call disable_irq which undoes all of the above.
+        */
+       if (!chip->vendor.irq) {
+               iowrite8(original_int_vec,
+                        chip->vendor.iobase +
+                            TPM_INT_VECTOR(chip->vendor.locality));
+               return 1;
+       }
+
+       return 0;
+}
+
+/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that
+ * do not have ACPI/etc. We typically expect the interrupt to be declared if
+ * present.
+ */
+static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask)
+{
+       u8 original_int_vec;
+       int i;
+
+       original_int_vec = ioread8(chip->vendor.iobase +
+                                  TPM_INT_VECTOR(chip->vendor.locality));
+
+       if (!original_int_vec) {
+               if (IS_ENABLED(CONFIG_X86))
+                       for (i = 3; i <= 15; i++)
+                               if (!tpm_tis_probe_irq_single(chip, intmask, 0,
+                                                             i))
+                                       return;
+       } else if (!tpm_tis_probe_irq_single(chip, intmask, 0,
+                                            original_int_vec))
+               return;
+}
+
 static bool interrupts = true;
 module_param(interrupts, bool, 0444);
 MODULE_PARM_DESC(interrupts, "Enable interrupts");
@@ -644,8 +674,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
                        acpi_handle acpi_dev_handle)
 {
        u32 vendor, intfcaps, intmask;
-       int rc, i, irq_s, irq_e, probe;
-       int irq_r = -1;
+       int rc, probe;
        struct tpm_chip *chip;
        struct priv_data *priv;
 
@@ -677,6 +706,15 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
                goto out_err;
        }
 
+       /* Take control of the TPM's interrupt hardware and shut it off */
+       intmask = ioread32(chip->vendor.iobase +
+                          TPM_INT_ENABLE(chip->vendor.locality));
+       intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
+                  TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
+       intmask &= ~TPM_GLOBAL_INT_ENABLE;
+       iowrite32(intmask,
+                 chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
+
        if (request_locality(chip, 0) != 0) {
                rc = -ENODEV;
                goto out_err;
@@ -731,126 +769,31 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
        if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
                dev_dbg(dev, "\tData Avail Int Support\n");
 
+       /* Very early on issue a command to the TPM in polling mode to make
+        * sure it works. May as well use that command to set the proper
+        *  timeouts for the driver.
+        */
+       if (tpm_get_timeouts(chip)) {
+               dev_err(dev, "Could not get TPM timeouts and durations\n");
+               rc = -ENODEV;
+               goto out_err;
+       }
+
        /* INTERRUPT Setup */
        init_waitqueue_head(&chip->vendor.read_queue);
        init_waitqueue_head(&chip->vendor.int_queue);
-
-       intmask =
-           ioread32(chip->vendor.iobase +
-                    TPM_INT_ENABLE(chip->vendor.locality));
-
-       intmask |= TPM_INTF_CMD_READY_INT
-           | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
-           | TPM_INTF_STS_VALID_INT;
-
-       iowrite32(intmask,
-                 chip->vendor.iobase +
-                 TPM_INT_ENABLE(chip->vendor.locality));
-       if (interrupts)
-               chip->vendor.irq = tpm_info->irq;
-       if (interrupts && !chip->vendor.irq) {
-               irq_s =
-                   ioread8(chip->vendor.iobase +
-                           TPM_INT_VECTOR(chip->vendor.locality));
-               irq_r = irq_s;
-               if (irq_s) {
-                       irq_e = irq_s;
-               } else {
-                       irq_s = 3;
-                       irq_e = 15;
-               }
-
-               for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
-                       iowrite8(i, chip->vendor.iobase +
-                                TPM_INT_VECTOR(chip->vendor.locality));
-                       if (devm_request_irq
-                           (dev, i, tis_int_probe, IRQF_SHARED,
-                            chip->devname, chip) != 0) {
-                               dev_info(chip->pdev,
-                                        "Unable to request irq: %d for probe\n",
-                                        i);
-                               continue;
-                       }
-
-                       /* Clear all existing */
-                       iowrite32(ioread32
-                                 (chip->vendor.iobase +
-                                  TPM_INT_STATUS(chip->vendor.locality)),
-                                 chip->vendor.iobase +
-                                 TPM_INT_STATUS(chip->vendor.locality));
-
-                       /* Turn on */
-                       iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
-                                 chip->vendor.iobase +
-                                 TPM_INT_ENABLE(chip->vendor.locality));
-
-                       chip->vendor.probed_irq = 0;
-
-                       /* Generate Interrupts */
-                       if (chip->flags & TPM_CHIP_FLAG_TPM2)
-                               tpm2_gen_interrupt(chip);
-                       else
-                               tpm_gen_interrupt(chip);
-
-                       chip->vendor.irq = chip->vendor.probed_irq;
-
-                       /* free_irq will call into tis_int_probe;
-                          clear all irqs we haven't seen while doing
-                          tpm_gen_interrupt */
-                       iowrite32(ioread32
-                                 (chip->vendor.iobase +
-                                  TPM_INT_STATUS(chip->vendor.locality)),
-                                 chip->vendor.iobase +
-                                 TPM_INT_STATUS(chip->vendor.locality));
-
-                       /* Turn off */
-                       iowrite32(intmask,
-                                 chip->vendor.iobase +
-                                 TPM_INT_ENABLE(chip->vendor.locality));
-
-                       devm_free_irq(dev, i, chip);
-               }
+       if (interrupts && tpm_info->irq != -1) {
+               if (tpm_info->irq) {
+                       tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
+                                                tpm_info->irq);
+                       if (!chip->vendor.irq)
+                               dev_err(chip->pdev, FW_BUG
+                                       "TPM interrupt not working, polling instead\n");
+               } else
+                       tpm_tis_probe_irq(chip, intmask);
        }
-       if (chip->vendor.irq) {
-               iowrite8(chip->vendor.irq,
-                        chip->vendor.iobase +
-                        TPM_INT_VECTOR(chip->vendor.locality));
-               if (devm_request_irq
-                   (dev, chip->vendor.irq, tis_int_handler, IRQF_SHARED,
-                    chip->devname, chip) != 0) {
-                       dev_info(chip->pdev,
-                                "Unable to request irq: %d for use\n",
-                                chip->vendor.irq);
-                       chip->vendor.irq = 0;
-               } else {
-                       /* Clear all existing */
-                       iowrite32(ioread32
-                                 (chip->vendor.iobase +
-                                  TPM_INT_STATUS(chip->vendor.locality)),
-                                 chip->vendor.iobase +
-                                 TPM_INT_STATUS(chip->vendor.locality));
-
-                       /* Turn on */
-                       iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
-                                 chip->vendor.iobase +
-                                 TPM_INT_ENABLE(chip->vendor.locality));
-               }
-       } else if (irq_r != -1)
-               iowrite8(irq_r, chip->vendor.iobase +
-                        TPM_INT_VECTOR(chip->vendor.locality));
 
        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");
@@ -866,12 +809,6 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
                        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;
@@ -933,9 +870,9 @@ static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
 
 #ifdef CONFIG_PNP
 static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
-                                     const struct pnp_device_id *pnp_id)
+                           const struct pnp_device_id *pnp_id)
 {
-       struct tpm_info tpm_info = tis_default_info;
+       struct tpm_info tpm_info = {};
        acpi_handle acpi_dev_handle = NULL;
 
        tpm_info.start = pnp_mem_start(pnp_dev, 0);
@@ -944,7 +881,7 @@ static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
        if (pnp_irq_valid(pnp_dev, 0))
                tpm_info.irq = pnp_irq(pnp_dev, 0);
        else
-               interrupts = false;
+               tpm_info.irq = -1;
 
 #ifdef CONFIG_ACPI
        if (pnp_acpi_device(pnp_dev)) {
@@ -1014,14 +951,25 @@ static int tpm_check_resource(struct acpi_resource *ares, void *data)
 
 static int tpm_tis_acpi_init(struct acpi_device *acpi_dev)
 {
+       struct acpi_table_tpm2 *tbl;
+       acpi_status st;
        struct list_head resources;
-       struct tpm_info tpm_info = tis_default_info;
+       struct tpm_info tpm_info = {};
        int ret;
 
-       if (!is_fifo(acpi_dev))
+       st = acpi_get_table(ACPI_SIG_TPM2, 1,
+                           (struct acpi_table_header **) &tbl);
+       if (ACPI_FAILURE(st) || tbl->header.length < sizeof(*tbl)) {
+               dev_err(&acpi_dev->dev,
+                       FW_BUG "failed to get TPM2 ACPI table\n");
+               return -EINVAL;
+       }
+
+       if (tbl->start_method != ACPI_TPM2_MEMORY_MAPPED)
                return -ENODEV;
 
        INIT_LIST_HEAD(&resources);
+       tpm_info.irq = -1;
        ret = acpi_dev_get_resources(acpi_dev, &resources, tpm_check_resource,
                                     &tpm_info);
        if (ret < 0)
@@ -1029,8 +977,11 @@ static int tpm_tis_acpi_init(struct acpi_device *acpi_dev)
 
        acpi_dev_free_resource_list(&resources);
 
-       if (!tpm_info.irq)
-               interrupts = false;
+       if (tpm_info.start == 0 && tpm_info.len == 0) {
+               dev_err(&acpi_dev->dev,
+                       FW_BUG "TPM2 ACPI table does not define a memory resource\n");
+               return -EINVAL;
+       }
 
        if (is_itpm(acpi_dev))
                itpm = true;