[PATCH 5/5] tpm: tpm_tis_spi_slb_9670: implement set_reset and unset_reset functions
From: Lino Sanfilippo
Date: Thu Apr 07 2022 - 07:19:55 EST
Provide implementations for the tpm phy operations set_reset and
unset_reset. Taking the chip out of reset requires a certain sequence of
line assertions and deassertions with respective minimum wait intervals:
deassert RST
wait at least 60 ms
assert RST
wait at least 2 usecs
deassert RST
wait at least 60 ms
assert RST
wait at least 2 usecs
deassert RST
wait at least 60 ms before issuing the first TPM command
According to the Infineon SLB 9670VQ2.0 datasheet this sequence is needed
to avoid triggering the chips defense modes in which it protects itself
from dictionary attacks in conjunction with resets.
Since the generic probe function tpm_tis_spi_probe only sets the
non-optional phy ops provide a custom version in which also set_reset and
unset_reset are assigned. Move the implementation of these functions into a
new file to separate the SLB9670 specific code from the generic code.
Signed-off-by: Lino Sanfilippo <LinoSanfilippo@xxxxxx>
---
drivers/char/tpm/Makefile | 1 +
drivers/char/tpm/tpm_tis_spi.h | 2 +
drivers/char/tpm/tpm_tis_spi_main.c | 4 +-
drivers/char/tpm/tpm_tis_spi_slb9670.c | 82 ++++++++++++++++++++++++++
4 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 drivers/char/tpm/tpm_tis_spi_slb9670.c
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 66d39ea6bd10..22c82eb4e382 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_TCG_TIS_SYNQUACER) += tpm_tis_synquacer.o
obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o
tpm_tis_spi-y := tpm_tis_spi_main.o
+tpm_tis_spi-y += tpm_tis_spi_slb9670.o
tpm_tis_spi-$(CONFIG_TCG_TIS_SPI_CR50) += tpm_tis_spi_cr50.o
obj-$(CONFIG_TCG_TIS_I2C_CR50) += tpm_tis_i2c_cr50.o
diff --git a/drivers/char/tpm/tpm_tis_spi.h b/drivers/char/tpm/tpm_tis_spi.h
index 8f4331d8a4dd..b90848832da4 100644
--- a/drivers/char/tpm/tpm_tis_spi.h
+++ b/drivers/char/tpm/tpm_tis_spi.h
@@ -51,6 +51,8 @@ static inline int cr50_spi_probe(struct spi_device *spi)
}
#endif
+extern int slb9670_spi_probe(struct spi_device *spi);
+
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_TCG_TIS_SPI_CR50)
extern int tpm_tis_spi_resume(struct device *dev);
#else
diff --git a/drivers/char/tpm/tpm_tis_spi_main.c b/drivers/char/tpm/tpm_tis_spi_main.c
index b2d13b844659..50da1f6eeaea 100644
--- a/drivers/char/tpm/tpm_tis_spi_main.c
+++ b/drivers/char/tpm/tpm_tis_spi_main.c
@@ -264,7 +264,7 @@ static void tpm_tis_spi_remove(struct spi_device *dev)
static const struct spi_device_id tpm_tis_spi_id[] = {
{ "st33htpm-spi", (unsigned long)tpm_tis_spi_probe },
- { "slb9670", (unsigned long)tpm_tis_spi_probe },
+ { "slb9670", (unsigned long)slb9670_spi_probe },
{ "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe },
{ "tpm_tis-spi", (unsigned long)tpm_tis_spi_probe },
{ "cr50", (unsigned long)cr50_spi_probe },
@@ -274,7 +274,7 @@ MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);
static const struct of_device_id of_tis_spi_match[] = {
{ .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe },
- { .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe },
+ { .compatible = "infineon,slb9670", .data = slb9670_spi_probe },
{ .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe },
{ .compatible = "google,cr50", .data = cr50_spi_probe },
{}
diff --git a/drivers/char/tpm/tpm_tis_spi_slb9670.c b/drivers/char/tpm/tpm_tis_spi_slb9670.c
new file mode 100644
index 000000000000..ba9cd54e8bff
--- /dev/null
+++ b/drivers/char/tpm/tpm_tis_spi_slb9670.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tpm_tis_spi_slb9670.c
+ *
+ * Copyright (C) 2022 KUNBUS GmbH
+ *
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "tpm_tis_core.h"
+#include "tpm_tis_spi.h"
+
+/*
+ * Time intervals used in the reset sequence.
+ * RSTIN: minimum time to hold the reset line deasserted.
+ * WRST: minimum time to hold the reset line asserted.
+ */
+#define SLB9670_TIME_RSTIN 60 /* time in ms */
+#define SLB9670_TIME_WRST 2 /* time in usecs */
+
+int slb9670_spi_unset_reset(struct tpm_tis_data *data)
+{
+ /*
+ * Perform the reset sequence: we have to deassert and assert the reset
+ * line two times and wait the respective time intervals.
+ * After a last wait interval of RSTIN the chip is ready to receive the
+ * first command.
+ */
+ gpiod_set_value(data->reset_gpio, 0);
+ msleep(SLB9670_TIME_RSTIN);
+ gpiod_set_value(data->reset_gpio, 1);
+ udelay(SLB9670_TIME_WRST);
+ gpiod_set_value(data->reset_gpio, 0);
+ msleep(SLB9670_TIME_RSTIN);
+ gpiod_set_value(data->reset_gpio, 1);
+ udelay(SLB9670_TIME_WRST);
+ gpiod_set_value(data->reset_gpio, 0);
+ msleep(SLB9670_TIME_RSTIN);
+
+ return 0;
+}
+
+int slb9670_spi_set_reset(struct tpm_tis_data *data)
+{
+ gpiod_set_value(data->reset_gpio, 1);
+ return 0;
+}
+
+static const struct tpm_tis_phy_ops slb9670_spi_phy_ops = {
+ .read_bytes = tpm_tis_spi_read_bytes,
+ .write_bytes = tpm_tis_spi_write_bytes,
+ .read16 = tpm_tis_spi_read16,
+ .read32 = tpm_tis_spi_read32,
+ .write32 = tpm_tis_spi_write32,
+ .set_reset = slb9670_spi_set_reset,
+ .unset_reset = slb9670_spi_unset_reset,
+};
+
+int slb9670_spi_probe(struct spi_device *spi)
+{
+ struct tpm_tis_spi_phy *phy;
+ int irq;
+
+ phy = devm_kzalloc(&spi->dev, sizeof(struct tpm_tis_spi_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->flow_control = tpm_tis_spi_flow_control;
+
+ /* If the SPI device has an IRQ then use that */
+ if (spi->irq > 0)
+ irq = spi->irq;
+ else
+ irq = -1;
+
+ init_completion(&phy->ready);
+ return tpm_tis_spi_init(spi, phy, irq, &slb9670_spi_phy_ops);
+}
--
2.35.1