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