tpm: Enable CLKRUN protocol for Braswell systems
authorAzhar Shaikh <azhar.shaikh@intel.com>
Mon, 19 Jun 2017 02:17:59 +0000 (19:17 -0700)
committerJames Morris <james.l.morris@oracle.com>
Thu, 6 Jul 2017 23:49:25 +0000 (09:49 +1000)
To overcome a hardware limitation on Intel Braswell systems,
disable CLKRUN protocol during TPM transactions and re-enable
once the transaction is completed.

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>
Signed-off-by: James Morris <james.l.morris@oracle.com>
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_tis.c

index 1df0521..cdd2613 100644 (file)
 #include <linux/highmem.h>
 #include <crypto/hash_info.h>
 
+#ifdef CONFIG_X86
+#include <asm/intel-family.h>
+#endif
+
 enum tpm_const {
        TPM_MINOR = 224,        /* officially assigned */
        TPM_BUFSIZE = 4096,
index b14d4aa..d97ce22 100644 (file)
@@ -132,13 +132,93 @@ static int check_acpi_tpm2(struct device *dev)
 }
 #endif
 
+#ifdef CONFIG_X86
+#define INTEL_LEGACY_BLK_BASE_ADDR      0xFED08000
+#define ILB_REMAP_SIZE                 0x100
+#define LPC_CNTRL_REG_OFFSET            0x84
+#define LPC_CLKRUN_EN                   (1 << 2)
+
+void __iomem *ilb_base_addr;
+
+static inline bool is_bsw(void)
+{
+       return ((boot_cpu_data.x86_model == INTEL_FAM6_ATOM_AIRMONT) ? 1 : 0);
+}
+
+/**
+ * tpm_platform_begin_xfer() - clear LPC CLKRUN_EN i.e. clocks will be running
+ */
+static void tpm_platform_begin_xfer(void)
+{
+       u32 clkrun_val;
+
+       if (!is_bsw())
+               return;
+
+       clkrun_val = ioread32(ilb_base_addr + LPC_CNTRL_REG_OFFSET);
+
+       /* Disable LPC CLKRUN# */
+       clkrun_val &= ~LPC_CLKRUN_EN;
+       iowrite32(clkrun_val, ilb_base_addr + LPC_CNTRL_REG_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(void)
+{
+       u32 clkrun_val;
+
+       if (!is_bsw())
+               return;
+
+       clkrun_val = ioread32(ilb_base_addr + LPC_CNTRL_REG_OFFSET);
+
+       /* Enable LPC CLKRUN# */
+       clkrun_val |= LPC_CLKRUN_EN;
+       iowrite32(clkrun_val, ilb_base_addr + LPC_CNTRL_REG_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 inline bool is_bsw(void)
+{
+       return false;
+}
+
+static void tpm_platform_begin_xfer(void)
+{
+}
+
+static void tpm_platform_end_xfer(void)
+{
+}
+#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();
+
        while (len--)
                *result++ = ioread8(phy->iobase + addr);
+
+       tpm_platform_end_xfer();
+
        return 0;
 }
 
@@ -147,8 +227,13 @@ 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();
+
        while (len--)
                iowrite8(*value++, phy->iobase + addr);
+
+       tpm_platform_end_xfer();
+
        return 0;
 }
 
@@ -156,7 +241,12 @@ 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();
+
        *result = ioread16(phy->iobase + addr);
+
+       tpm_platform_end_xfer();
+
        return 0;
 }
 
@@ -164,7 +254,12 @@ 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();
+
        *result = ioread32(phy->iobase + addr);
+
+       tpm_platform_end_xfer();
+
        return 0;
 }
 
@@ -172,7 +267,12 @@ 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();
+
        iowrite32(value, phy->iobase + addr);
+
+       tpm_platform_end_xfer();
+
        return 0;
 }
 
@@ -360,6 +460,11 @@ static int __init init_tis(void)
        if (rc)
                goto err_force;
 
+#ifdef CONFIG_X86
+       if (is_bsw())
+               ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR,
+                                       ILB_REMAP_SIZE);
+#endif
        rc = platform_driver_register(&tis_drv);
        if (rc)
                goto err_platform;
@@ -378,6 +483,10 @@ err_pnp:
 err_platform:
        if (force_pdev)
                platform_device_unregister(force_pdev);
+#ifdef CONFIG_X86
+       if (is_bsw())
+               iounmap(ilb_base_addr);
+#endif
 err_force:
        return rc;
 }
@@ -387,6 +496,10 @@ static void __exit cleanup_tis(void)
        pnp_unregister_driver(&tis_pnp_driver);
        platform_driver_unregister(&tis_drv);
 
+#ifdef CONFIG_X86
+       if (is_bsw())
+               iounmap(ilb_base_addr);
+#endif
        if (force_pdev)
                platform_device_unregister(force_pdev);
 }