[PATCH 2/2] reset: Add Infineon SLB9670 TPM reset driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Lino Sanfilippo <l.sanfilippo@xxxxxxxxxx>

Normally the platform firmware is responsible for taking a Trusted
Platform Module out of reset on boot and storing measurements into it.

However if the platform firmware is incapable of doing that -- as is the
case on the Raspberry Pi -- then the onus is on the kernel to take the
TPM out of reset before trying to attach a driver to it.

Provide a reset driver for such platforms.

The Infineon SLB9670 TPM requires a specific reset sequence on its RST#
pin which is documented in sections 5.4 and 5.5 of the datasheet:

https://www.infineon.com/dgdl/Infineon-SLB%209670VQ2.0-DataSheet-v01_04-EN.pdf?fileId=5546d4626fc1ce0b016fc78270350cd6

The sequence with minimum wait intervals is as follows:

  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

Signed-off-by: Lino Sanfilippo <l.sanfilippo@xxxxxxxxxx>
Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx>
---
 drivers/reset/Kconfig         |   9 +++
 drivers/reset/Makefile        |   1 +
 drivers/reset/reset-slb9670.c | 141 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 drivers/reset/reset-slb9670.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index ccd59dd..3296e33 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -229,6 +229,15 @@ config RESET_SIMPLE
 	   - Allwinner SoCs
 	   - SiFive FU740 SoCs
 
+config RESET_SLB9670
+	tristate "Infineon SLB9670 TPM Reset Driver"
+	depends on TCG_TIS_SPI
+	help
+	  This enables the reset driver for the Infineon SLB9670 Trusted
+	  Platform Module. Only say Y here if your platform firmware is
+	  incapable of taking the TPM out of reset on boot, requiring the
+	  kernel to do so.
+
 config RESET_SOCFPGA
 	bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA)
 	default ARM && ARCH_INTEL_SOCFPGA
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 8270da8..d9c182e 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
 obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
+obj-$(CONFIG_RESET_SLB9670) += reset-slb9670.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
 obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-slb9670.c b/drivers/reset/reset-slb9670.c
new file mode 100644
index 00000000..cc09ab5
--- /dev/null
+++ b/drivers/reset/reset-slb9670.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Reset driver for Infineon SLB9670 Trusted Platform Module
+ *
+ * Copyright (C) 2022 KUNBUS GmbH
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.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 /* msecs */
+#define SLB9670_TIME_WRST	2  /* usecs */
+
+struct reset_slb9670 {
+	struct reset_controller_dev rcdev;
+	struct gpio_desc *gpio;
+};
+
+static inline struct reset_slb9670 *
+to_reset_slb9670(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct reset_slb9670, rcdev);
+}
+
+static int reset_slb9670_assert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	struct reset_slb9670 *reset_slb9670 = to_reset_slb9670(rcdev);
+
+	gpiod_set_value(reset_slb9670->gpio, 1);
+	return 0;
+}
+
+static int reset_slb9670_deassert(struct reset_controller_dev *rcdev,
+				  unsigned long id)
+{
+	struct reset_slb9670 *reset_slb9670 = to_reset_slb9670(rcdev);
+
+	/*
+	 * Perform the reset sequence: Deassert and assert the reset line twice
+	 * 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(reset_slb9670->gpio, 0);
+	msleep(SLB9670_TIME_RSTIN);
+	gpiod_set_value(reset_slb9670->gpio, 1);
+	udelay(SLB9670_TIME_WRST);
+	gpiod_set_value(reset_slb9670->gpio, 0);
+	msleep(SLB9670_TIME_RSTIN);
+	gpiod_set_value(reset_slb9670->gpio, 1);
+	udelay(SLB9670_TIME_WRST);
+	gpiod_set_value(reset_slb9670->gpio, 0);
+	msleep(SLB9670_TIME_RSTIN);
+
+	return 0;
+}
+
+static int reset_slb9670_reset(struct reset_controller_dev *rcdev,
+			       unsigned long id)
+{
+	int ret;
+
+	ret = reset_slb9670_assert(rcdev, id);
+	if (ret)
+		return ret;
+
+	return reset_slb9670_deassert(rcdev, id);
+}
+
+static int reset_slb9670_status(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	struct reset_slb9670 *reset_slb9670 = to_reset_slb9670(rcdev);
+
+	return gpiod_get_value(reset_slb9670->gpio);
+}
+
+static const struct reset_control_ops reset_slb9670_ops = {
+	.assert		= reset_slb9670_assert,
+	.deassert	= reset_slb9670_deassert,
+	.reset		= reset_slb9670_reset,
+	.status		= reset_slb9670_status,
+};
+
+static int reset_slb9670_of_xlate(struct reset_controller_dev *rcdev,
+				  const struct of_phandle_args *reset_spec)
+{
+	return 0;
+}
+
+static int reset_slb9670_probe(struct platform_device *pdev)
+{
+	struct reset_slb9670 *reset_slb9670;
+	struct device *dev = &pdev->dev;
+
+	reset_slb9670 = devm_kzalloc(dev, sizeof(*reset_slb9670), GFP_KERNEL);
+	if (!reset_slb9670)
+		return -ENOMEM;
+
+	reset_slb9670->gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+	if (IS_ERR(reset_slb9670->gpio))
+		return dev_err_probe(dev, PTR_ERR(reset_slb9670->gpio),
+				     "cannot get reset gpio\n");
+
+	reset_slb9670->rcdev.nr_resets = 1;
+	reset_slb9670->rcdev.owner = THIS_MODULE;
+	reset_slb9670->rcdev.of_node = dev->of_node;
+	reset_slb9670->rcdev.ops = &reset_slb9670_ops;
+	reset_slb9670->rcdev.of_xlate = reset_slb9670_of_xlate;
+	reset_slb9670->rcdev.of_reset_n_cells = 0;
+
+	return devm_reset_controller_register(dev, &reset_slb9670->rcdev);
+}
+
+static const struct of_device_id reset_slb9670_dt_ids[] = {
+	{ .compatible = "infineon,slb9670-reset" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, reset_slb9670_dt_ids);
+
+static struct platform_driver reset_slb9670_driver = {
+	.probe	= reset_slb9670_probe,
+	.driver = {
+		.name		= "reset-slb9670",
+		.of_match_table	= reset_slb9670_dt_ids,
+	},
+};
+module_platform_driver(reset_slb9670_driver);
+
+MODULE_DESCRIPTION("Infineon SLB9670 TPM Reset Driver");
+MODULE_AUTHOR("Lino Sanfilippo <l.sanfilippo@xxxxxxxxxx>");
+MODULE_LICENSE("GPL");
-- 
2.40.1





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux