Message ID | 20190716224518.62556-6-swboyd@chromium.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | tpm: Add driver for cr50 | expand |
On 17.07.2019 00:45, Stephen Boyd wrote: > From: Andrey Pronin <apronin@chromium.org> > > Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50 > firmware. The firmware running on the currently supported H1 > Secure Microcontroller requires a special driver to handle its > specifics: > - need to ensure a certain delay between spi transactions, or else > the chip may miss some part of the next transaction; > - if there is no spi activity for some time, it may go to sleep, > and needs to be waken up before sending further commands; > - access to vendor-specific registers. > > Signed-off-by: Andrey Pronin <apronin@chromium.org> > [swboyd@chromium.org: Replace boilerplate with SPDX tag, drop > suspended bit and remove ifdef checks in cr50.h, push tpm.h > include into cr50.c] > Signed-off-by: Stephen Boyd <swboyd@chromium.org> > --- > drivers/char/tpm/Kconfig | 16 ++ > drivers/char/tpm/Makefile | 2 + > drivers/char/tpm/cr50.c | 33 +++ > drivers/char/tpm/cr50.h | 15 ++ > drivers/char/tpm/cr50_spi.c | 450 ++++++++++++++++++++++++++++++++++++ > 5 files changed, 516 insertions(+) > create mode 100644 drivers/char/tpm/cr50.c > create mode 100644 drivers/char/tpm/cr50.h > create mode 100644 drivers/char/tpm/cr50_spi.c > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > index 88a3c06fc153..b7028bfa6f87 100644 > --- a/drivers/char/tpm/Kconfig > +++ b/drivers/char/tpm/Kconfig > @@ -114,6 +114,22 @@ config TCG_ATMEL > will be accessible from within Linux. To compile this driver > as a module, choose M here; the module will be called tpm_atmel. > > +config TCG_CR50 > + bool > + ---help--- > + Common routines shared by drivers for Cr50-based devices. > + Is it a common pattern to add config options that are not useful on their own? When would I ever enable TCG_CR50 without also enabling TCG_CR50_SPI? Why can't you just use TCG_CR50_SPI for everything? > +config TCG_CR50_SPI > + tristate "Cr50 SPI Interface" > + depends on SPI > + select TCG_TIS_CORE > + select TCG_CR50 > + ---help--- > + If you have a H1 secure module running Cr50 firmware on SPI bus, > + say Yes and it will be accessible from within Linux. To compile > + this driver as a module, choose M here; the module will be called > + cr50_spi. > + > config TCG_INFINEON > tristate "Infineon Technologies TPM Interface" > depends on PNP > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > index a01c4cab902a..4e89538c73c8 100644 > --- a/drivers/char/tpm/Makefile > +++ b/drivers/char/tpm/Makefile > @@ -27,6 +27,8 @@ obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o > obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o > obj-$(CONFIG_TCG_NSC) += tpm_nsc.o > obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o > +obj-$(CONFIG_TCG_CR50) += cr50.o > +obj-$(CONFIG_TCG_CR50_SPI) += cr50_spi.o > obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o > obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o > obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ > diff --git a/drivers/char/tpm/cr50.c b/drivers/char/tpm/cr50.c > new file mode 100644 > index 000000000000..8c83ec25f8e1 > --- /dev/null > +++ b/drivers/char/tpm/cr50.c > @@ -0,0 +1,33 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright 2016 Google Inc. > + */ > + > +/* > + * This file contains common code for devices with Cr50 firmware. > + */ > + > +#include <linux/export.h> > +#include <linux/suspend.h> > +#include "cr50.h" > +#include "tpm.h" > + > +#ifdef CONFIG_PM_SLEEP > +int cr50_resume(struct device *dev) > +{ > + if (pm_suspend_via_firmware()) > + return tpm_pm_resume(dev); > + > + return 0; > +} > +EXPORT_SYMBOL(cr50_resume); > + > +int cr50_suspend(struct device *dev) > +{ > + if (pm_suspend_via_firmware()) > + return tpm_pm_suspend(dev); > + > + return 0; > +} > +EXPORT_SYMBOL(cr50_suspend); > +#endif /* CONFIG_PM_SLEEP */ > diff --git a/drivers/char/tpm/cr50.h b/drivers/char/tpm/cr50.h > new file mode 100644 > index 000000000000..1c635fae03e8 > --- /dev/null > +++ b/drivers/char/tpm/cr50.h > @@ -0,0 +1,15 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright 2016 Google Inc. > + * This file contains common code for devices with Cr50 firmware. > + */ > + > +#ifndef __CR50_H__ > +#define __CR50_H__ > + > +struct device; > + > +int cr50_resume(struct device *dev); > +int cr50_suspend(struct device *dev); > + > +#endif /* __CR50_H__ */ > diff --git a/drivers/char/tpm/cr50_spi.c b/drivers/char/tpm/cr50_spi.c > new file mode 100644 > index 000000000000..3c1b472297ad > --- /dev/null > +++ b/drivers/char/tpm/cr50_spi.c > @@ -0,0 +1,450 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2016 Google, Inc > + * > + * This device driver implements a TCG PTP FIFO interface over SPI for chips > + * with Cr50 firmware. > + * It is based on tpm_tis_spi driver by Peter Huewe and Christophe Ricard. > + */ > + > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/pm.h> > +#include <linux/spi/spi.h> > +#include <linux/wait.h> > +#include "cr50.h" > +#include "tpm_tis_core.h" > + > +/* > + * Cr50 timing constants: > + * - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC. > + * - needs up to CR50_WAKE_START_DELAY_MSEC to wake after sleep. > + * - requires waiting for "ready" IRQ, if supported; or waiting for at least > + * CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported. > + * - waits for up to CR50_FLOW_CONTROL_MSEC for flow control 'ready' indication. > + */ > +#define CR50_SLEEP_DELAY_MSEC 1000 > +#define CR50_WAKE_START_DELAY_MSEC 1 > +#define CR50_NOIRQ_ACCESS_DELAY_MSEC 2 > +#define CR50_READY_IRQ_TIMEOUT_MSEC TPM2_TIMEOUT_A > +#define CR50_FLOW_CONTROL_MSEC TPM2_TIMEOUT_A > +#define MAX_IRQ_CONFIRMATION_ATTEMPTS 3 > + > +#define MAX_SPI_FRAMESIZE 64 > + > +#define TPM_CR50_FW_VER(l) (0x0F90 | ((l) << 12)) > +#define TPM_CR50_MAX_FW_VER_LEN 64 > + > +static unsigned short rng_quality = 1022; > +module_param(rng_quality, ushort, 0644); > +MODULE_PARM_DESC(rng_quality, > + "Estimation of true entropy, in bits per 1024 bits."); What is the purpose of this parameter? None of the other TPM drivers have it. > + > +struct cr50_spi_phy { > + struct tpm_tis_data priv; > + struct spi_device *spi_device; > + > + struct mutex time_track_mutex; > + unsigned long last_access_jiffies; > + unsigned long wake_after_jiffies; > + > + unsigned long access_delay_jiffies; > + unsigned long sleep_delay_jiffies; > + unsigned int wake_start_delay_msec; > + > + struct completion tpm_ready; > + > + unsigned int irq_confirmation_attempt; > + bool irq_needs_confirmation; > + bool irq_confirmed; > + > + u8 tx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned; > + u8 rx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned; > +}; > + > +static struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_data *data) > +{ > + return container_of(data, struct cr50_spi_phy, priv); > +} > + > +/* > + * The cr50 interrupt handler just signals waiting threads that the > + * interrupt was asserted. It does not do any processing triggered > + * by interrupts but is instead used to avoid fixed delays. > + */ > +static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id) > +{ > + struct cr50_spi_phy *phy = dev_id; > + > + phy->irq_confirmed = true; > + complete(&phy->tpm_ready); > + > + return IRQ_HANDLED; > +} > + > +/* > + * Cr50 needs to have at least some delay between consecutive > + * transactions. Make sure we wait. > + */ > +static void cr50_ensure_access_delay(struct cr50_spi_phy *phy) > +{ > + /* > + * Note: There is a small chance, if Cr50 is not accessed in a few days, > + * that time_in_range will not provide the correct result after the wrap > + * around for jiffies. In this case, we'll have an unneeded short delay, > + * which is fine. > + */ > + unsigned long allowed_access = > + phy->last_access_jiffies + phy->access_delay_jiffies; > + unsigned long time_now = jiffies; > + > + if (time_in_range_open(time_now, > + phy->last_access_jiffies, allowed_access)) { > + unsigned long remaining = > + wait_for_completion_timeout(&phy->tpm_ready, > + allowed_access - time_now); > + if (remaining == 0 && phy->irq_confirmed) { > + dev_warn(&phy->spi_device->dev, > + "Timeout waiting for TPM ready IRQ\n"); > + } > + } > + if (phy->irq_needs_confirmation) { > + if (phy->irq_confirmed) { > + phy->irq_needs_confirmation = false; > + phy->access_delay_jiffies = > + msecs_to_jiffies(CR50_READY_IRQ_TIMEOUT_MSEC); > + dev_info(&phy->spi_device->dev, > + "TPM ready IRQ confirmed on attempt %u\n", > + phy->irq_confirmation_attempt); > + } else if (++phy->irq_confirmation_attempt > > + MAX_IRQ_CONFIRMATION_ATTEMPTS) { > + phy->irq_needs_confirmation = false; > + dev_warn(&phy->spi_device->dev, > + "IRQ not confirmed - will use delays\n"); > + } > + } > +} > + > +/* > + * Cr50 might go to sleep if there is no SPI activity for some time and > + * miss the first few bits/bytes on the bus. In such case, wake it up > + * by asserting CS and give it time to start up. > + */ > +static bool cr50_needs_waking(struct cr50_spi_phy *phy) > +{ > + /* > + * Note: There is a small chance, if Cr50 is not accessed in a few days, > + * that time_in_range will not provide the correct result after the wrap > + * around for jiffies. In this case, we'll probably timeout or read > + * incorrect value from TPM_STS and just retry the operation. > + */ > + return !time_in_range_open(jiffies, > + phy->last_access_jiffies, > + phy->wake_after_jiffies); > +} > + > +static void cr50_wake_if_needed(struct cr50_spi_phy *phy) > +{ > + if (cr50_needs_waking(phy)) { > + /* Assert CS, wait 1 msec, deassert CS */ > + struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 }; > + > + spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1); > + /* Wait for it to fully wake */ > + msleep(phy->wake_start_delay_msec); wake_start_delay_msec is always 1, isn't it? (Why is that a variable at all? I see only one place that ever sets it.) Then msleep is not the best function to use, since it will usually sleep much longer. Use usleep_range instead. See Documentation/timers/timers-howto.txt. > + } > + /* Reset the time when we need to wake Cr50 again */ > + phy->wake_after_jiffies = jiffies + phy->sleep_delay_jiffies; > +} > + > +/* > + * Flow control: clock the bus and wait for cr50 to set LSB before > + * sending/receiving data. TCG PTP spec allows it to happen during > + * the last byte of header, but cr50 never does that in practice, > + * and earlier versions had a bug when it was set too early, so don't > + * check for it during header transfer. > + */ > +static int cr50_spi_flow_control(struct cr50_spi_phy *phy) > +{ > + unsigned long timeout_jiffies = > + jiffies + msecs_to_jiffies(CR50_FLOW_CONTROL_MSEC); > + struct spi_message m; > + int ret; > + struct spi_transfer spi_xfer = { > + .rx_buf = phy->rx_buf, > + .len = 1, > + .cs_change = 1, > + }; > + > + do { > + spi_message_init(&m); > + spi_message_add_tail(&spi_xfer, &m); > + ret = spi_sync_locked(phy->spi_device, &m); > + if (ret < 0) > + return ret; > + if (time_after(jiffies, timeout_jiffies)) { > + dev_warn(&phy->spi_device->dev, > + "Timeout during flow control\n"); > + return -EBUSY; > + } > + } while (!(phy->rx_buf[0] & 0x01)); > + return 0; > +} > + > +static int cr50_spi_xfer_bytes(struct tpm_tis_data *data, u32 addr, > + u16 len, const u8 *tx, u8 *rx) > +{ > + struct cr50_spi_phy *phy = to_cr50_spi_phy(data); > + struct spi_message m; > + struct spi_transfer spi_xfer = { > + .tx_buf = phy->tx_buf, > + .rx_buf = phy->rx_buf, > + .len = 4, > + .cs_change = 1, > + }; > + int ret; > + > + if (len > MAX_SPI_FRAMESIZE) > + return -EINVAL; > + > + /* > + * Do this outside of spi_bus_lock in case cr50 is not the > + * only device on that spi bus. > + */ > + mutex_lock(&phy->time_track_mutex); > + cr50_ensure_access_delay(phy); > + cr50_wake_if_needed(phy); > + > + phy->tx_buf[0] = (tx ? 0x00 : 0x80) | (len - 1); > + phy->tx_buf[1] = 0xD4; > + phy->tx_buf[2] = (addr >> 8) & 0xFF; > + phy->tx_buf[3] = addr & 0xFF; > + > + spi_message_init(&m); > + spi_message_add_tail(&spi_xfer, &m); > + > + spi_bus_lock(phy->spi_device->master); > + ret = spi_sync_locked(phy->spi_device, &m); > + if (ret < 0) > + goto exit; > + > + ret = cr50_spi_flow_control(phy); > + if (ret < 0) > + goto exit; > + > + spi_xfer.cs_change = 0; > + spi_xfer.len = len; > + if (tx) { > + memcpy(phy->tx_buf, tx, len); > + spi_xfer.rx_buf = NULL; > + } else { > + spi_xfer.tx_buf = NULL; > + } > + > + spi_message_init(&m); > + spi_message_add_tail(&spi_xfer, &m); > + reinit_completion(&phy->tpm_ready); > + ret = spi_sync_locked(phy->spi_device, &m); > + if (rx) > + memcpy(rx, phy->rx_buf, len); > + > +exit: > + spi_bus_unlock(phy->spi_device->master); > + phy->last_access_jiffies = jiffies; > + mutex_unlock(&phy->time_track_mutex); > + > + return ret; > +} > + > +static int cr50_spi_read_bytes(struct tpm_tis_data *data, u32 addr, > + u16 len, u8 *result) > +{ > + return cr50_spi_xfer_bytes(data, addr, len, NULL, result); > +} > + > +static int cr50_spi_write_bytes(struct tpm_tis_data *data, u32 addr, > + u16 len, const u8 *value) > +{ > + return cr50_spi_xfer_bytes(data, addr, len, value, NULL); > +} > + > +static int cr50_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) > +{ > + int rc; > + __le16 le_val; > + > + rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)&le_val); > + if (!rc) > + *result = le16_to_cpu(le_val); > + return rc; > +} > + > +static int cr50_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) > +{ > + int rc; > + __le32 le_val; > + > + rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)&le_val); > + if (!rc) > + *result = le32_to_cpu(le_val); > + return rc; > +} > + > +static int cr50_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) > +{ > + __le32 le_val = cpu_to_le32(value); > + > + return data->phy_ops->write_bytes(data, addr, sizeof(u32), > + (u8 *)&le_val); > +} > + > +static void cr50_get_fw_version(struct tpm_tis_data *data, char *fw_ver) > +{ > + int i, len = 0; > + char fw_ver_block[4]; > + > + /* > + * Write anything to TPM_CR50_FW_VER to start from the beginning > + * of the version string > + */ > + tpm_tis_write8(data, TPM_CR50_FW_VER(data->locality), 0); > + > + /* Read the string, 4 bytes at a time, until we get '\0' */ > + do { > + tpm_tis_read_bytes(data, TPM_CR50_FW_VER(data->locality), 4, > + fw_ver_block); > + for (i = 0; i < 4 && fw_ver_block[i]; ++len, ++i) > + fw_ver[len] = fw_ver_block[i]; > + } while (i == 4 && len < TPM_CR50_MAX_FW_VER_LEN); > + fw_ver[len] = 0; > +} > + > +static const struct tpm_tis_phy_ops cr50_spi_phy_ops = { > + .read_bytes = cr50_spi_read_bytes, > + .write_bytes = cr50_spi_write_bytes, > + .read16 = cr50_spi_read16, > + .read32 = cr50_spi_read32, > + .write32 = cr50_spi_write32, > + .max_xfer_size = MAX_SPI_FRAMESIZE, > +}; > + > +static int cr50_spi_probe(struct spi_device *dev) > +{ > + char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1]; > + struct cr50_spi_phy *phy; > + int rc; > + > + phy = devm_kzalloc(&dev->dev, sizeof(*phy), GFP_KERNEL); > + if (!phy) > + return -ENOMEM; > + > + phy->spi_device = dev; > + > + phy->access_delay_jiffies = > + msecs_to_jiffies(CR50_NOIRQ_ACCESS_DELAY_MSEC); > + phy->sleep_delay_jiffies = msecs_to_jiffies(CR50_SLEEP_DELAY_MSEC); > + phy->wake_start_delay_msec = CR50_WAKE_START_DELAY_MSEC; > + > + mutex_init(&phy->time_track_mutex); > + phy->wake_after_jiffies = jiffies; > + phy->last_access_jiffies = jiffies; > + > + init_completion(&phy->tpm_ready); > + if (dev->irq > 0) { > + rc = devm_request_irq(&dev->dev, dev->irq, cr50_spi_irq_handler, > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > + "cr50_spi", phy); > + if (rc < 0) { > + if (rc == -EPROBE_DEFER) > + return rc; > + dev_warn(&dev->dev, "Requesting IRQ %d failed: %d\n", > + dev->irq, rc); > + /* > + * This is not fatal, the driver will fall back to > + * delays automatically, since tpm_ready will never > + * be completed without a registered irq handler. > + * So, just fall through. > + */ > + } else { > + /* > + * IRQ requested, let's verify that it is actually > + * triggered, before relying on it. > + */ > + phy->irq_needs_confirmation = true; > + } > + } else { > + dev_warn(&dev->dev, > + "No IRQ - will use delays between transactions.\n"); > + } > + > + phy->priv.rng_quality = rng_quality; > + > + rc = tpm_tis_core_init(&dev->dev, &phy->priv, -1, &cr50_spi_phy_ops, > + NULL); > + if (rc < 0) > + return rc; > + > + cr50_get_fw_version(&phy->priv, fw_ver); > + dev_info(&dev->dev, "Cr50 firmware version: %s\n", fw_ver); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int cr50_spi_resume(struct device *dev) > +{ > + struct tpm_chip *chip = dev_get_drvdata(dev); > + struct tpm_tis_data *data = dev_get_drvdata(&chip->dev); > + struct cr50_spi_phy *phy = to_cr50_spi_phy(data); > + > + /* > + * Jiffies not increased during suspend, so we need to reset > + * the time to wake Cr50 after resume. > + */ > + phy->wake_after_jiffies = jiffies; > + > + return cr50_resume(dev); > +} > +#endif > + > +static SIMPLE_DEV_PM_OPS(cr50_spi_pm, cr50_suspend, cr50_spi_resume); > + > +static int cr50_spi_remove(struct spi_device *dev) > +{ > + struct tpm_chip *chip = spi_get_drvdata(dev); > + > + tpm_chip_unregister(chip); > + tpm_tis_remove(chip); > + return 0; > +} > + > +static const struct spi_device_id cr50_spi_id[] = { > + { "cr50", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(spi, cr50_spi_id); > + > +#ifdef CONFIG_OF > +static const struct of_device_id of_cr50_spi_match[] = { > + { .compatible = "google,cr50", }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, of_cr50_spi_match); > +#endif > + > +static struct spi_driver cr50_spi_driver = { > + .driver = { > + .name = "cr50_spi", > + .pm = &cr50_spi_pm, > + .of_match_table = of_match_ptr(of_cr50_spi_match), > + }, > + .probe = cr50_spi_probe, > + .remove = cr50_spi_remove, > + .id_table = cr50_spi_id, > +}; > +module_spi_driver(cr50_spi_driver); > + > +MODULE_DESCRIPTION("Cr50 TCG PTP FIFO SPI driver"); > +MODULE_LICENSE("GPL v2"); This copies a lot of code from tpm_tis_spi, but then slightly changes some things, without really explaining why. For example, struct cr50_spi_phy contains both tx_buf and rx_buf, whereas tpm_tis_spi uses a single iobuf, that is allocated via devm_kmalloc instead of being part of the struct. Maybe the difference matters, maybe not, who knows? Can't the code be shared more explicitly, e.g. by cr50_spi wrapping tpm_tis_spi, so that it can intercept the calls, execute the additional actions (like waking up the device), but then let tpm_tis_spi do the common work? Alexander
On Wed, Jul 17, 2019 at 02:00:06PM +0200, Alexander Steffen wrote: > On 17.07.2019 00:45, Stephen Boyd wrote: > > From: Andrey Pronin <apronin@chromium.org> > > > > Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50 > > firmware. The firmware running on the currently supported H1 > > Secure Microcontroller requires a special driver to handle its > > specifics: > > - need to ensure a certain delay between spi transactions, or else > > the chip may miss some part of the next transaction; > > - if there is no spi activity for some time, it may go to sleep, > > and needs to be waken up before sending further commands; > > - access to vendor-specific registers. > > > > Signed-off-by: Andrey Pronin <apronin@chromium.org> > > [swboyd@chromium.org: Replace boilerplate with SPDX tag, drop > > suspended bit and remove ifdef checks in cr50.h, push tpm.h > > include into cr50.c] > > Signed-off-by: Stephen Boyd <swboyd@chromium.org> > > drivers/char/tpm/Kconfig | 16 ++ > > drivers/char/tpm/Makefile | 2 + > > drivers/char/tpm/cr50.c | 33 +++ > > drivers/char/tpm/cr50.h | 15 ++ > > drivers/char/tpm/cr50_spi.c | 450 ++++++++++++++++++++++++++++++++++++ > > 5 files changed, 516 insertions(+) > > create mode 100644 drivers/char/tpm/cr50.c > > create mode 100644 drivers/char/tpm/cr50.h > > create mode 100644 drivers/char/tpm/cr50_spi.c > > > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > > index 88a3c06fc153..b7028bfa6f87 100644 > > +++ b/drivers/char/tpm/Kconfig > > @@ -114,6 +114,22 @@ config TCG_ATMEL > > will be accessible from within Linux. To compile this driver > > as a module, choose M here; the module will be called tpm_atmel. > > +config TCG_CR50 > > + bool > > + ---help--- > > + Common routines shared by drivers for Cr50-based devices. > > + > > Is it a common pattern to add config options that are not useful on their > own? When would I ever enable TCG_CR50 without also enabling TCG_CR50_SPI? > Why can't you just use TCG_CR50_SPI for everything? This is an internal kconfig symbol, it isn't seen by the user, which is a pretty normal pattern. But I don't think the help should be included (since it cannot be seen), and I'm pretty sure it should be a tristate But overall, it might be better to just double link the little helper: obj-$(CONFIG_TCG_CR50_SPI) += cr50.o cr50_spi.o obj-$(CONFIG_TCG_CR50_I2C) += cr50.o cr50_i2c.o As we don't actually ever expect to load both modules into the same system Jason
Quoting Jason Gunthorpe (2019-07-17 05:25:58) > On Wed, Jul 17, 2019 at 02:00:06PM +0200, Alexander Steffen wrote: > > On 17.07.2019 00:45, Stephen Boyd wrote: > > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > > > index 88a3c06fc153..b7028bfa6f87 100644 > > > +++ b/drivers/char/tpm/Kconfig > > > @@ -114,6 +114,22 @@ config TCG_ATMEL > > > will be accessible from within Linux. To compile this driver > > > as a module, choose M here; the module will be called tpm_atmel. > > > +config TCG_CR50 > > > + bool > > > + ---help--- > > > + Common routines shared by drivers for Cr50-based devices. > > > + > > > > Is it a common pattern to add config options that are not useful on their > > own? When would I ever enable TCG_CR50 without also enabling TCG_CR50_SPI? > > Why can't you just use TCG_CR50_SPI for everything? > > This is an internal kconfig symbol, it isn't seen by the user, which > is a pretty normal pattern. > > But I don't think the help should be included (since it cannot be > seen), and I'm pretty sure it should be a tristate Good point. I'll fix it. > > But overall, it might be better to just double link the little helper: > > obj-$(CONFIG_TCG_CR50_SPI) += cr50.o cr50_spi.o > obj-$(CONFIG_TCG_CR50_I2C) += cr50.o cr50_i2c.o > > As we don't actually ever expect to load both modules into the same > system > Sometimes we have both drivers built-in. To maintain the tiny space savings I'd prefer to just leave this as helpless and tristate.
On Wed, Jul 17, 2019 at 09:49:42AM -0700, Stephen Boyd wrote: > Quoting Jason Gunthorpe (2019-07-17 05:25:58) > > On Wed, Jul 17, 2019 at 02:00:06PM +0200, Alexander Steffen wrote: > > > On 17.07.2019 00:45, Stephen Boyd wrote: > > > > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig > > > > index 88a3c06fc153..b7028bfa6f87 100644 > > > > +++ b/drivers/char/tpm/Kconfig > > > > @@ -114,6 +114,22 @@ config TCG_ATMEL > > > > will be accessible from within Linux. To compile this driver > > > > as a module, choose M here; the module will be called tpm_atmel. > > > > +config TCG_CR50 > > > > + bool > > > > + ---help--- > > > > + Common routines shared by drivers for Cr50-based devices. > > > > + > > > > > > Is it a common pattern to add config options that are not useful on their > > > own? When would I ever enable TCG_CR50 without also enabling TCG_CR50_SPI? > > > Why can't you just use TCG_CR50_SPI for everything? > > > > This is an internal kconfig symbol, it isn't seen by the user, which > > is a pretty normal pattern. > > > > But I don't think the help should be included (since it cannot be > > seen), and I'm pretty sure it should be a tristate > > Good point. I'll fix it. > > > > > But overall, it might be better to just double link the little helper: > > > > obj-$(CONFIG_TCG_CR50_SPI) += cr50.o cr50_spi.o > > obj-$(CONFIG_TCG_CR50_I2C) += cr50.o cr50_i2c.o > > > > As we don't actually ever expect to load both modules into the same > > system > > > > Sometimes we have both drivers built-in. To maintain the tiny space > savings I'd prefer to just leave this as helpless and tristate. If it is builtin you only get one copy of cr50.o anyhow. The only differences is for modules, then the two modules will both be a bit bigger instead of a 3rd module being created Jason
Quoting Jason Gunthorpe (2019-07-17 09:56:28) > On Wed, Jul 17, 2019 at 09:49:42AM -0700, Stephen Boyd wrote: > > Quoting Jason Gunthorpe (2019-07-17 05:25:58) > > > On Wed, Jul 17, 2019 at 02:00:06PM +0200, Alexander Steffen wrote: > > > > On 17.07.2019 00:45, Stephen Boyd wrote: > > > > > > But overall, it might be better to just double link the little helper: > > > > > > obj-$(CONFIG_TCG_CR50_SPI) += cr50.o cr50_spi.o > > > obj-$(CONFIG_TCG_CR50_I2C) += cr50.o cr50_i2c.o > > > > > > As we don't actually ever expect to load both modules into the same > > > system > > > > > > > Sometimes we have both drivers built-in. To maintain the tiny space > > savings I'd prefer to just leave this as helpless and tristate. > > If it is builtin you only get one copy of cr50.o anyhow. The only > differences is for modules, then the two modules will both be a bit > bigger instead of a 3rd module being created > Yes. The space savings comes from having the extra module 'cr50.ko' that holds almost nothing at all when the two drivers are modules.
On Wed, Jul 17, 2019 at 10:05:52AM -0700, Stephen Boyd wrote: > Quoting Jason Gunthorpe (2019-07-17 09:56:28) > > On Wed, Jul 17, 2019 at 09:49:42AM -0700, Stephen Boyd wrote: > > > Quoting Jason Gunthorpe (2019-07-17 05:25:58) > > > > On Wed, Jul 17, 2019 at 02:00:06PM +0200, Alexander Steffen wrote: > > > > > On 17.07.2019 00:45, Stephen Boyd wrote: > > > > > > > > But overall, it might be better to just double link the little helper: > > > > > > > > obj-$(CONFIG_TCG_CR50_SPI) += cr50.o cr50_spi.o > > > > obj-$(CONFIG_TCG_CR50_I2C) += cr50.o cr50_i2c.o > > > > > > > > As we don't actually ever expect to load both modules into the same > > > > system > > > > > > > > > > Sometimes we have both drivers built-in. To maintain the tiny space > > > savings I'd prefer to just leave this as helpless and tristate. > > > > If it is builtin you only get one copy of cr50.o anyhow. The only > > differences is for modules, then the two modules will both be a bit > > bigger instead of a 3rd module being created > > > > Yes. The space savings comes from having the extra module 'cr50.ko' that > holds almost nothing at all when the two drivers are modules. I'm not sure it is an actual savings, there is alot of minimum overhead and alignment to have a module in the first place. Jason
Quoting Jason Gunthorpe (2019-07-17 10:12:16) > On Wed, Jul 17, 2019 at 10:05:52AM -0700, Stephen Boyd wrote: > > > > Yes. The space savings comes from having the extra module 'cr50.ko' that > > holds almost nothing at all when the two drivers are modules. > > I'm not sure it is an actual savings, there is alot of minimum > overhead and alignment to have a module in the first place. > Yeah. I'm pretty sure that's why it's a bool and not a tristate for this symbol. A module has overhead that is not necessary for these little helpers.
On Wed, Jul 17, 2019 at 10:22:20AM -0700, Stephen Boyd wrote: > Quoting Jason Gunthorpe (2019-07-17 10:12:16) > > On Wed, Jul 17, 2019 at 10:05:52AM -0700, Stephen Boyd wrote: > > > > > > Yes. The space savings comes from having the extra module 'cr50.ko' that > > > holds almost nothing at all when the two drivers are modules. > > > > I'm not sure it is an actual savings, there is alot of minimum > > overhead and alignment to have a module in the first place. > > > > Yeah. I'm pretty sure that's why it's a bool and not a tristate for this > symbol. A module has overhead that is not necessary for these little > helpers. Linking driver stuff like that to the kernel is pretty hacky, IMHO Jason
Quoting Jason Gunthorpe (2019-07-17 10:25:44) > On Wed, Jul 17, 2019 at 10:22:20AM -0700, Stephen Boyd wrote: > > Quoting Jason Gunthorpe (2019-07-17 10:12:16) > > > On Wed, Jul 17, 2019 at 10:05:52AM -0700, Stephen Boyd wrote: > > > > > > > > Yes. The space savings comes from having the extra module 'cr50.ko' that > > > > holds almost nothing at all when the two drivers are modules. > > > > > > I'm not sure it is an actual savings, there is alot of minimum > > > overhead and alignment to have a module in the first place. > > > > > > > Yeah. I'm pretty sure that's why it's a bool and not a tristate for this > > symbol. A module has overhead that is not necessary for these little > > helpers. > > Linking driver stuff like that to the kernel is pretty hacky, IMHO > So combine lines? obj-$(CONFIG_...) += cr50.o cr50_spi.o Sounds great.
On Wed, Jul 17, 2019 at 11:21 AM Stephen Boyd <swboyd@chromium.org> wrote: > > Quoting Jason Gunthorpe (2019-07-17 10:25:44) > > On Wed, Jul 17, 2019 at 10:22:20AM -0700, Stephen Boyd wrote: > > > Quoting Jason Gunthorpe (2019-07-17 10:12:16) > > > > On Wed, Jul 17, 2019 at 10:05:52AM -0700, Stephen Boyd wrote: > > > > > > > > > > Yes. The space savings comes from having the extra module 'cr50.ko' that > > > > > holds almost nothing at all when the two drivers are modules. > > > > > > > > I'm not sure it is an actual savings, there is alot of minimum > > > > overhead and alignment to have a module in the first place. > > > > > > > > > > Yeah. I'm pretty sure that's why it's a bool and not a tristate for this > > > symbol. A module has overhead that is not necessary for these little > > > helpers. > > > > Linking driver stuff like that to the kernel is pretty hacky, IMHO > > > > So combine lines? > > obj-$(CONFIG_...) += cr50.o cr50_spi.o > > Sounds great. > Please keep in mind that cr50.c exports symbols. If cr50.o is added to two modules, those symbols will subsequently available from both modules. To avoid that, you might want to consider removing the EXPORT_SYMBOL() declarations from cr50.c. I don't know what happens if those two modules are both built into the kernel (as happens, for example, with allyesconfig). Does the linker try to load cr50.o twice, resulting in duplicate symbols ? Guenter
On Wed, Jul 17, 2019 at 11:30:42AM -0700, Guenter Roeck wrote: > On Wed, Jul 17, 2019 at 11:21 AM Stephen Boyd <swboyd@chromium.org> wrote: > > > > Quoting Jason Gunthorpe (2019-07-17 10:25:44) > > > On Wed, Jul 17, 2019 at 10:22:20AM -0700, Stephen Boyd wrote: > > > > Quoting Jason Gunthorpe (2019-07-17 10:12:16) > > > > > On Wed, Jul 17, 2019 at 10:05:52AM -0700, Stephen Boyd wrote: > > > > > > > > > > > > Yes. The space savings comes from having the extra module 'cr50.ko' that > > > > > > holds almost nothing at all when the two drivers are modules. > > > > > > > > > > I'm not sure it is an actual savings, there is alot of minimum > > > > > overhead and alignment to have a module in the first place. > > > > > > > > > > > > > Yeah. I'm pretty sure that's why it's a bool and not a tristate for this > > > > symbol. A module has overhead that is not necessary for these little > > > > helpers. > > > > > > Linking driver stuff like that to the kernel is pretty hacky, IMHO > > > > > > > So combine lines? > > > > obj-$(CONFIG_...) += cr50.o cr50_spi.o > > > > Sounds great. > > > > Please keep in mind that cr50.c exports symbols. If cr50.o is added to > two modules, those symbols will subsequently available from both > modules. To avoid that, you might want to consider removing the > EXPORT_SYMBOL() declarations from cr50.c. Yep > I don't know what happens if those two modules are both built into the > kernel (as happens, for example, with allyesconfig). Does the linker > try to load cr50.o twice, resulting in duplicate symbols ? Hum. Looks like it uses --whole-archive here and would probably break? Maybe not, hns recently sent a patch doing this, but maybe they never tested it too. Jason
Quoting Alexander Steffen (2019-07-17 05:00:06) > On 17.07.2019 00:45, Stephen Boyd wrote: > > From: Andrey Pronin <apronin@chromium.org> > > > > Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50 > > firmware. The firmware running on the currently supported H1 > > Secure Microcontroller requires a special driver to handle its > > specifics: > > - need to ensure a certain delay between spi transactions, or else > > the chip may miss some part of the next transaction; > > - if there is no spi activity for some time, it may go to sleep, > > and needs to be waken up before sending further commands; > > - access to vendor-specific registers. > > > > Signed-off-by: Andrey Pronin <apronin@chromium.org> > > [swboyd@chromium.org: Replace boilerplate with SPDX tag, drop > > suspended bit and remove ifdef checks in cr50.h, push tpm.h > > include into cr50.c] > > Signed-off-by: Stephen Boyd <swboyd@chromium.org> > > diff --git a/drivers/char/tpm/cr50_spi.c b/drivers/char/tpm/cr50_spi.c > > new file mode 100644 > > index 000000000000..3c1b472297ad > > --- /dev/null > > +++ b/drivers/char/tpm/cr50_spi.c > > @@ -0,0 +1,450 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (C) 2016 Google, Inc > > + * > > + * This device driver implements a TCG PTP FIFO interface over SPI for chips > > + * with Cr50 firmware. > > + * It is based on tpm_tis_spi driver by Peter Huewe and Christophe Ricard. > > + */ > > + > > +#include <linux/init.h> > > +#include <linux/interrupt.h> > > +#include <linux/module.h> > > +#include <linux/of.h> > > +#include <linux/pm.h> > > +#include <linux/spi/spi.h> > > +#include <linux/wait.h> > > +#include "cr50.h" > > +#include "tpm_tis_core.h" > > + > > +/* > > + * Cr50 timing constants: > > + * - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC. > > + * - needs up to CR50_WAKE_START_DELAY_MSEC to wake after sleep. > > + * - requires waiting for "ready" IRQ, if supported; or waiting for at least > > + * CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported. > > + * - waits for up to CR50_FLOW_CONTROL_MSEC for flow control 'ready' indication. > > + */ > > +#define CR50_SLEEP_DELAY_MSEC 1000 > > +#define CR50_WAKE_START_DELAY_MSEC 1 > > +#define CR50_NOIRQ_ACCESS_DELAY_MSEC 2 > > +#define CR50_READY_IRQ_TIMEOUT_MSEC TPM2_TIMEOUT_A > > +#define CR50_FLOW_CONTROL_MSEC TPM2_TIMEOUT_A > > +#define MAX_IRQ_CONFIRMATION_ATTEMPTS 3 > > + > > +#define MAX_SPI_FRAMESIZE 64 > > + > > +#define TPM_CR50_FW_VER(l) (0x0F90 | ((l) << 12)) > > +#define TPM_CR50_MAX_FW_VER_LEN 64 > > + > > +static unsigned short rng_quality = 1022; > > +module_param(rng_quality, ushort, 0644); > > +MODULE_PARM_DESC(rng_quality, > > + "Estimation of true entropy, in bits per 1024 bits."); > > What is the purpose of this parameter? None of the other TPM drivers > have it. I think the idea is to let users override the quality if they decide that they don't want to use the default value specified in the driver. > > > + > > +struct cr50_spi_phy { > > + struct tpm_tis_data priv; > > + struct spi_device *spi_device; > > + > > + struct mutex time_track_mutex; > > + unsigned long last_access_jiffies; > > + unsigned long wake_after_jiffies; > > + > > + unsigned long access_delay_jiffies; > > + unsigned long sleep_delay_jiffies; > > + unsigned int wake_start_delay_msec; > > + > > + struct completion tpm_ready; > > + > > + unsigned int irq_confirmation_attempt; > > + bool irq_needs_confirmation; > > + bool irq_confirmed; > > + > > + u8 tx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned; > > + u8 rx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned; > > +}; > > + > > +static struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_data *data) > > +{ > > + return container_of(data, struct cr50_spi_phy, priv); > > +} > > + > > +/* > > + * The cr50 interrupt handler just signals waiting threads that the > > + * interrupt was asserted. It does not do any processing triggered > > + * by interrupts but is instead used to avoid fixed delays. > > + */ > > +static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id) > > +{ > > + struct cr50_spi_phy *phy = dev_id; > > + > > + phy->irq_confirmed = true; > > + complete(&phy->tpm_ready); > > + > > + return IRQ_HANDLED; > > +} > > + > > +/* > > + * Cr50 needs to have at least some delay between consecutive > > + * transactions. Make sure we wait. > > + */ > > +static void cr50_ensure_access_delay(struct cr50_spi_phy *phy) > > +{ > > + /* > > + * Note: There is a small chance, if Cr50 is not accessed in a few days, > > + * that time_in_range will not provide the correct result after the wrap > > + * around for jiffies. In this case, we'll have an unneeded short delay, > > + * which is fine. > > + */ > > + unsigned long allowed_access = > > + phy->last_access_jiffies + phy->access_delay_jiffies; > > + unsigned long time_now = jiffies; > > + > > + if (time_in_range_open(time_now, > > + phy->last_access_jiffies, allowed_access)) { > > + unsigned long remaining = > > + wait_for_completion_timeout(&phy->tpm_ready, > > + allowed_access - time_now); > > + if (remaining == 0 && phy->irq_confirmed) { > > + dev_warn(&phy->spi_device->dev, > > + "Timeout waiting for TPM ready IRQ\n"); > > + } > > + } > > + if (phy->irq_needs_confirmation) { > > + if (phy->irq_confirmed) { > > + phy->irq_needs_confirmation = false; > > + phy->access_delay_jiffies = > > + msecs_to_jiffies(CR50_READY_IRQ_TIMEOUT_MSEC); > > + dev_info(&phy->spi_device->dev, > > + "TPM ready IRQ confirmed on attempt %u\n", > > + phy->irq_confirmation_attempt); > > + } else if (++phy->irq_confirmation_attempt > > > + MAX_IRQ_CONFIRMATION_ATTEMPTS) { > > + phy->irq_needs_confirmation = false; > > + dev_warn(&phy->spi_device->dev, > > + "IRQ not confirmed - will use delays\n"); > > + } > > + } > > +} > > + > > +/* > > + * Cr50 might go to sleep if there is no SPI activity for some time and > > + * miss the first few bits/bytes on the bus. In such case, wake it up > > + * by asserting CS and give it time to start up. > > + */ > > +static bool cr50_needs_waking(struct cr50_spi_phy *phy) > > +{ > > + /* > > + * Note: There is a small chance, if Cr50 is not accessed in a few days, > > + * that time_in_range will not provide the correct result after the wrap > > + * around for jiffies. In this case, we'll probably timeout or read > > + * incorrect value from TPM_STS and just retry the operation. > > + */ > > + return !time_in_range_open(jiffies, > > + phy->last_access_jiffies, > > + phy->wake_after_jiffies); > > +} > > + > > +static void cr50_wake_if_needed(struct cr50_spi_phy *phy) > > +{ > > + if (cr50_needs_waking(phy)) { > > + /* Assert CS, wait 1 msec, deassert CS */ > > + struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 }; > > + > > + spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1); > > + /* Wait for it to fully wake */ > > + msleep(phy->wake_start_delay_msec); > > wake_start_delay_msec is always 1, isn't it? (Why is that a variable at > all? I see only one place that ever sets it.) Then msleep is not the > best function to use, since it will usually sleep much longer. Use > usleep_range instead. See Documentation/timers/timers-howto.txt. Thanks. Will fix to be 1ms to 2ms range. > > > + } > > + /* Reset the time when we need to wake Cr50 again */ > > + phy->wake_after_jiffies = jiffies + phy->sleep_delay_jiffies; > > +} > > + > > +/* > > + * Flow control: clock the bus and wait for cr50 to set LSB before > > + * sending/receiving data. TCG PTP spec allows it to happen during > > + * the last byte of header, but cr50 never does that in practice, > > + * and earlier versions had a bug when it was set too early, so don't > > + * check for it during header transfer. > > + */ > > +static int cr50_spi_flow_control(struct cr50_spi_phy *phy) > > +{ > > + unsigned long timeout_jiffies = > > + jiffies + msecs_to_jiffies(CR50_FLOW_CONTROL_MSEC); > > + struct spi_message m; > > + int ret; > > + struct spi_transfer spi_xfer = { > > + .rx_buf = phy->rx_buf, > > + .len = 1, > > + .cs_change = 1, > > + }; > > + > > + do { > > + spi_message_init(&m); > > + spi_message_add_tail(&spi_xfer, &m); > > + ret = spi_sync_locked(phy->spi_device, &m); > > + if (ret < 0) > > + return ret; > > + if (time_after(jiffies, timeout_jiffies)) { > > + dev_warn(&phy->spi_device->dev, > > + "Timeout during flow control\n"); > > + return -EBUSY; > > + } > > + } while (!(phy->rx_buf[0] & 0x01)); > > + return 0; > > +} > > + > > +static int cr50_spi_xfer_bytes(struct tpm_tis_data *data, u32 addr, > > + u16 len, const u8 *tx, u8 *rx) > > +{ > > + struct cr50_spi_phy *phy = to_cr50_spi_phy(data); > > + struct spi_message m; > > + struct spi_transfer spi_xfer = { > > + .tx_buf = phy->tx_buf, > > + .rx_buf = phy->rx_buf, > > + .len = 4, > > + .cs_change = 1, > > + }; > > + int ret; > > + > > + if (len > MAX_SPI_FRAMESIZE) > > + return -EINVAL; > > + > > + /* > > + * Do this outside of spi_bus_lock in case cr50 is not the > > + * only device on that spi bus. > > + */ > > + mutex_lock(&phy->time_track_mutex); > > + cr50_ensure_access_delay(phy); > > + cr50_wake_if_needed(phy); > > + > > + phy->tx_buf[0] = (tx ? 0x00 : 0x80) | (len - 1); > > + phy->tx_buf[1] = 0xD4; > > + phy->tx_buf[2] = (addr >> 8) & 0xFF; > > + phy->tx_buf[3] = addr & 0xFF; > > + > > + spi_message_init(&m); > > + spi_message_add_tail(&spi_xfer, &m); > > + > > + spi_bus_lock(phy->spi_device->master); > > + ret = spi_sync_locked(phy->spi_device, &m); > > + if (ret < 0) > > + goto exit; > > + > > + ret = cr50_spi_flow_control(phy); > > + if (ret < 0) > > + goto exit; > > + > > + spi_xfer.cs_change = 0; > > + spi_xfer.len = len; > > + if (tx) { > > + memcpy(phy->tx_buf, tx, len); > > + spi_xfer.rx_buf = NULL; > > + } else { > > + spi_xfer.tx_buf = NULL; > > + } > > + > > + spi_message_init(&m); > > + spi_message_add_tail(&spi_xfer, &m); > > + reinit_completion(&phy->tpm_ready); > > + ret = spi_sync_locked(phy->spi_device, &m); > > + if (rx) > > + memcpy(rx, phy->rx_buf, len); > > + > > +exit: > > + spi_bus_unlock(phy->spi_device->master); > > + phy->last_access_jiffies = jiffies; > > + mutex_unlock(&phy->time_track_mutex); > > + > > + return ret; > > +} > > This copies a lot of code from tpm_tis_spi, but then slightly changes > some things, without really explaining why. The commit text has some explanations. Here's the copy/paste from above: > > - need to ensure a certain delay between spi transactions, or else > > the chip may miss some part of the next transaction; > > - if there is no spi activity for some time, it may go to sleep, > > and needs to be waken up before sending further commands; > > - access to vendor-specific registers. Do you want me to describe something further? > For example, struct > cr50_spi_phy contains both tx_buf and rx_buf, whereas tpm_tis_spi uses a > single iobuf, that is allocated via devm_kmalloc instead of being part > of the struct. Maybe the difference matters, maybe not, who knows? Ok. Are you asking if this is a full-duplex SPI device? > > Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > tpm_tis_spi, so that it can intercept the calls, execute the additional > actions (like waking up the device), but then let tpm_tis_spi do the > common work? > I suppose the read{16,32} and write32 functions could be reused. I'm not sure how great it will be if we combine these two drivers, but I can give it a try today and see how it looks.
Quoting Stephen Boyd (2019-07-17 12:57:34) > Quoting Alexander Steffen (2019-07-17 05:00:06) > > > > Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > > tpm_tis_spi, so that it can intercept the calls, execute the additional > > actions (like waking up the device), but then let tpm_tis_spi do the > > common work? > > > > I suppose the read{16,32} and write32 functions could be reused. I'm not > sure how great it will be if we combine these two drivers, but I can > give it a try today and see how it looks. > Here's the patch. I haven't tested it besides compile testing. ----8<---- diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c index 19513e622053..12f4026c3620 100644 --- a/drivers/char/tpm/tpm_tis_spi.c +++ b/drivers/char/tpm/tpm_tis_spi.c @@ -34,14 +34,54 @@ #include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/tpm.h> +#include "cr50.h" #include "tpm.h" #include "tpm_tis_core.h" #define MAX_SPI_FRAMESIZE 64 +/* + * Cr50 timing constants: + * - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC. + * - needs up to CR50_WAKE_START_DELAY_USEC to wake after sleep. + * - requires waiting for "ready" IRQ, if supported; or waiting for at least + * CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported. + * - waits for up to CR50_FLOW_CONTROL for flow control 'ready' indication. + */ +#define CR50_SLEEP_DELAY_MSEC 1000 +#define CR50_WAKE_START_DELAY_USEC 1000 +#define CR50_NOIRQ_ACCESS_DELAY msecs_to_jiffies(2) +#define CR50_READY_IRQ_TIMEOUT msecs_to_jiffies(TPM2_TIMEOUT_A) +#define CR50_FLOW_CONTROL msecs_to_jiffies(TPM2_TIMEOUT_A) +#define MAX_IRQ_CONFIRMATION_ATTEMPTS 3 + +#define TPM_CR50_FW_VER(l) (0x0f90 | ((l) << 12)) +#define TPM_CR50_MAX_FW_VER_LEN 64 +#define TIS_IS_CR50 1 + +static unsigned short rng_quality = 1022; +module_param(rng_quality, ushort, 0644); +MODULE_PARM_DESC(rng_quality, + "Estimation of true entropy, in bits per 1024 bits."); + + struct tpm_tis_spi_phy { struct tpm_tis_data priv; struct spi_device *spi_device; + + struct mutex time_track_mutex; + unsigned long last_access; + unsigned long wake_after; + + unsigned long access_delay; + + struct completion ready; + + unsigned int irq_confirmation_attempt; + bool irq_needs_confirmation; + bool irq_confirmed; + bool is_cr50; + u8 *iobuf; }; @@ -50,6 +90,127 @@ static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *da return container_of(data, struct tpm_tis_spi_phy, priv); } +/* + * The cr50 interrupt handler just signals waiting threads that the + * interrupt was asserted. It does not do any processing triggered + * by interrupts but is instead used to avoid fixed delays. + */ +static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id) +{ + struct tpm_tis_spi_phy *phy = dev_id; + + phy->irq_confirmed = true; + complete(&phy->ready); + + return IRQ_HANDLED; +} + +/* + * Cr50 needs to have at least some delay between consecutive + * transactions. Make sure we wait. + */ +static void cr50_ensure_access_delay(struct tpm_tis_spi_phy *phy) +{ + unsigned long allowed_access = phy->last_access + phy->access_delay; + unsigned long time_now = jiffies; + struct device *dev = &phy->spi_device->dev; + + /* + * Note: There is a small chance, if Cr50 is not accessed in a few days, + * that time_in_range will not provide the correct result after the wrap + * around for jiffies. In this case, we'll have an unneeded short delay, + * which is fine. + */ + if (time_in_range_open(time_now, phy->last_access, allowed_access)) { + unsigned long remaining, timeout = allowed_access - time_now; + + remaining = wait_for_completion_timeout(&phy->ready, timeout); + if (!remaining && phy->irq_confirmed) + dev_warn(dev, "Timeout waiting for TPM ready IRQ\n"); + } + + if (phy->irq_needs_confirmation) { + unsigned int attempt = ++phy->irq_confirmation_attempt; + + if (phy->irq_confirmed) { + phy->irq_needs_confirmation = false; + phy->access_delay = CR50_READY_IRQ_TIMEOUT; + dev_info(dev, "TPM ready IRQ confirmed on attempt %u\n", + attempt); + } else if (attempt > MAX_IRQ_CONFIRMATION_ATTEMPTS) { + phy->irq_needs_confirmation = false; + dev_warn(dev, "IRQ not confirmed - will use delays\n"); + } + } +} + +/* + * Cr50 might go to sleep if there is no SPI activity for some time and + * miss the first few bits/bytes on the bus. In such case, wake it up + * by asserting CS and give it time to start up. + */ +static bool cr50_needs_waking(struct tpm_tis_spi_phy *phy) +{ + /* + * Note: There is a small chance, if Cr50 is not accessed in a few days, + * that time_in_range will not provide the correct result after the wrap + * around for jiffies. In this case, we'll probably timeout or read + * incorrect value from TPM_STS and just retry the operation. + */ + return !time_in_range_open(jiffies, phy->last_access, phy->wake_after); +} + +static void cr50_wake_if_needed(struct tpm_tis_spi_phy *phy) +{ + if (cr50_needs_waking(phy)) { + /* Assert CS, wait 1 msec, deassert CS */ + struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 }; + + spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1); + /* Wait for it to fully wake */ + usleep_range(CR50_WAKE_START_DELAY_USEC, + CR50_WAKE_START_DELAY_USEC * 2); + } + /* Reset the time when we need to wake Cr50 again */ + phy->wake_after = jiffies + msecs_to_jiffies(CR50_SLEEP_DELAY_MSEC); + +} + +/* + * Flow control: clock the bus and wait for cr50 to set LSB before + * sending/receiving data. TCG PTP spec allows it to happen during + * the last byte of header, but cr50 never does that in practice, + * and earlier versions had a bug when it was set too early, so don't + * check for it during header transfer. + */ +static int cr50_spi_flow_control(struct tpm_tis_spi_phy *phy) +{ + struct device *dev = &phy->spi_device->dev; + unsigned long timeout = jiffies + CR50_FLOW_CONTROL; + struct spi_message m; + int ret; + struct spi_transfer spi_xfer = { + .rx_buf = phy->iobuf, + .len = 1, + .cs_change = 1, + }; + + do { + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + return ret; + + if (time_after(jiffies, timeout)) { + dev_warn(dev, "Timeout during flow control\n"); + return -EBUSY; + } + } while (!(phy->iobuf[0] & 0x01)); + + return 0; +} + static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, u8 *in, const u8 *out) { @@ -60,6 +221,12 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, struct spi_transfer spi_xfer; u8 transfer_len; + mutex_lock(&phy->time_track_mutex); + if (phy->is_cr50) { + cr50_ensure_access_delay(phy); + cr50_wake_if_needed(phy); + } + spi_bus_lock(phy->spi_device->master); while (len) { @@ -82,7 +249,11 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, if (ret < 0) goto exit; - if ((phy->iobuf[3] & 0x01) == 0) { + if (phy->is_cr50) { + ret = cr50_spi_flow_control(phy); + if (ret < 0) + goto exit; + } else if ((phy->iobuf[3] & 0x01) == 0) { // handle SPI wait states phy->iobuf[0] = 0; @@ -117,6 +288,7 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, spi_message_init(&m); spi_message_add_tail(&spi_xfer, &m); + reinit_completion(&phy->ready); ret = spi_sync_locked(phy->spi_device, &m); if (ret < 0) goto exit; @@ -131,6 +303,8 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, exit: spi_bus_unlock(phy->spi_device->master); + phy->last_access = jiffies; + mutex_lock(&phy->time_track_mutex); return ret; } @@ -192,10 +366,37 @@ static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { .write32 = tpm_tis_spi_write32, }; +static void cr50_print_fw_version(struct tpm_tis_spi_phy *phy) +{ + int i, len = 0; + char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1]; + char fw_ver_block[4]; + struct tpm_tis_data *data = &phy->priv; + + /* + * Write anything to TPM_CR50_FW_VER to start from the beginning + * of the version string + */ + tpm_tis_write8(data, TPM_CR50_FW_VER(data->locality), 0); + + /* Read the string, 4 bytes at a time, until we get '\0' */ + do { + tpm_tis_read_bytes(data, TPM_CR50_FW_VER(data->locality), 4, + fw_ver_block); + for (i = 0; i < 4 && fw_ver_block[i]; ++len, ++i) + fw_ver[len] = fw_ver_block[i]; + } while (i == 4 && len < TPM_CR50_MAX_FW_VER_LEN); + fw_ver[len] = '\0'; + + dev_info(&phy->spi_device->dev, "Cr50 firmware version: %s\n", fw_ver); +} + static int tpm_tis_spi_probe(struct spi_device *dev) { struct tpm_tis_spi_phy *phy; - int irq; + int ret, irq = -1; + struct device_node *np = dev->dev.of_node; + const struct spi_device_id *spi_dev_id = spi_get_device_id(dev); phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), GFP_KERNEL); @@ -208,17 +409,94 @@ static int tpm_tis_spi_probe(struct spi_device *dev) if (!phy->iobuf) return -ENOMEM; - /* If the SPI device has an IRQ then use that */ - if (dev->irq > 0) + phy->is_cr50 = of_device_is_compatible(np, "google,cr50") || + (spi_dev_id && spi_dev_id->driver_data == TIS_IS_CR50); + + if (phy->is_cr50) { + phy->access_delay = CR50_NOIRQ_ACCESS_DELAY; + + mutex_init(&phy->time_track_mutex); + phy->wake_after = jiffies; + phy->last_access = jiffies; + + init_completion(&phy->ready); + if (dev->irq > 0) { + ret = devm_request_irq(&dev->dev, dev->irq, cr50_spi_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "cr50_spi", phy); + if (ret < 0) { + if (ret == -EPROBE_DEFER) + return ret; + dev_warn(&dev->dev, "Requesting IRQ %d failed: %d\n", + dev->irq, ret); + /* + * This is not fatal, the driver will fall back to + * delays automatically, since ready will never + * be completed without a registered irq handler. + * So, just fall through. + */ + } else { + /* + * IRQ requested, let's verify that it is actually + * triggered, before relying on it. + */ + phy->irq_needs_confirmation = true; + } + } else { + dev_warn(&dev->dev, + "No IRQ - will use delays between transactions.\n"); + } + + phy->priv.rng_quality = rng_quality; + } else if (dev->irq > 0) { + /* If the SPI device has an IRQ then use that */ irq = dev->irq; - else - irq = -1; + } - return tpm_tis_core_init(&dev->dev, &phy->priv, irq, &tpm_spi_phy_ops, + ret = tpm_tis_core_init(&dev->dev, &phy->priv, irq, &tpm_spi_phy_ops, NULL); + + if (!ret && phy->is_cr50) + cr50_print_fw_version(phy); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int tpm_tis_spi_pm_suspend(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_data *data = dev_get_drvdata(&chip->dev); + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + + if (phy->is_cr50) + return cr50_suspend(dev); + + return tpm_pm_suspend(dev); +} + +static int tpm_tis_spi_pm_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_data *data = dev_get_drvdata(&chip->dev); + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + + if (phy->is_cr50) { + /* + * Jiffies not increased during suspend, so we need to reset + * the time to wake Cr50 after resume. + */ + phy->wake_after = jiffies; + + return cr50_resume(dev); + } + + return tpm_tis_resume(dev); } +#endif -static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); +static SIMPLE_DEV_PM_OPS(tpm_tis_pm, + tpm_tis_spi_pm_suspend, tpm_tis_spi_pm_resume); static int tpm_tis_spi_remove(struct spi_device *dev) { @@ -230,12 +508,14 @@ static int tpm_tis_spi_remove(struct spi_device *dev) } static const struct spi_device_id tpm_tis_spi_id[] = { + {"cr50", TIS_IS_CR50}, {"tpm_tis_spi", 0}, {} }; MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); static const struct of_device_id of_tis_spi_match[] = { + { .compatible = "google,cr50", }, { .compatible = "st,st33htpm-spi", }, { .compatible = "infineon,slb9670", }, { .compatible = "tcg,tpm_tis-spi", },
Quoting Stephen Boyd (2019-07-17 14:38:36) > @@ -131,6 +303,8 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, > > exit: > spi_bus_unlock(phy->spi_device->master); > + phy->last_access = jiffies; > + mutex_lock(&phy->time_track_mutex); This should be mutex_unlock().
On 17.07.2019 21:57, Stephen Boyd wrote: > Quoting Alexander Steffen (2019-07-17 05:00:06) >> On 17.07.2019 00:45, Stephen Boyd wrote: >>> From: Andrey Pronin <apronin@chromium.org> >>> >>> +static unsigned short rng_quality = 1022; >>> +module_param(rng_quality, ushort, 0644); >>> +MODULE_PARM_DESC(rng_quality, >>> + "Estimation of true entropy, in bits per 1024 bits."); >> >> What is the purpose of this parameter? None of the other TPM drivers >> have it. > > I think the idea is to let users override the quality if they decide > that they don't want to use the default value specified in the driver. But isn't this something that applies to all TPMs, not only cr50? So shouldn't this parameter be added to one of the global modules (tpm? tpm_tis_core?) instead? Or do all low-level drivers (tpm_tis, tpm_tis_spi, ...) need this parameter to provide a consistent interface for the user? >> This copies a lot of code from tpm_tis_spi, but then slightly changes >> some things, without really explaining why. > > The commit text has some explanations. Here's the copy/paste from above: > >>> - need to ensure a certain delay between spi transactions, or else >>> the chip may miss some part of the next transaction; >>> - if there is no spi activity for some time, it may go to sleep, >>> and needs to be waken up before sending further commands; >>> - access to vendor-specific registers. > > Do you want me to describe something further? > >> For example, struct >> cr50_spi_phy contains both tx_buf and rx_buf, whereas tpm_tis_spi uses a >> single iobuf, that is allocated via devm_kmalloc instead of being part >> of the struct. Maybe the difference matters, maybe not, who knows? > > Ok. Are you asking if this is a full-duplex SPI device? No, this was meant as an example for the previous question. As far as I understood it, cr50 is basically compliant to the spec implemented by tpm_tis_spi, but needs special handling in some cases. Therefore, I'd expect a driver for cr50 to look exactly like tpm_tis_spi except for the special bits here and there. The way buffers are allocated within the driver is probably not something that should differ because of the TPM chip. Alexander
On 17.07.2019 23:38, Stephen Boyd wrote: > Quoting Stephen Boyd (2019-07-17 12:57:34) >> Quoting Alexander Steffen (2019-07-17 05:00:06) >>> >>> Can't the code be shared more explicitly, e.g. by cr50_spi wrapping >>> tpm_tis_spi, so that it can intercept the calls, execute the additional >>> actions (like waking up the device), but then let tpm_tis_spi do the >>> common work? >>> >> >> I suppose the read{16,32} and write32 functions could be reused. I'm not >> sure how great it will be if we combine these two drivers, but I can >> give it a try today and see how it looks. >> > > Here's the patch. I haven't tested it besides compile testing. Thanks for providing this. Makes it much easier to see what the actual differences between the devices are. Do we have a general policy on how to support devices that are very similar but need special handling in some places? Not duplicating the whole driver just to change a few things definitely seems like an improvement (and has already been done in the past, as with TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to tpm_tis_spi.c? Or is there some way to keep a clearer separation, especially when (in the future) we have multiple devices that all have their own set of deviations from the spec? Alexander
Quoting Alexander Steffen (2019-07-18 09:47:14) > On 17.07.2019 21:57, Stephen Boyd wrote: > > > > I think the idea is to let users override the quality if they decide > > that they don't want to use the default value specified in the driver. > > But isn't this something that applies to all TPMs, not only cr50? So > shouldn't this parameter be added to one of the global modules (tpm? > tpm_tis_core?) instead? Or do all low-level drivers (tpm_tis, > tpm_tis_spi, ...) need this parameter to provide a consistent interface > for the user? Looking at commit 7a64c5597aa4 ("tpm: Allow tpm_tis drivers to set hwrng quality.") I think all low-level drivers need to set the hwrng quality somehow. I'm not sure how tpm_tis_spi will do that in general, but at least for cr50 we have derived this quality number. I can move this module parameter to tpm_tis_core.c, but then it will be a global hwrng quality override for whatever tpm is registered through tpm_tis_core instead of per-tpm driver. This is sort of a problem right now too if we have two tpm_tis_spi devices. I can drop this parameter if you want. > > > > > Do you want me to describe something further? > > > >> For example, struct > >> cr50_spi_phy contains both tx_buf and rx_buf, whereas tpm_tis_spi uses a > >> single iobuf, that is allocated via devm_kmalloc instead of being part > >> of the struct. Maybe the difference matters, maybe not, who knows? > > > > Ok. Are you asking if this is a full-duplex SPI device? > > No, this was meant as an example for the previous question. As far as I > understood it, cr50 is basically compliant to the spec implemented by > tpm_tis_spi, but needs special handling in some cases. Therefore, I'd > expect a driver for cr50 to look exactly like tpm_tis_spi except for the > special bits here and there. The way buffers are allocated within the > driver is probably not something that should differ because of the TPM chip. > Ok.
Quoting Alexander Steffen (2019-07-18 09:47:22) > On 17.07.2019 23:38, Stephen Boyd wrote: > > Quoting Stephen Boyd (2019-07-17 12:57:34) > >> Quoting Alexander Steffen (2019-07-17 05:00:06) > >>> > >>> Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > >>> tpm_tis_spi, so that it can intercept the calls, execute the additional > >>> actions (like waking up the device), but then let tpm_tis_spi do the > >>> common work? > >>> > >> > >> I suppose the read{16,32} and write32 functions could be reused. I'm not > >> sure how great it will be if we combine these two drivers, but I can > >> give it a try today and see how it looks. > >> > > > > Here's the patch. I haven't tested it besides compile testing. The code seems to work but I haven't done any extensive testing besides making sure that the TPM responds to pcr reads and some commands like reading random numbers. > > Thanks for providing this. Makes it much easier to see what the actual > differences between the devices are. > > Do we have a general policy on how to support devices that are very > similar but need special handling in some places? Not duplicating the > whole driver just to change a few things definitely seems like an > improvement (and has already been done in the past, as with > TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to > tpm_tis_spi.c? Or is there some way to keep a clearer separation, > especially when (in the future) we have multiple devices that all have > their own set of deviations from the spec? > If you have any ideas on how to do it please let me know. At this point, I'd prefer if the maintainers could provide direction on what they want.
On 18.07.2019 20:07, Stephen Boyd wrote: > Quoting Alexander Steffen (2019-07-18 09:47:14) >> On 17.07.2019 21:57, Stephen Boyd wrote: >>> >>> I think the idea is to let users override the quality if they decide >>> that they don't want to use the default value specified in the driver. >> >> But isn't this something that applies to all TPMs, not only cr50? So >> shouldn't this parameter be added to one of the global modules (tpm? >> tpm_tis_core?) instead? Or do all low-level drivers (tpm_tis, >> tpm_tis_spi, ...) need this parameter to provide a consistent interface >> for the user? > > Looking at commit 7a64c5597aa4 ("tpm: Allow tpm_tis drivers to set hwrng > quality.") I think all low-level drivers need to set the hwrng quality > somehow. I'm not sure how tpm_tis_spi will do that in general, but at > least for cr50 we have derived this quality number. > > I can move this module parameter to tpm_tis_core.c, but then it will be > a global hwrng quality override for whatever tpm is registered through > tpm_tis_core instead of per-tpm driver. This is sort of a problem right > now too if we have two tpm_tis_spi devices. I can drop this parameter if > you want. Since it does not seem like a critical feature, maybe just drop it for now. Then we can figure out a way to do it properly and add it later. Alexander
On 18.07.2019 20:11, Stephen Boyd wrote: > Quoting Alexander Steffen (2019-07-18 09:47:22) >> On 17.07.2019 23:38, Stephen Boyd wrote: >>> Quoting Stephen Boyd (2019-07-17 12:57:34) >>>> Quoting Alexander Steffen (2019-07-17 05:00:06) >>>>> >>>>> Can't the code be shared more explicitly, e.g. by cr50_spi wrapping >>>>> tpm_tis_spi, so that it can intercept the calls, execute the additional >>>>> actions (like waking up the device), but then let tpm_tis_spi do the >>>>> common work? >>>>> >>>> >>>> I suppose the read{16,32} and write32 functions could be reused. I'm not >>>> sure how great it will be if we combine these two drivers, but I can >>>> give it a try today and see how it looks. >>>> >>> >>> Here's the patch. I haven't tested it besides compile testing. > > The code seems to work but I haven't done any extensive testing besides > making sure that the TPM responds to pcr reads and some commands like > reading random numbers. > >> >> Thanks for providing this. Makes it much easier to see what the actual >> differences between the devices are. >> >> Do we have a general policy on how to support devices that are very >> similar but need special handling in some places? Not duplicating the >> whole driver just to change a few things definitely seems like an >> improvement (and has already been done in the past, as with >> TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to >> tpm_tis_spi.c? Or is there some way to keep a clearer separation, >> especially when (in the future) we have multiple devices that all have >> their own set of deviations from the spec? >> > > If you have any ideas on how to do it please let me know. At this point, > I'd prefer if the maintainers could provide direction on what they want. Sure, I'd expect Jarkko will say something once he's back from vacation. Alexander
Quoting Alexander Steffen (2019-07-19 00:53:00) > On 18.07.2019 20:11, Stephen Boyd wrote: > > Quoting Alexander Steffen (2019-07-18 09:47:22) > >> On 17.07.2019 23:38, Stephen Boyd wrote: > >>> Quoting Stephen Boyd (2019-07-17 12:57:34) > >>>> Quoting Alexander Steffen (2019-07-17 05:00:06) > >>>>> > >>>>> Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > >>>>> tpm_tis_spi, so that it can intercept the calls, execute the additional > >>>>> actions (like waking up the device), but then let tpm_tis_spi do the > >>>>> common work? > >>>>> > >>>> > >>>> I suppose the read{16,32} and write32 functions could be reused. I'm not > >>>> sure how great it will be if we combine these two drivers, but I can > >>>> give it a try today and see how it looks. > >>>> > >>> > >>> Here's the patch. I haven't tested it besides compile testing. > > > > The code seems to work but I haven't done any extensive testing besides > > making sure that the TPM responds to pcr reads and some commands like > > reading random numbers. > > > >> > >> Thanks for providing this. Makes it much easier to see what the actual > >> differences between the devices are. > >> > >> Do we have a general policy on how to support devices that are very > >> similar but need special handling in some places? Not duplicating the > >> whole driver just to change a few things definitely seems like an > >> improvement (and has already been done in the past, as with > >> TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to > >> tpm_tis_spi.c? Or is there some way to keep a clearer separation, > >> especially when (in the future) we have multiple devices that all have > >> their own set of deviations from the spec? > >> > > > > If you have any ideas on how to do it please let me know. At this point, > > I'd prefer if the maintainers could provide direction on what they want. > > Sure, I'd expect Jarkko will say something once he's back from vacation. > Should I just resend this patch series? I haven't attempted to make the i2c driver changes, but at least the SPI driver changes seem good enough to resend.
On Thu, 2019-08-01 at 09:02 -0700, Stephen Boyd wrote: > Quoting Alexander Steffen (2019-07-19 00:53:00) > > On 18.07.2019 20:11, Stephen Boyd wrote: > > > Quoting Alexander Steffen (2019-07-18 09:47:22) > > > > On 17.07.2019 23:38, Stephen Boyd wrote: > > > > > Quoting Stephen Boyd (2019-07-17 12:57:34) > > > > > > Quoting Alexander Steffen (2019-07-17 05:00:06) > > > > > > > Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > > > > > > > tpm_tis_spi, so that it can intercept the calls, execute the additional > > > > > > > actions (like waking up the device), but then let tpm_tis_spi do the > > > > > > > common work? > > > > > > > > > > > > > > > > > > > I suppose the read{16,32} and write32 functions could be reused. I'm not > > > > > > sure how great it will be if we combine these two drivers, but I can > > > > > > give it a try today and see how it looks. > > > > > > > > > > > > > > > > Here's the patch. I haven't tested it besides compile testing. > > > > > > The code seems to work but I haven't done any extensive testing besides > > > making sure that the TPM responds to pcr reads and some commands like > > > reading random numbers. > > > > > > > Thanks for providing this. Makes it much easier to see what the actual > > > > differences between the devices are. > > > > > > > > Do we have a general policy on how to support devices that are very > > > > similar but need special handling in some places? Not duplicating the > > > > whole driver just to change a few things definitely seems like an > > > > improvement (and has already been done in the past, as with > > > > TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to > > > > tpm_tis_spi.c? Or is there some way to keep a clearer separation, > > > > especially when (in the future) we have multiple devices that all have > > > > their own set of deviations from the spec? > > > > > > > > > > If you have any ideas on how to do it please let me know. At this point, > > > I'd prefer if the maintainers could provide direction on what they want. > > > > Sure, I'd expect Jarkko will say something once he's back from vacation. > > > > Should I just resend this patch series? I haven't attempted to make the > i2c driver changes, but at least the SPI driver changes seem good enough > to resend. Hi, I'm back. If there are already like obvious changes, please send an update and I'll take a look at that. /Jarkko
Quoting Jarkko Sakkinen (2019-08-02 08:21:06) > On Thu, 2019-08-01 at 09:02 -0700, Stephen Boyd wrote: > > Quoting Alexander Steffen (2019-07-19 00:53:00) > > > On 18.07.2019 20:11, Stephen Boyd wrote: > > > > Quoting Alexander Steffen (2019-07-18 09:47:22) > > > > > On 17.07.2019 23:38, Stephen Boyd wrote: > > > > > > Quoting Stephen Boyd (2019-07-17 12:57:34) > > > > > > > Quoting Alexander Steffen (2019-07-17 05:00:06) > > > > > > > > Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > > > > > > > > tpm_tis_spi, so that it can intercept the calls, execute the additional > > > > > > > > actions (like waking up the device), but then let tpm_tis_spi do the > > > > > > > > common work? > > > > > > > > > > > > > > > > > > > > > > I suppose the read{16,32} and write32 functions could be reused. I'm not > > > > > > > sure how great it will be if we combine these two drivers, but I can > > > > > > > give it a try today and see how it looks. > > > > > > > > > > > > > > > > > > > Here's the patch. I haven't tested it besides compile testing. > > > > > > > > The code seems to work but I haven't done any extensive testing besides > > > > making sure that the TPM responds to pcr reads and some commands like > > > > reading random numbers. > > > > > > > > > Thanks for providing this. Makes it much easier to see what the actual > > > > > differences between the devices are. > > > > > > > > > > Do we have a general policy on how to support devices that are very > > > > > similar but need special handling in some places? Not duplicating the > > > > > whole driver just to change a few things definitely seems like an > > > > > improvement (and has already been done in the past, as with > > > > > TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to > > > > > tpm_tis_spi.c? Or is there some way to keep a clearer separation, > > > > > especially when (in the future) we have multiple devices that all have > > > > > their own set of deviations from the spec? > > > > > > > > > > > > > If you have any ideas on how to do it please let me know. At this point, > > > > I'd prefer if the maintainers could provide direction on what they want. > > > > > > Sure, I'd expect Jarkko will say something once he's back from vacation. > > > > > > > Should I just resend this patch series? I haven't attempted to make the > > i2c driver changes, but at least the SPI driver changes seem good enough > > to resend. > > Hi, I'm back. If there are already like obvious changes, please send an > update and I'll take a look at that. > Ok. Will do today. Thanks.
On Thu, Aug 01, 2019 at 09:02:02AM -0700, Stephen Boyd wrote: > Quoting Alexander Steffen (2019-07-19 00:53:00) > > On 18.07.2019 20:11, Stephen Boyd wrote: > > > Quoting Alexander Steffen (2019-07-18 09:47:22) > > >> On 17.07.2019 23:38, Stephen Boyd wrote: > > >>> Quoting Stephen Boyd (2019-07-17 12:57:34) > > >>>> Quoting Alexander Steffen (2019-07-17 05:00:06) > > >>>>> > > >>>>> Can't the code be shared more explicitly, e.g. by cr50_spi wrapping > > >>>>> tpm_tis_spi, so that it can intercept the calls, execute the additional > > >>>>> actions (like waking up the device), but then let tpm_tis_spi do the > > >>>>> common work? > > >>>>> > > >>>> > > >>>> I suppose the read{16,32} and write32 functions could be reused. I'm not > > >>>> sure how great it will be if we combine these two drivers, but I can > > >>>> give it a try today and see how it looks. > > >>>> > > >>> > > >>> Here's the patch. I haven't tested it besides compile testing. > > > > > > The code seems to work but I haven't done any extensive testing besides > > > making sure that the TPM responds to pcr reads and some commands like > > > reading random numbers. > > > > > >> > > >> Thanks for providing this. Makes it much easier to see what the actual > > >> differences between the devices are. > > >> > > >> Do we have a general policy on how to support devices that are very > > >> similar but need special handling in some places? Not duplicating the > > >> whole driver just to change a few things definitely seems like an > > >> improvement (and has already been done in the past, as with > > >> TPM_TIS_ITPM_WORKAROUND). But should all the code just be added to > > >> tpm_tis_spi.c? Or is there some way to keep a clearer separation, > > >> especially when (in the future) we have multiple devices that all have > > >> their own set of deviations from the spec? > > >> > > > > > > If you have any ideas on how to do it please let me know. At this point, > > > I'd prefer if the maintainers could provide direction on what they want. > > > > Sure, I'd expect Jarkko will say something once he's back from vacation. > > > > Should I just resend this patch series? I haven't attempted to make the > i2c driver changes, but at least the SPI driver changes seem good enough > to resend. Go ahead. /Jarkko
On Wed, Jul 17, 2019 at 02:00:06PM +0200, Alexander Steffen wrote: > Is it a common pattern to add config options that are not useful on their > own? When would I ever enable TCG_CR50 without also enabling TCG_CR50_SPI? > Why can't you just use TCG_CR50_SPI for everything? Agreed. If the kernel only has the spi driver, we only want option for that. /Jarkko
On Thu, Jul 18, 2019 at 06:47:14PM +0200, Alexander Steffen wrote: > On 17.07.2019 21:57, Stephen Boyd wrote: > > Quoting Alexander Steffen (2019-07-17 05:00:06) > > > On 17.07.2019 00:45, Stephen Boyd wrote: > > > > From: Andrey Pronin <apronin@chromium.org> > > > > > > > > +static unsigned short rng_quality = 1022; > > > > +module_param(rng_quality, ushort, 0644); > > > > +MODULE_PARM_DESC(rng_quality, > > > > + "Estimation of true entropy, in bits per 1024 bits."); > > > > > > What is the purpose of this parameter? None of the other TPM drivers > > > have it. > > > > I think the idea is to let users override the quality if they decide > > that they don't want to use the default value specified in the driver. > > But isn't this something that applies to all TPMs, not only cr50? So > shouldn't this parameter be added to one of the global modules (tpm? > tpm_tis_core?) instead? Or do all low-level drivers (tpm_tis, tpm_tis_spi, > ...) need this parameter to provide a consistent interface for the user? This definitely something that is out of context of the patch set and thus must be removed from the patch set. /Jarkko
Quoting Jarkko Sakkinen (2019-08-02 13:43:18) > On Thu, Jul 18, 2019 at 06:47:14PM +0200, Alexander Steffen wrote: > > On 17.07.2019 21:57, Stephen Boyd wrote: > > > Quoting Alexander Steffen (2019-07-17 05:00:06) > > > > On 17.07.2019 00:45, Stephen Boyd wrote: > > > > > From: Andrey Pronin <apronin@chromium.org> > > > > > > > > > > +static unsigned short rng_quality = 1022; > > > > > +module_param(rng_quality, ushort, 0644); > > > > > +MODULE_PARM_DESC(rng_quality, > > > > > + "Estimation of true entropy, in bits per 1024 bits."); > > > > > > > > What is the purpose of this parameter? None of the other TPM drivers > > > > have it. > > > > > > I think the idea is to let users override the quality if they decide > > > that they don't want to use the default value specified in the driver. > > > > But isn't this something that applies to all TPMs, not only cr50? So > > shouldn't this parameter be added to one of the global modules (tpm? > > tpm_tis_core?) instead? Or do all low-level drivers (tpm_tis, tpm_tis_spi, > > ...) need this parameter to provide a consistent interface for the user? > > This definitely something that is out of context of the patch set and > thus must be removed from the patch set. Ok. I've dropped this part of the patch.
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 88a3c06fc153..b7028bfa6f87 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -114,6 +114,22 @@ config TCG_ATMEL will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_atmel. +config TCG_CR50 + bool + ---help--- + Common routines shared by drivers for Cr50-based devices. + +config TCG_CR50_SPI + tristate "Cr50 SPI Interface" + depends on SPI + select TCG_TIS_CORE + select TCG_CR50 + ---help--- + If you have a H1 secure module running Cr50 firmware on SPI bus, + say Yes and it will be accessible from within Linux. To compile + this driver as a module, choose M here; the module will be called + cr50_spi. + config TCG_INFINEON tristate "Infineon Technologies TPM Interface" depends on PNP diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index a01c4cab902a..4e89538c73c8 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -27,6 +27,8 @@ obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o +obj-$(CONFIG_TCG_CR50) += cr50.o +obj-$(CONFIG_TCG_CR50_SPI) += cr50_spi.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ diff --git a/drivers/char/tpm/cr50.c b/drivers/char/tpm/cr50.c new file mode 100644 index 000000000000..8c83ec25f8e1 --- /dev/null +++ b/drivers/char/tpm/cr50.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2016 Google Inc. + */ + +/* + * This file contains common code for devices with Cr50 firmware. + */ + +#include <linux/export.h> +#include <linux/suspend.h> +#include "cr50.h" +#include "tpm.h" + +#ifdef CONFIG_PM_SLEEP +int cr50_resume(struct device *dev) +{ + if (pm_suspend_via_firmware()) + return tpm_pm_resume(dev); + + return 0; +} +EXPORT_SYMBOL(cr50_resume); + +int cr50_suspend(struct device *dev) +{ + if (pm_suspend_via_firmware()) + return tpm_pm_suspend(dev); + + return 0; +} +EXPORT_SYMBOL(cr50_suspend); +#endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/char/tpm/cr50.h b/drivers/char/tpm/cr50.h new file mode 100644 index 000000000000..1c635fae03e8 --- /dev/null +++ b/drivers/char/tpm/cr50.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2016 Google Inc. + * This file contains common code for devices with Cr50 firmware. + */ + +#ifndef __CR50_H__ +#define __CR50_H__ + +struct device; + +int cr50_resume(struct device *dev); +int cr50_suspend(struct device *dev); + +#endif /* __CR50_H__ */ diff --git a/drivers/char/tpm/cr50_spi.c b/drivers/char/tpm/cr50_spi.c new file mode 100644 index 000000000000..3c1b472297ad --- /dev/null +++ b/drivers/char/tpm/cr50_spi.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Google, Inc + * + * This device driver implements a TCG PTP FIFO interface over SPI for chips + * with Cr50 firmware. + * It is based on tpm_tis_spi driver by Peter Huewe and Christophe Ricard. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> +#include "cr50.h" +#include "tpm_tis_core.h" + +/* + * Cr50 timing constants: + * - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC. + * - needs up to CR50_WAKE_START_DELAY_MSEC to wake after sleep. + * - requires waiting for "ready" IRQ, if supported; or waiting for at least + * CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported. + * - waits for up to CR50_FLOW_CONTROL_MSEC for flow control 'ready' indication. + */ +#define CR50_SLEEP_DELAY_MSEC 1000 +#define CR50_WAKE_START_DELAY_MSEC 1 +#define CR50_NOIRQ_ACCESS_DELAY_MSEC 2 +#define CR50_READY_IRQ_TIMEOUT_MSEC TPM2_TIMEOUT_A +#define CR50_FLOW_CONTROL_MSEC TPM2_TIMEOUT_A +#define MAX_IRQ_CONFIRMATION_ATTEMPTS 3 + +#define MAX_SPI_FRAMESIZE 64 + +#define TPM_CR50_FW_VER(l) (0x0F90 | ((l) << 12)) +#define TPM_CR50_MAX_FW_VER_LEN 64 + +static unsigned short rng_quality = 1022; +module_param(rng_quality, ushort, 0644); +MODULE_PARM_DESC(rng_quality, + "Estimation of true entropy, in bits per 1024 bits."); + +struct cr50_spi_phy { + struct tpm_tis_data priv; + struct spi_device *spi_device; + + struct mutex time_track_mutex; + unsigned long last_access_jiffies; + unsigned long wake_after_jiffies; + + unsigned long access_delay_jiffies; + unsigned long sleep_delay_jiffies; + unsigned int wake_start_delay_msec; + + struct completion tpm_ready; + + unsigned int irq_confirmation_attempt; + bool irq_needs_confirmation; + bool irq_confirmed; + + u8 tx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned; + u8 rx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned; +}; + +static struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_data *data) +{ + return container_of(data, struct cr50_spi_phy, priv); +} + +/* + * The cr50 interrupt handler just signals waiting threads that the + * interrupt was asserted. It does not do any processing triggered + * by interrupts but is instead used to avoid fixed delays. + */ +static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id) +{ + struct cr50_spi_phy *phy = dev_id; + + phy->irq_confirmed = true; + complete(&phy->tpm_ready); + + return IRQ_HANDLED; +} + +/* + * Cr50 needs to have at least some delay between consecutive + * transactions. Make sure we wait. + */ +static void cr50_ensure_access_delay(struct cr50_spi_phy *phy) +{ + /* + * Note: There is a small chance, if Cr50 is not accessed in a few days, + * that time_in_range will not provide the correct result after the wrap + * around for jiffies. In this case, we'll have an unneeded short delay, + * which is fine. + */ + unsigned long allowed_access = + phy->last_access_jiffies + phy->access_delay_jiffies; + unsigned long time_now = jiffies; + + if (time_in_range_open(time_now, + phy->last_access_jiffies, allowed_access)) { + unsigned long remaining = + wait_for_completion_timeout(&phy->tpm_ready, + allowed_access - time_now); + if (remaining == 0 && phy->irq_confirmed) { + dev_warn(&phy->spi_device->dev, + "Timeout waiting for TPM ready IRQ\n"); + } + } + if (phy->irq_needs_confirmation) { + if (phy->irq_confirmed) { + phy->irq_needs_confirmation = false; + phy->access_delay_jiffies = + msecs_to_jiffies(CR50_READY_IRQ_TIMEOUT_MSEC); + dev_info(&phy->spi_device->dev, + "TPM ready IRQ confirmed on attempt %u\n", + phy->irq_confirmation_attempt); + } else if (++phy->irq_confirmation_attempt > + MAX_IRQ_CONFIRMATION_ATTEMPTS) { + phy->irq_needs_confirmation = false; + dev_warn(&phy->spi_device->dev, + "IRQ not confirmed - will use delays\n"); + } + } +} + +/* + * Cr50 might go to sleep if there is no SPI activity for some time and + * miss the first few bits/bytes on the bus. In such case, wake it up + * by asserting CS and give it time to start up. + */ +static bool cr50_needs_waking(struct cr50_spi_phy *phy) +{ + /* + * Note: There is a small chance, if Cr50 is not accessed in a few days, + * that time_in_range will not provide the correct result after the wrap + * around for jiffies. In this case, we'll probably timeout or read + * incorrect value from TPM_STS and just retry the operation. + */ + return !time_in_range_open(jiffies, + phy->last_access_jiffies, + phy->wake_after_jiffies); +} + +static void cr50_wake_if_needed(struct cr50_spi_phy *phy) +{ + if (cr50_needs_waking(phy)) { + /* Assert CS, wait 1 msec, deassert CS */ + struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 }; + + spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1); + /* Wait for it to fully wake */ + msleep(phy->wake_start_delay_msec); + } + /* Reset the time when we need to wake Cr50 again */ + phy->wake_after_jiffies = jiffies + phy->sleep_delay_jiffies; +} + +/* + * Flow control: clock the bus and wait for cr50 to set LSB before + * sending/receiving data. TCG PTP spec allows it to happen during + * the last byte of header, but cr50 never does that in practice, + * and earlier versions had a bug when it was set too early, so don't + * check for it during header transfer. + */ +static int cr50_spi_flow_control(struct cr50_spi_phy *phy) +{ + unsigned long timeout_jiffies = + jiffies + msecs_to_jiffies(CR50_FLOW_CONTROL_MSEC); + struct spi_message m; + int ret; + struct spi_transfer spi_xfer = { + .rx_buf = phy->rx_buf, + .len = 1, + .cs_change = 1, + }; + + do { + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + return ret; + if (time_after(jiffies, timeout_jiffies)) { + dev_warn(&phy->spi_device->dev, + "Timeout during flow control\n"); + return -EBUSY; + } + } while (!(phy->rx_buf[0] & 0x01)); + return 0; +} + +static int cr50_spi_xfer_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, const u8 *tx, u8 *rx) +{ + struct cr50_spi_phy *phy = to_cr50_spi_phy(data); + struct spi_message m; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + .len = 4, + .cs_change = 1, + }; + int ret; + + if (len > MAX_SPI_FRAMESIZE) + return -EINVAL; + + /* + * Do this outside of spi_bus_lock in case cr50 is not the + * only device on that spi bus. + */ + mutex_lock(&phy->time_track_mutex); + cr50_ensure_access_delay(phy); + cr50_wake_if_needed(phy); + + phy->tx_buf[0] = (tx ? 0x00 : 0x80) | (len - 1); + phy->tx_buf[1] = 0xD4; + phy->tx_buf[2] = (addr >> 8) & 0xFF; + phy->tx_buf[3] = addr & 0xFF; + + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + + spi_bus_lock(phy->spi_device->master); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + goto exit; + + ret = cr50_spi_flow_control(phy); + if (ret < 0) + goto exit; + + spi_xfer.cs_change = 0; + spi_xfer.len = len; + if (tx) { + memcpy(phy->tx_buf, tx, len); + spi_xfer.rx_buf = NULL; + } else { + spi_xfer.tx_buf = NULL; + } + + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + reinit_completion(&phy->tpm_ready); + ret = spi_sync_locked(phy->spi_device, &m); + if (rx) + memcpy(rx, phy->rx_buf, len); + +exit: + spi_bus_unlock(phy->spi_device->master); + phy->last_access_jiffies = jiffies; + mutex_unlock(&phy->time_track_mutex); + + return ret; +} + +static int cr50_spi_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + return cr50_spi_xfer_bytes(data, addr, len, NULL, result); +} + +static int cr50_spi_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, const u8 *value) +{ + return cr50_spi_xfer_bytes(data, addr, len, value, NULL); +} + +static int cr50_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) +{ + int rc; + __le16 le_val; + + rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)&le_val); + if (!rc) + *result = le16_to_cpu(le_val); + return rc; +} + +static int cr50_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) +{ + int rc; + __le32 le_val; + + rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)&le_val); + if (!rc) + *result = le32_to_cpu(le_val); + return rc; +} + +static int cr50_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) +{ + __le32 le_val = cpu_to_le32(value); + + return data->phy_ops->write_bytes(data, addr, sizeof(u32), + (u8 *)&le_val); +} + +static void cr50_get_fw_version(struct tpm_tis_data *data, char *fw_ver) +{ + int i, len = 0; + char fw_ver_block[4]; + + /* + * Write anything to TPM_CR50_FW_VER to start from the beginning + * of the version string + */ + tpm_tis_write8(data, TPM_CR50_FW_VER(data->locality), 0); + + /* Read the string, 4 bytes at a time, until we get '\0' */ + do { + tpm_tis_read_bytes(data, TPM_CR50_FW_VER(data->locality), 4, + fw_ver_block); + for (i = 0; i < 4 && fw_ver_block[i]; ++len, ++i) + fw_ver[len] = fw_ver_block[i]; + } while (i == 4 && len < TPM_CR50_MAX_FW_VER_LEN); + fw_ver[len] = 0; +} + +static const struct tpm_tis_phy_ops cr50_spi_phy_ops = { + .read_bytes = cr50_spi_read_bytes, + .write_bytes = cr50_spi_write_bytes, + .read16 = cr50_spi_read16, + .read32 = cr50_spi_read32, + .write32 = cr50_spi_write32, + .max_xfer_size = MAX_SPI_FRAMESIZE, +}; + +static int cr50_spi_probe(struct spi_device *dev) +{ + char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1]; + struct cr50_spi_phy *phy; + int rc; + + phy = devm_kzalloc(&dev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->spi_device = dev; + + phy->access_delay_jiffies = + msecs_to_jiffies(CR50_NOIRQ_ACCESS_DELAY_MSEC); + phy->sleep_delay_jiffies = msecs_to_jiffies(CR50_SLEEP_DELAY_MSEC); + phy->wake_start_delay_msec = CR50_WAKE_START_DELAY_MSEC; + + mutex_init(&phy->time_track_mutex); + phy->wake_after_jiffies = jiffies; + phy->last_access_jiffies = jiffies; + + init_completion(&phy->tpm_ready); + if (dev->irq > 0) { + rc = devm_request_irq(&dev->dev, dev->irq, cr50_spi_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "cr50_spi", phy); + if (rc < 0) { + if (rc == -EPROBE_DEFER) + return rc; + dev_warn(&dev->dev, "Requesting IRQ %d failed: %d\n", + dev->irq, rc); + /* + * This is not fatal, the driver will fall back to + * delays automatically, since tpm_ready will never + * be completed without a registered irq handler. + * So, just fall through. + */ + } else { + /* + * IRQ requested, let's verify that it is actually + * triggered, before relying on it. + */ + phy->irq_needs_confirmation = true; + } + } else { + dev_warn(&dev->dev, + "No IRQ - will use delays between transactions.\n"); + } + + phy->priv.rng_quality = rng_quality; + + rc = tpm_tis_core_init(&dev->dev, &phy->priv, -1, &cr50_spi_phy_ops, + NULL); + if (rc < 0) + return rc; + + cr50_get_fw_version(&phy->priv, fw_ver); + dev_info(&dev->dev, "Cr50 firmware version: %s\n", fw_ver); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cr50_spi_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_data *data = dev_get_drvdata(&chip->dev); + struct cr50_spi_phy *phy = to_cr50_spi_phy(data); + + /* + * Jiffies not increased during suspend, so we need to reset + * the time to wake Cr50 after resume. + */ + phy->wake_after_jiffies = jiffies; + + return cr50_resume(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cr50_spi_pm, cr50_suspend, cr50_spi_resume); + +static int cr50_spi_remove(struct spi_device *dev) +{ + struct tpm_chip *chip = spi_get_drvdata(dev); + + tpm_chip_unregister(chip); + tpm_tis_remove(chip); + return 0; +} + +static const struct spi_device_id cr50_spi_id[] = { + { "cr50", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, cr50_spi_id); + +#ifdef CONFIG_OF +static const struct of_device_id of_cr50_spi_match[] = { + { .compatible = "google,cr50", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_cr50_spi_match); +#endif + +static struct spi_driver cr50_spi_driver = { + .driver = { + .name = "cr50_spi", + .pm = &cr50_spi_pm, + .of_match_table = of_match_ptr(of_cr50_spi_match), + }, + .probe = cr50_spi_probe, + .remove = cr50_spi_remove, + .id_table = cr50_spi_id, +}; +module_spi_driver(cr50_spi_driver); + +MODULE_DESCRIPTION("Cr50 TCG PTP FIFO SPI driver"); +MODULE_LICENSE("GPL v2");