diff options
Diffstat (limited to 'drivers/char/tpm')
-rw-r--r-- | drivers/char/tpm/eventlog/tpm1.c | 3 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-chip.c | 11 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-interface.c | 21 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 4 | ||||
-rw-r--r-- | drivers/char/tpm/tpm2-space.c | 2 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_ftpm_tee.c | 1 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 94 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_core.c | 60 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_core.h | 1 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_spi_main.c | 93 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_synquacer.c | 19 |
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"); |