]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
tpm: Keep CLKRUN enabled throughout the duration of transmit_cmd()
authorAzhar Shaikh <azhar.shaikh@intel.com>
Fri, 22 Dec 2017 20:13:44 +0000 (12:13 -0800)
committerJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Mon, 8 Jan 2018 10:58:38 +0000 (12:58 +0200)
Commit 5e572cab92f0bb5 ("tpm: Enable CLKRUN protocol for Braswell
systems") disabled CLKRUN protocol during TPM transactions and re-enabled
once the transaction is completed. But there were still some corner cases
observed where, reading of TPM header failed for savestate command
while going to suspend, which resulted in suspend failure.
To fix this issue keep the CLKRUN protocol disabled for the entire
duration of a single TPM command and not disabling and re-enabling
again for every TPM transaction. For the other TPM accesses outside
TPM command flow, add a higher level of disabling and re-enabling
the CLKRUN protocol, instead of doing for every TPM transaction.

Fixes: 5e572cab92f0bb5 ("tpm: Enable CLKRUN protocol for Braswell systems")
Signed-off-by: Azhar Shaikh <azhar.shaikh@intel.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm_tis.c
drivers/char/tpm/tpm_tis_core.c
drivers/char/tpm/tpm_tis_core.h
include/linux/tpm.h

index af355bd97bea0fa8a1884586ab787e0610e0d3ba..76df4fbcf089c2371b2ad8b08f31bf446b5c488b 100644 (file)
@@ -425,6 +425,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
        if (chip->dev.parent)
                pm_runtime_get_sync(chip->dev.parent);
 
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, true);
+
        /* Store the decision as chip->locality will be changed. */
        need_locality = chip->locality == -1;
 
@@ -501,6 +504,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
                chip->locality = -1;
        }
 out_no_locality:
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, false);
+
        if (chip->dev.parent)
                pm_runtime_put_sync(chip->dev.parent);
 
index 923f8f2cbaca29d27c2a8c1424077c76885f2fe7..c847fc69a2fc27e9c6f415aafdf8a1e3ccd57c2f 100644 (file)
@@ -133,79 +133,17 @@ static int check_acpi_tpm2(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_X86
-#define LPC_CNTRL_OFFSET               0x84
-#define LPC_CLKRUN_EN                  (1 << 2)
-
-/**
- * tpm_platform_begin_xfer() - clear LPC CLKRUN_EN i.e. clocks will be running
- */
-static void tpm_platform_begin_xfer(struct tpm_tis_data *data)
-{
-       u32 clkrun_val;
-
-       if (!is_bsw())
-               return;
-
-       clkrun_val = ioread32(data->ilb_base_addr + LPC_CNTRL_OFFSET);
-
-       /* Disable LPC CLKRUN# */
-       clkrun_val &= ~LPC_CLKRUN_EN;
-       iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
-
-       /*
-        * Write any random value on port 0x80 which is on LPC, to make
-        * sure LPC clock is running before sending any TPM command.
-        */
-       outb(0xCC, 0x80);
-
-}
-
-/**
- * tpm_platform_end_xfer() - set LPC CLKRUN_EN i.e. clocks can be turned off
- */
-static void tpm_platform_end_xfer(struct tpm_tis_data *data)
-{
-       u32 clkrun_val;
-
-       if (!is_bsw())
-               return;
-
-       clkrun_val = ioread32(data->ilb_base_addr + LPC_CNTRL_OFFSET);
-
-       /* Enable LPC CLKRUN# */
-       clkrun_val |= LPC_CLKRUN_EN;
-       iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
-
-       /*
-        * Write any random value on port 0x80 which is on LPC, to make
-        * sure LPC clock is running before sending any TPM command.
-        */
-       outb(0xCC, 0x80);
-
-}
-#else
-static void tpm_platform_begin_xfer(struct tpm_tis_data *data)
-{
-}
-
-static void tpm_platform_end_xfer(struct tpm_tis_data *data)
-{
-}
-#endif
-
 static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
                              u8 *result)
 {
        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       tpm_platform_begin_xfer(data);
+       if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
+               WARN(1, "CLKRUN not enabled!\n");
 
        while (len--)
                *result++ = ioread8(phy->iobase + addr);
 
-       tpm_platform_end_xfer(data);
-
        return 0;
 }
 
@@ -214,13 +152,12 @@ static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
 {
        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       tpm_platform_begin_xfer(data);
+       if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
+               WARN(1, "CLKRUN not enabled!\n");
 
        while (len--)
                iowrite8(*value++, phy->iobase + addr);
 
-       tpm_platform_end_xfer(data);
-
        return 0;
 }
 
@@ -228,12 +165,11 @@ static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
 {
        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       tpm_platform_begin_xfer(data);
+       if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
+               WARN(1, "CLKRUN not enabled!\n");
 
        *result = ioread16(phy->iobase + addr);
 
-       tpm_platform_end_xfer(data);
-
        return 0;
 }
 
@@ -241,12 +177,11 @@ static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
 {
        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       tpm_platform_begin_xfer(data);
+       if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
+               WARN(1, "CLKRUN not enabled!\n");
 
        *result = ioread32(phy->iobase + addr);
 
-       tpm_platform_end_xfer(data);
-
        return 0;
 }
 
@@ -254,12 +189,11 @@ static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
 {
        struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-       tpm_platform_begin_xfer(data);
+       if (is_bsw() && !(data->flags & TPM_TIS_CLK_ENABLE))
+               WARN(1, "CLKRUN not enabled!\n");
 
        iowrite32(value, phy->iobase + addr);
 
-       tpm_platform_end_xfer(data);
-
        return 0;
 }
 
@@ -341,9 +275,6 @@ static void tpm_tis_pnp_remove(struct pnp_dev *dev)
 
        tpm_chip_unregister(chip);
        tpm_tis_remove(chip);
-       if (is_bsw())
-               iounmap(priv->ilb_base_addr);
-
 }
 
 static struct pnp_driver tis_pnp_driver = {
@@ -395,9 +326,6 @@ static int tpm_tis_plat_remove(struct platform_device *pdev)
        tpm_chip_unregister(chip);
        tpm_tis_remove(chip);
 
-       if (is_bsw())
-               iounmap(priv->ilb_base_addr);
-
        return 0;
 }
 
index aff567840e50247435659022b2558056040c81be..3238731fdbfdd42f164bd290f64c0682c527c327 100644 (file)
@@ -37,6 +37,8 @@
  */
 #define TPM_POLL_SLEEP 1  /* msec */
 
+static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value);
+
 static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
                                        bool check_cancel, bool *canceled)
 {
@@ -487,19 +489,28 @@ static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
        int i, rc;
        u32 did_vid;
 
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, true);
+
        rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
        if (rc < 0)
-               return rc;
+               goto out;
 
        for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
                if (vendor_timeout_overrides[i].did_vid != did_vid)
                        continue;
                memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
                       sizeof(vendor_timeout_overrides[i].timeout_us));
-               return true;
+               rc = true;
        }
 
-       return false;
+       rc = false;
+
+out:
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, false);
+
+       return rc;
 }
 
 /*
@@ -719,14 +730,74 @@ void tpm_tis_remove(struct tpm_chip *chip)
        u32 interrupt;
        int rc;
 
+       tpm_tis_clkrun_enable(chip, true);
+
        rc = tpm_tis_read32(priv, reg, &interrupt);
        if (rc < 0)
                interrupt = 0;
 
        tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
+
+       tpm_tis_clkrun_enable(chip, false);
+
+       if (priv->ilb_base_addr)
+               iounmap(priv->ilb_base_addr);
 }
 EXPORT_SYMBOL_GPL(tpm_tis_remove);
 
+/**
+ * tpm_tis_clkrun_enable() - Keep clkrun protocol disabled for entire duration
+ *                           of a single TPM command
+ * @chip:      TPM chip to use
+ * @value:     1 - Disable CLKRUN protocol, so that clocks are free running
+ *             0 - Enable CLKRUN protocol
+ * Call this function directly in tpm_tis_remove() in error or driver removal
+ * path, since the chip->ops is set to NULL in tpm_chip_unregister().
+ */
+static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value)
+{
+       struct tpm_tis_data *data = dev_get_drvdata(&chip->dev);
+       u32 clkrun_val;
+
+       if (!IS_ENABLED(CONFIG_X86) || !is_bsw())
+               return;
+
+       if (value) {
+               data->flags |= TPM_TIS_CLK_ENABLE;
+               data->clkrun_enabled++;
+               if (data->clkrun_enabled > 1)
+                       return;
+               clkrun_val = ioread32(data->ilb_base_addr + LPC_CNTRL_OFFSET);
+
+               /* Disable LPC CLKRUN# */
+               clkrun_val &= ~LPC_CLKRUN_EN;
+               iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
+
+               /*
+                * Write any random value on port 0x80 which is on LPC, to make
+                * sure LPC clock is running before sending any TPM command.
+                */
+               outb(0xCC, 0x80);
+       } else {
+               data->clkrun_enabled--;
+               if (data->clkrun_enabled)
+                       return;
+
+               clkrun_val = ioread32(data->ilb_base_addr + LPC_CNTRL_OFFSET);
+
+               /* Enable LPC CLKRUN# */
+               clkrun_val |= LPC_CLKRUN_EN;
+               iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
+
+               /*
+                * Write any random value on port 0x80 which is on LPC, to make
+                * sure LPC clock is running before sending any TPM command.
+                */
+               outb(0xCC, 0x80);
+               data->flags &= ~TPM_TIS_CLK_ENABLE;
+       }
+}
+
 static const struct tpm_class_ops tpm_tis = {
        .flags = TPM_OPS_AUTO_STARTUP,
        .status = tpm_tis_status,
@@ -739,6 +810,7 @@ static const struct tpm_class_ops tpm_tis = {
        .req_canceled = tpm_tis_req_canceled,
        .request_locality = request_locality,
        .relinquish_locality = release_locality,
+       .clk_enable = tpm_tis_clkrun_enable,
 };
 
 int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
@@ -773,6 +845,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
                        return -ENOMEM;
        }
 
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, true);
+
        if (wait_startup(chip, 0) != 0) {
                rc = -ENODEV;
                goto out_err;
@@ -864,14 +939,18 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
        }
 
        rc = tpm_chip_register(chip);
-       if (rc && is_bsw())
-               iounmap(priv->ilb_base_addr);
+       if (rc)
+               goto out_err;
 
-       return rc;
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, false);
+
+       return 0;
 out_err:
+       if ((chip->ops != NULL) && (chip->ops->clk_enable != NULL))
+               chip->ops->clk_enable(chip, false);
+
        tpm_tis_remove(chip);
-       if (is_bsw())
-               iounmap(priv->ilb_base_addr);
 
        return rc;
 }
@@ -884,22 +963,31 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
        u32 intmask;
        int rc;
 
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, true);
+
        /* reenable interrupts that device may have lost or
         * BIOS/firmware may have disabled
         */
        rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq);
        if (rc < 0)
-               return;
+               goto out;
 
        rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
        if (rc < 0)
-               return;
+               goto out;
 
        intmask |= TPM_INTF_CMD_READY_INT
            | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
            | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
 
        tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+
+out:
+       if (chip->ops->clk_enable != NULL)
+               chip->ops->clk_enable(chip, false);
+
+       return;
 }
 
 int tpm_tis_resume(struct device *dev)
index 458847f72758ad79ea745e5747bcfb1da1c43663..afc50cde1ba64d4f41315d54b9d2fae90e0bda00 100644 (file)
@@ -79,11 +79,14 @@ enum tis_defaults {
 #define        TPM_DID_VID(l)                  (0x0F00 | ((l) << 12))
 #define        TPM_RID(l)                      (0x0F04 | ((l) << 12))
 
+#define LPC_CNTRL_OFFSET               0x84
+#define LPC_CLKRUN_EN                  (1 << 2)
 #define INTEL_LEGACY_BLK_BASE_ADDR     0xFED08000
 #define ILB_REMAP_SIZE                 0x100
 
 enum tpm_tis_flags {
        TPM_TIS_ITPM_WORKAROUND         = BIT(0),
+       TPM_TIS_CLK_ENABLE              = BIT(1),
 };
 
 struct tpm_tis_data {
@@ -93,6 +96,7 @@ struct tpm_tis_data {
        bool irq_tested;
        unsigned int flags;
        void __iomem *ilb_base_addr;
+       u16 clkrun_enabled;
        wait_queue_head_t int_queue;
        wait_queue_head_t read_queue;
        const struct tpm_tis_phy_ops *phy_ops;
index ddc9b88ff6d31bf64a092ee994a51e1bf9de4e1d..bcdd3790e94ddeac42161e55f98dcb284949a2d7 100644 (file)
@@ -45,6 +45,7 @@ struct tpm_class_ops {
                                unsigned long *timeout_cap);
        int (*request_locality)(struct tpm_chip *chip, int loc);
        void (*relinquish_locality)(struct tpm_chip *chip, int loc);
+       void (*clk_enable)(struct tpm_chip *chip, bool value);
 };
 
 #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)