aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/tpm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/tpm')
-rw-r--r--drivers/char/tpm/eventlog/tpm1.c3
-rw-r--r--drivers/char/tpm/tpm-chip.c11
-rw-r--r--drivers/char/tpm/tpm-interface.c21
-rw-r--r--drivers/char/tpm/tpm.h4
-rw-r--r--drivers/char/tpm/tpm2-space.c2
-rw-r--r--drivers/char/tpm/tpm_ftpm_tee.c1
-rw-r--r--drivers/char/tpm/tpm_tis.c94
-rw-r--r--drivers/char/tpm/tpm_tis_core.c60
-rw-r--r--drivers/char/tpm/tpm_tis_core.h1
-rw-r--r--drivers/char/tpm/tpm_tis_spi_main.c93
-rw-r--r--drivers/char/tpm/tpm_tis_synquacer.c19
11 files changed, 159 insertions, 150 deletions
diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
index 8aa9057601d6..12ee42a31c71 100644
--- a/drivers/char/tpm/eventlog/tpm1.c
+++ b/drivers/char/tpm/eventlog/tpm1.c
@@ -251,7 +251,6 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v)
static int tpm1_ascii_bios_measurements_show(struct seq_file *m, void *v)
{
- int len = 0;
char *eventname;
struct tcpa_event *event = v;
unsigned char *event_entry =
@@ -273,7 +272,7 @@ static int tpm1_ascii_bios_measurements_show(struct seq_file *m, void *v)
/* 3rd: event type identifier */
seq_printf(m, " %02x", do_endian_conversion(event->event_type));
- len += get_event_name(eventname, event, event_entry);
+ get_event_name(eventname, event, event_entry);
/* 4th: eventname <= max + \'0' delimiter */
seq_printf(m, " %s\n", eventname);
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index ea6b4013bc38..23f6f2eda84c 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -28,8 +28,13 @@
DEFINE_IDR(dev_nums_idr);
static DEFINE_MUTEX(idr_lock);
-struct class *tpm_class;
-struct class *tpmrm_class;
+const struct class tpm_class = {
+ .name = "tpm",
+ .shutdown_pre = tpm_class_shutdown,
+};
+const struct class tpmrm_class = {
+ .name = "tmprm",
+};
dev_t tpm_devt;
static int tpm_request_locality(struct tpm_chip *chip)
@@ -336,7 +341,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
device_initialize(&chip->dev);
- chip->dev.class = tpm_class;
+ chip->dev.class = &tpm_class;
chip->dev.release = tpm_dev_release;
chip->dev.parent = pdev;
chip->dev.groups = chip->groups;
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 586ca10b0d72..66b16d26eecc 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -476,18 +476,15 @@ static int __init tpm_init(void)
{
int rc;
- tpm_class = class_create("tpm");
- if (IS_ERR(tpm_class)) {
+ rc = class_register(&tpm_class);
+ if (rc) {
pr_err("couldn't create tpm class\n");
- return PTR_ERR(tpm_class);
+ return rc;
}
- tpm_class->shutdown_pre = tpm_class_shutdown;
-
- tpmrm_class = class_create("tpmrm");
- if (IS_ERR(tpmrm_class)) {
+ rc = class_register(&tpmrm_class);
+ if (rc) {
pr_err("couldn't create tpmrm class\n");
- rc = PTR_ERR(tpmrm_class);
goto out_destroy_tpm_class;
}
@@ -508,9 +505,9 @@ static int __init tpm_init(void)
out_unreg_chrdev:
unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES);
out_destroy_tpmrm_class:
- class_destroy(tpmrm_class);
+ class_unregister(&tpmrm_class);
out_destroy_tpm_class:
- class_destroy(tpm_class);
+ class_unregister(&tpm_class);
return rc;
}
@@ -518,8 +515,8 @@ out_destroy_tpm_class:
static void __exit tpm_exit(void)
{
idr_destroy(&dev_nums_idr);
- class_destroy(tpm_class);
- class_destroy(tpmrm_class);
+ class_unregister(&tpm_class);
+ class_unregister(&tpmrm_class);
unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
tpm_dev_common_exit();
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 460bb85dd142..61445f1dc46d 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -230,8 +230,8 @@ enum tpm2_pt_props {
* compiler warnings about stack frame size. */
#define TPM_MAX_RNG_DATA 128
-extern struct class *tpm_class;
-extern struct class *tpmrm_class;
+extern const struct class tpm_class;
+extern const struct class tpmrm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
extern const struct file_operations tpmrm_fops;
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index ffb35f0154c1..363afdd4d1d3 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -606,7 +606,7 @@ int tpm_devs_add(struct tpm_chip *chip)
device_initialize(&chip->devs);
chip->devs.parent = chip->dev.parent;
- chip->devs.class = tpmrm_class;
+ chip->devs.class = &tpmrm_class;
/*
* Get extra reference on main device to hold on behalf of devs.
diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
index 528f35b14fb6..76adb108076c 100644
--- a/drivers/char/tpm/tpm_ftpm_tee.c
+++ b/drivers/char/tpm/tpm_ftpm_tee.c
@@ -11,7 +11,6 @@
#include <linux/acpi.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/tee_drv.h>
#include <linux/tpm.h>
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 7fa3d91042b2..2c52b7905b07 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -25,9 +25,7 @@
#include <linux/acpi.h>
#include <linux/freezer.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/kernel.h>
-#include <linux/dmi.h>
#include "tpm.h"
#include "tpm_tis_core.h"
@@ -89,8 +87,8 @@ static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr)
tpm_tis_flush(iobase);
}
-static int interrupts;
-module_param(interrupts, int, 0444);
+static bool interrupts;
+module_param(interrupts, bool, 0444);
MODULE_PARM_DESC(interrupts, "Enable interrupts");
static bool itpm;
@@ -103,92 +101,6 @@ module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
#endif
-static int tpm_tis_disable_irq(const struct dmi_system_id *d)
-{
- if (interrupts == -1) {
- pr_notice("tpm_tis: %s detected: disabling interrupts.\n", d->ident);
- interrupts = 0;
- }
-
- return 0;
-}
-
-static const struct dmi_system_id tpm_tis_dmi_table[] = {
- {
- .callback = tpm_tis_disable_irq,
- .ident = "Framework Laptop (12th Gen Intel Core)",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Laptop (12th Gen Intel Core)"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "Framework Laptop (13th Gen Intel Core)",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Laptop (13th Gen Intel Core)"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "ThinkPad T490s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T490s"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "ThinkStation P360 Tiny",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkStation P360 Tiny"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "ThinkPad L490",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L490"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "ThinkPad L590",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L590"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "ThinkStation P620",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkStation P620"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "TUXEDO InfinityBook S 15/17 Gen7",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "TUXEDO InfinityBook S 15/17 Gen7"),
- },
- },
- {
- .callback = tpm_tis_disable_irq,
- .ident = "UPX-TGL",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
- DMI_MATCH(DMI_PRODUCT_NAME, "UPX-TGL01"),
- },
- },
- {}
-};
-
#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
static int has_hid(struct acpi_device *dev, const char *hid)
{
@@ -312,8 +224,6 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
int irq = -1;
int rc;
- dmi_check_system(tpm_tis_dmi_table);
-
rc = check_acpi_tpm2(dev);
if (rc)
return rc;
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index b95963095729..1b350412d8a6 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -340,7 +340,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
return size;
}
-static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_tis_try_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int size = 0;
@@ -348,11 +348,6 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
u32 expected;
int rc;
- if (count < TPM_HEADER_SIZE) {
- size = -EIO;
- goto out;
- }
-
size = recv_data(chip, buf, TPM_HEADER_SIZE);
/* read first 10 bytes, including tag, paramsize, and result */
if (size < TPM_HEADER_SIZE) {
@@ -385,7 +380,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
goto out;
}
status = tpm_tis_status(chip);
- if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ if (status & TPM_STS_DATA_AVAIL) {
dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
@@ -399,10 +394,36 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
out:
- tpm_tis_ready(chip);
return size;
}
+static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ unsigned int try;
+ int rc = 0;
+
+ if (count < TPM_HEADER_SIZE)
+ return -EIO;
+
+ for (try = 0; try < TPM_RETRY; try++) {
+ rc = tpm_tis_try_recv(chip, buf, count);
+
+ if (rc == -EIO)
+ /* Data transfer errors, indicated by EIO, can be
+ * recovered by rereading the response.
+ */
+ tpm_tis_write8(priv, TPM_STS(priv->locality),
+ TPM_STS_RESPONSE_RETRY);
+ else
+ break;
+ }
+
+ tpm_tis_ready(chip);
+
+ return rc;
+}
+
/*
* If interrupts are used (signaled by an irq set in the vendor structure)
* tpm.c can skip polling for the data to be available as the interrupt is
@@ -469,6 +490,12 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
goto out_err;
}
+ rc = tpm_tis_verify_crc(priv, len, buf);
+ if (rc < 0) {
+ dev_err(&chip->dev, "CRC mismatch for command.\n");
+ goto out_err;
+ }
+
return 0;
out_err:
@@ -512,15 +539,16 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len)
int rc;
u32 ordinal;
unsigned long dur;
+ unsigned int try;
- rc = tpm_tis_send_data(chip, buf, len);
- if (rc < 0)
- return rc;
-
- rc = tpm_tis_verify_crc(priv, len, buf);
- if (rc < 0) {
- dev_err(&chip->dev, "CRC mismatch for command.\n");
- return rc;
+ for (try = 0; try < TPM_RETRY; try++) {
+ rc = tpm_tis_send_data(chip, buf, len);
+ if (rc >= 0)
+ /* Data transfer done successfully */
+ break;
+ else if (rc != -EIO)
+ /* Data transfer failed, not recoverable */
+ return rc;
}
/* go and do it */
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index b1a169d7d1ca..13e99cf65efe 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -34,6 +34,7 @@ enum tis_status {
TPM_STS_GO = 0x20,
TPM_STS_DATA_AVAIL = 0x10,
TPM_STS_DATA_EXPECT = 0x08,
+ TPM_STS_RESPONSE_RETRY = 0x02,
TPM_STS_READ_ZERO = 0x23, /* bits that must be zero on read */
};
diff --git a/drivers/char/tpm/tpm_tis_spi_main.c b/drivers/char/tpm/tpm_tis_spi_main.c
index 9bfaba092a06..c5c3197ee29f 100644
--- a/drivers/char/tpm/tpm_tis_spi_main.c
+++ b/drivers/char/tpm/tpm_tis_spi_main.c
@@ -28,7 +28,7 @@
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/tpm.h>
@@ -71,8 +71,74 @@ static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy,
return 0;
}
-int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *in, const u8 *out)
+/*
+ * Half duplex controller with support for TPM wait state detection like
+ * Tegra QSPI need CMD, ADDR & DATA sent in single message to manage HW flow
+ * control. Each phase sent in different transfer for controller to idenity
+ * phase.
+ */
+static int tpm_tis_spi_transfer_half(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *in, const u8 *out)
+{
+ struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+ struct spi_transfer spi_xfer[3];
+ struct spi_message m;
+ u8 transfer_len;
+ int ret;
+
+ while (len) {
+ transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
+
+ spi_message_init(&m);
+ phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
+ phy->iobuf[1] = 0xd4;
+ phy->iobuf[2] = addr >> 8;
+ phy->iobuf[3] = addr;
+
+ memset(&spi_xfer, 0, sizeof(spi_xfer));
+
+ spi_xfer[0].tx_buf = phy->iobuf;
+ spi_xfer[0].len = 1;
+ spi_message_add_tail(&spi_xfer[0], &m);
+
+ spi_xfer[1].tx_buf = phy->iobuf + 1;
+ spi_xfer[1].len = 3;
+ spi_message_add_tail(&spi_xfer[1], &m);
+
+ if (out) {
+ spi_xfer[2].tx_buf = &phy->iobuf[4];
+ spi_xfer[2].rx_buf = NULL;
+ memcpy(&phy->iobuf[4], out, transfer_len);
+ out += transfer_len;
+ }
+
+ if (in) {
+ spi_xfer[2].tx_buf = NULL;
+ spi_xfer[2].rx_buf = &phy->iobuf[4];
+ }
+
+ spi_xfer[2].len = transfer_len;
+ spi_message_add_tail(&spi_xfer[2], &m);
+
+ reinit_completion(&phy->ready);
+
+ ret = spi_sync(phy->spi_device, &m);
+ if (ret < 0)
+ return ret;
+
+ if (in) {
+ memcpy(in, &phy->iobuf[4], transfer_len);
+ in += transfer_len;
+ }
+
+ len -= transfer_len;
+ }
+
+ return ret;
+}
+
+static int tpm_tis_spi_transfer_full(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *in, const u8 *out)
{
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
int ret = 0;
@@ -148,6 +214,24 @@ exit:
return ret;
}
+int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *in, const u8 *out)
+{
+ struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+ struct spi_controller *ctlr = phy->spi_device->controller;
+
+ /*
+ * TPM flow control over SPI requires full duplex support.
+ * Send entire message to a half duplex controller to handle
+ * wait polling in controller.
+ * Set TPM HW flow control flag..
+ */
+ if (ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX)
+ return tpm_tis_spi_transfer_half(data, addr, len, in, out);
+ else
+ return tpm_tis_spi_transfer_full(data, addr, len, in, out);
+}
+
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
{
@@ -189,6 +273,9 @@ static int tpm_tis_spi_probe(struct spi_device *dev)
phy->flow_control = tpm_tis_spi_flow_control;
+ if (dev->controller->flags & SPI_CONTROLLER_HALF_DUPLEX)
+ dev->mode |= SPI_TPM_HW_FLOW;
+
/* If the SPI device has an IRQ then use that */
if (dev->irq > 0)
irq = dev->irq;
diff --git a/drivers/char/tpm/tpm_tis_synquacer.c b/drivers/char/tpm/tpm_tis_synquacer.c
index 49278746b0e2..0621ebec530b 100644
--- a/drivers/char/tpm/tpm_tis_synquacer.c
+++ b/drivers/char/tpm/tpm_tis_synquacer.c
@@ -9,7 +9,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/kernel.h>
#include "tpm.h"
#include "tpm_tis_core.h"
@@ -162,23 +161,7 @@ static struct platform_driver tis_synquacer_drv = {
},
};
-static int __init tpm_tis_synquacer_module_init(void)
-{
- int rc;
-
- rc = platform_driver_register(&tis_synquacer_drv);
- if (rc)
- return rc;
-
- return 0;
-}
-
-static void __exit tpm_tis_synquacer_module_exit(void)
-{
- platform_driver_unregister(&tis_synquacer_drv);
-}
+module_platform_driver(tis_synquacer_drv);
-module_init(tpm_tis_synquacer_module_init);
-module_exit(tpm_tis_synquacer_module_exit);
MODULE_DESCRIPTION("TPM MMIO Driver for Socionext SynQuacer platform");
MODULE_LICENSE("GPL");