From patchwork Tue Aug 6 22:07:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Boyd X-Patchwork-Id: 11079655 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 36C9D1395 for ; Tue, 6 Aug 2019 22:08:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 259B7287DE for ; Tue, 6 Aug 2019 22:08:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1A0BD288D7; Tue, 6 Aug 2019 22:08:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DFD13287DE for ; Tue, 6 Aug 2019 22:08:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726716AbfHFWHx (ORCPT ); Tue, 6 Aug 2019 18:07:53 -0400 Received: from mail-pf1-f193.google.com ([209.85.210.193]:40957 "EHLO mail-pf1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726934AbfHFWHx (ORCPT ); Tue, 6 Aug 2019 18:07:53 -0400 Received: by mail-pf1-f193.google.com with SMTP id p184so42244351pfp.7 for ; Tue, 06 Aug 2019 15:07:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0yp39iSDiHsvaqQ+V6c31HHn1q9RV6+/SR9JS6bJ76M=; b=kmuoZfqWKtsBp3o3fVOVcDaWAuTSACpIL2N7EmKeieZgU8QkmSIhrTJGg0rinb9r0N E/Yt0MVNCPJUULHHl6++mKQo5yi9zD4YN7/XaMicDiYA7D1ANM3/7PoHvbyQclmerWCI +qyStL2BOUbneAx1vdDaYfQxMnv65Ek33I6s0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0yp39iSDiHsvaqQ+V6c31HHn1q9RV6+/SR9JS6bJ76M=; b=G/A0xUo8kTBoHI71A2KeJ+DQSSsNg8eprQ59AhdAF80V/pNfDgk+Tg4rVqzAiPcx4f LoUAcDjeaDL0Uqva4YGbnqSmcKcHynaGkU+yBhOkrMTz5XA7BAZcm3ezCa6BJrQkoRTF 5J2OJvnHGZ/MEXRruxlnNCYUOQeBAm/3h+zQbch1+zl5aCF+CS0aXz/mNiYsoR/u8Eug KGq2wOq0QAWObxbzfq6y9oiYFIhl8saY15dppT2UXE2DiAXdMUAlBq7dkrQcvLmPwTPS nDMxd3RyBzMXXQFq0vOaITSPE/sH/5H8uxCfqpN5TlkhUZN0OBWMs0x/qjAmzhHTYr3i 6wdQ== X-Gm-Message-State: APjAAAW7jUfeZ+NfSdBuEsHFbEi6vE6Ynk/7GgX1cxyoV8xkC50gSpy1 oK4sKXcKFsacxKmq/FHIJHFJLw== X-Google-Smtp-Source: APXvYqx6lgCKd4PA8HLEjTxGg9c+g7VMEbw+wvSX0EcJ29Ge48iD0QDOl6YFqACwtwH+9oO/uNjTlA== X-Received: by 2002:a17:90a:3548:: with SMTP id q66mr5343352pjb.17.1565129272895; Tue, 06 Aug 2019 15:07:52 -0700 (PDT) Received: from smtp.gmail.com ([2620:15c:202:1:fa53:7765:582b:82b9]) by smtp.gmail.com with ESMTPSA id p7sm98982509pfp.131.2019.08.06.15.07.52 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Tue, 06 Aug 2019 15:07:52 -0700 (PDT) From: Stephen Boyd To: Peter Huewe , Jarkko Sakkinen Cc: linux-kernel@vger.kernel.org, linux-integrity@vger.kernel.org, Andrey Pronin , Duncan Laurie , Jason Gunthorpe , Arnd Bergmann , Greg Kroah-Hartman , Guenter Roeck , Alexander Steffen Subject: [PATCH v3 1/4] tpm: Add a flag to indicate TPM power is managed by firmware Date: Tue, 6 Aug 2019 15:07:47 -0700 Message-Id: <20190806220750.86597-2-swboyd@chromium.org> X-Mailer: git-send-email 2.22.0.770.g0f2c4a37fd-goog In-Reply-To: <20190806220750.86597-1-swboyd@chromium.org> References: <20190806220750.86597-1-swboyd@chromium.org> MIME-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On some platforms, the TPM power is managed by firmware and therefore we don't need to stop the TPM on suspend when going to a light version of suspend such as S0ix ("freeze" suspend state). Add a chip flag to indicate this so that certain platforms can probe for the usage of this light suspend and avoid touching the TPM state across suspend/resume. Cc: Andrey Pronin Cc: Duncan Laurie Cc: Jason Gunthorpe Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Guenter Roeck Cc: Alexander Steffen Signed-off-by: Stephen Boyd --- drivers/char/tpm/tpm-interface.c | 8 +++++++- drivers/char/tpm/tpm.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 1b4f95c13e00..9bf3ddee6c8c 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -395,7 +396,11 @@ int tpm_pm_suspend(struct device *dev) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) - return 0; + goto suspended; + + if (chip->flags & TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED) + if (!pm_suspend_via_firmware()) + goto suspended; if (!tpm_chip_start(chip)) { if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -406,6 +411,7 @@ int tpm_pm_suspend(struct device *dev) tpm_chip_stop(chip); } +suspended: return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index e503ffc3aa39..28f73331aa2e 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -162,6 +162,7 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_VIRTUAL = BIT(3), TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), + TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED = BIT(6), }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) From patchwork Tue Aug 6 22:07:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Boyd X-Patchwork-Id: 11079651 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D297E14E5 for ; Tue, 6 Aug 2019 22:08:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C0F0F287DE for ; Tue, 6 Aug 2019 22:08:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B3A05288D7; Tue, 6 Aug 2019 22:08:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E7B42287DE for ; Tue, 6 Aug 2019 22:08:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727303AbfHFWH4 (ORCPT ); Tue, 6 Aug 2019 18:07:56 -0400 Received: from mail-pl1-f194.google.com ([209.85.214.194]:34977 "EHLO mail-pl1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727128AbfHFWHy (ORCPT ); Tue, 6 Aug 2019 18:07:54 -0400 Received: by mail-pl1-f194.google.com with SMTP id w24so38413081plp.2 for ; Tue, 06 Aug 2019 15:07:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=h/xhP/vB7IkzGf8wgypQiR7wXsPZkOsDnbKr/VVmc1c=; b=TOMfBvy3auNNsbtX7pMhzSFghcR0Y3nkcmgpZPntLPGtn10unKBLqZ3VvVxcesV/Zl 4xnaqpSB9qZYbcAj5XLMSdhF8XTnzxpLMsxth6YYHpCimGRDHSM6/l00/Hrb/qAylbFr J+L5fvDaOzsvGpcr7XL+8iARpP7InreIOJhos= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=h/xhP/vB7IkzGf8wgypQiR7wXsPZkOsDnbKr/VVmc1c=; b=b/W32N+wknkWAh61HJfCMpzBKY8CKdWXdfjuUXRSNgS6ll2Qx1uObW+7yBW7lS5Keq RrNS747hw3rHXZ8iyn2WuGhhYqLdzwo698tkZvOIq66dUDKizGprmg3pSQOe4r6sl1VL NHCK5glRV35FZoGwCFSN6YLvzRLeepFe3AiMamwd6H72Rp0HDMtzB5O+VCYjQdNZ8VLj cAsHNBBK5yNdsKxrVza3TF0hW95fVZqa/1+wmFyrxfxnC7DpQYO/kb1+2BUK6iYvCegS gpfvLGex2Wtn94WS+cxFazvchoaxZL4LBBCN4N3qNyUcEb19Vbi+bTjeSJcTdfxcm8LI dRpQ== X-Gm-Message-State: APjAAAVpkkTy6FJd+UQ8zqfwHilqQSDuCBs4LBvG4JE61sh6ueiFNrFs KgejHZvdr75Fsa2lfF3/nRgLdQ== X-Google-Smtp-Source: APXvYqwwdF1lRp3/AgWvmNMONW0hz7Usq6oiIdN3G6dHCBEdd6jsPmnih7QV1onNwq1d6FKLb0tojA== X-Received: by 2002:a17:902:7083:: with SMTP id z3mr195085plk.87.1565129273802; Tue, 06 Aug 2019 15:07:53 -0700 (PDT) Received: from smtp.gmail.com ([2620:15c:202:1:fa53:7765:582b:82b9]) by smtp.gmail.com with ESMTPSA id p7sm98982509pfp.131.2019.08.06.15.07.52 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Tue, 06 Aug 2019 15:07:53 -0700 (PDT) From: Stephen Boyd To: Peter Huewe , Jarkko Sakkinen Cc: linux-kernel@vger.kernel.org, linux-integrity@vger.kernel.org, Andrey Pronin , Duncan Laurie , Jason Gunthorpe , Arnd Bergmann , Greg Kroah-Hartman , Guenter Roeck , Alexander Steffen Subject: [PATCH v3 2/4] tpm: tpm_tis_spi: Export functionality to other drivers Date: Tue, 6 Aug 2019 15:07:48 -0700 Message-Id: <20190806220750.86597-3-swboyd@chromium.org> X-Mailer: git-send-email 2.22.0.770.g0f2c4a37fd-goog In-Reply-To: <20190806220750.86597-1-swboyd@chromium.org> References: <20190806220750.86597-1-swboyd@chromium.org> MIME-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP We want to use most of the code in this driver, except we want to modify the flow control and idle behavior. Let's "libify" this driver so that another driver can call the code in here and slightly tweak the behavior. Cc: Andrey Pronin Cc: Duncan Laurie Cc: Jason Gunthorpe Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Guenter Roeck Cc: Alexander Steffen Signed-off-by: Stephen Boyd --- drivers/char/tpm/tpm_tis_spi.c | 98 +++++++++++++++++++--------------- drivers/char/tpm/tpm_tis_spi.h | 37 +++++++++++++ 2 files changed, 93 insertions(+), 42 deletions(-) create mode 100644 drivers/char/tpm/tpm_tis_spi.h diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c index 19513e622053..fd3fb4f9f506 100644 --- a/drivers/char/tpm/tpm_tis_spi.c +++ b/drivers/char/tpm/tpm_tis_spi.c @@ -36,26 +36,43 @@ #include #include "tpm.h" #include "tpm_tis_core.h" +#include "tpm_tis_spi.h" #define MAX_SPI_FRAMESIZE 64 -struct tpm_tis_spi_phy { - struct tpm_tis_data priv; - struct spi_device *spi_device; - u8 *iobuf; -}; - -static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) +static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy, + struct spi_transfer *spi_xfer) { - return container_of(data, struct tpm_tis_spi_phy, priv); + struct spi_message m; + int ret, i; + + if ((phy->iobuf[3] & 0x01) == 0) { + // handle SPI wait states + phy->iobuf[0] = 0; + + for (i = 0; i < TPM_RETRY; i++) { + spi_xfer->len = 1; + 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 (phy->iobuf[0] & 0x01) + break; + } + + if (i == TPM_RETRY) + return -ETIMEDOUT; + } + + return 0; } -static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, +int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, u8 *in, const u8 *out) { struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); int ret = 0; - int i; struct spi_message m; struct spi_transfer spi_xfer; u8 transfer_len; @@ -82,26 +99,9 @@ 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) { - // handle SPI wait states - phy->iobuf[0] = 0; - - for (i = 0; i < TPM_RETRY; i++) { - spi_xfer.len = 1; - spi_message_init(&m); - spi_message_add_tail(&spi_xfer, &m); - ret = spi_sync_locked(phy->spi_device, &m); - if (ret < 0) - goto exit; - if (phy->iobuf[0] & 0x01) - break; - } - - if (i == TPM_RETRY) { - ret = -ETIMEDOUT; - goto exit; - } - } + ret = phy->flow_control(phy, &spi_xfer); + if (ret < 0) + goto exit; spi_xfer.cs_change = 0; spi_xfer.len = transfer_len; @@ -117,6 +117,8 @@ 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); + if (phy->pre_transfer) + phy->pre_transfer(phy); ret = spi_sync_locked(phy->spi_device, &m); if (ret < 0) goto exit; @@ -133,9 +135,10 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, spi_bus_unlock(phy->spi_device->master); return ret; } +EXPORT_SYMBOL_GPL(tpm_tis_spi_transfer); -static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, - u16 len, u8 *result) +static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *result) { return tpm_tis_spi_transfer(data, addr, len, result, NULL); } @@ -146,7 +149,7 @@ static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, return tpm_tis_spi_transfer(data, addr, len, NULL, value); } -static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) +int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) { __le16 result_le; int rc; @@ -158,8 +161,9 @@ static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) return rc; } +EXPORT_SYMBOL_GPL(tpm_tis_spi_read16); -static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) +int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) { __le32 result_le; int rc; @@ -171,8 +175,9 @@ static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) return rc; } +EXPORT_SYMBOL_GPL(tpm_tis_spi_read32); -static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) +int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) { __le32 value_le; int rc; @@ -183,6 +188,20 @@ static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) return rc; } +EXPORT_SYMBOL_GPL(tpm_tis_spi_write32); + +int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy, + int irq, const struct tpm_tis_phy_ops *phy_ops) +{ + phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL); + if (!phy->iobuf) + return -ENOMEM; + + phy->spi_device = spi; + + return tpm_tis_core_init(&spi->dev, &phy->priv, irq, phy_ops, NULL); +} +EXPORT_SYMBOL_GPL(tpm_tis_spi_init); static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { .read_bytes = tpm_tis_spi_read_bytes, @@ -202,11 +221,7 @@ static int tpm_tis_spi_probe(struct spi_device *dev) if (!phy) return -ENOMEM; - phy->spi_device = dev; - - phy->iobuf = devm_kmalloc(&dev->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL); - if (!phy->iobuf) - return -ENOMEM; + phy->flow_control = tpm_tis_spi_flow_control; /* If the SPI device has an IRQ then use that */ if (dev->irq > 0) @@ -214,8 +229,7 @@ static int tpm_tis_spi_probe(struct spi_device *dev) else irq = -1; - return tpm_tis_core_init(&dev->dev, &phy->priv, irq, &tpm_spi_phy_ops, - NULL); + return tpm_tis_spi_init(dev, phy, irq, &tpm_spi_phy_ops); } static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); diff --git a/drivers/char/tpm/tpm_tis_spi.h b/drivers/char/tpm/tpm_tis_spi.h new file mode 100644 index 000000000000..48be5130794a --- /dev/null +++ b/drivers/char/tpm/tpm_tis_spi.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015 Infineon Technologies AG + * Copyright (C) 2016 STMicroelectronics SAS + */ + +#ifndef TPM_TIS_SPI_H +#define TPM_TIS_SPI_H + +#include "tpm_tis_core.h" + +struct tpm_tis_spi_phy { + struct tpm_tis_data priv; + struct spi_device *spi_device; + int (*flow_control)(struct tpm_tis_spi_phy *phy, + struct spi_transfer *xfer); + void (*pre_transfer)(struct tpm_tis_spi_phy *phy); + + u8 *iobuf; +}; + +static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) +{ + return container_of(data, struct tpm_tis_spi_phy, priv); +} + +extern int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy, + int irq, const struct tpm_tis_phy_ops *phy_ops); + +extern int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *in, const u8 *out); + +extern int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result); +extern int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result); +extern int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value); + +#endif From patchwork Tue Aug 6 22:07:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Boyd X-Patchwork-Id: 11079649 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 599C91395 for ; Tue, 6 Aug 2019 22:07:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 48CA8287DE for ; Tue, 6 Aug 2019 22:07:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3C61C288D7; Tue, 6 Aug 2019 22:07:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D18C8287DE for ; Tue, 6 Aug 2019 22:07:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727256AbfHFWHz (ORCPT ); Tue, 6 Aug 2019 18:07:55 -0400 Received: from mail-pl1-f193.google.com ([209.85.214.193]:36298 "EHLO mail-pl1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727204AbfHFWHz (ORCPT ); Tue, 6 Aug 2019 18:07:55 -0400 Received: by mail-pl1-f193.google.com with SMTP id k8so38429025plt.3 for ; Tue, 06 Aug 2019 15:07:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pbLJacb6MCh29sDmogMC5h9hCymvEateyvCVNbUfmp0=; b=YDoIiBkou3pIhFNxBZpj0J83UtcpbrvdPUfIEHIJdhP8SlF5ssVac7LzF+ibBYWH5U wiEkdRC6X4/LBGjrSEDSe1FoPUzZQZ0oOVE44aAVabHsFrZMvqrb5hOgrULJb0iQ9hKK +CvYjb0K4E4ld/5eUKf4zNddqy++fMK7t/WuA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pbLJacb6MCh29sDmogMC5h9hCymvEateyvCVNbUfmp0=; b=kyKeao887NPB+ISNtbgrdVciUdP7jHDPPHipuxeIlWb6FTv9m0rl+rXQanNxKS+9Y/ STQPXKldT15tlxBBdKPs4POFeofkkl1ieiU2XXeGIyg1fb061I93VxtLgy4dbkZUNySy UKaHwsS8RsgKkBamt5usA6R5jjBaZy4JCPp1AkGvYqOrEGVOwFqcdEuXwLNy+XZHV7Cl 1A/Q0Xtswlh/ZtPdjyeS0bTlPXm8zyOO1RIETU/d2A7swQKBmnqcCKThHmH1NxIVh3zV awTvI4OPIgdGeTUw3xGPlysMLCIg2X29kUZHY0uY/5TfYlXm5LHq/fucrdSgoL6GUssi aqDA== X-Gm-Message-State: APjAAAWFR4prX5cYovflOuSrkJ0rR8sutmmDh4uvf5bc+LBJRh/AGkvv 4PaZlD4hx64U+LFUaCYbh7Al3w== X-Google-Smtp-Source: APXvYqxPGQX5rSvQMu77o3qqkAW7npZloPdgkKNzcWKS4m1GnUr7uro7fEp/tJsmJ3LNl3LVL22idA== X-Received: by 2002:a17:902:e613:: with SMTP id cm19mr4814750plb.299.1565129274668; Tue, 06 Aug 2019 15:07:54 -0700 (PDT) Received: from smtp.gmail.com ([2620:15c:202:1:fa53:7765:582b:82b9]) by smtp.gmail.com with ESMTPSA id p7sm98982509pfp.131.2019.08.06.15.07.53 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Tue, 06 Aug 2019 15:07:54 -0700 (PDT) From: Stephen Boyd To: Peter Huewe , Jarkko Sakkinen Cc: Andrey Pronin , linux-kernel@vger.kernel.org, linux-integrity@vger.kernel.org, Duncan Laurie , Jason Gunthorpe , Arnd Bergmann , Greg Kroah-Hartman , Guenter Roeck , Alexander Steffen , Rob Herring Subject: [PATCH v3 3/4] dt-bindings: tpm: document properties for cr50 Date: Tue, 6 Aug 2019 15:07:49 -0700 Message-Id: <20190806220750.86597-4-swboyd@chromium.org> X-Mailer: git-send-email 2.22.0.770.g0f2c4a37fd-goog In-Reply-To: <20190806220750.86597-1-swboyd@chromium.org> References: <20190806220750.86597-1-swboyd@chromium.org> MIME-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Andrey Pronin Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50 firmware. Cc: Andrey Pronin Cc: Duncan Laurie Cc: Jason Gunthorpe Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Guenter Roeck Cc: Alexander Steffen Signed-off-by: Andrey Pronin Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- .../bindings/security/tpm/google,cr50.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/security/tpm/google,cr50.txt diff --git a/Documentation/devicetree/bindings/security/tpm/google,cr50.txt b/Documentation/devicetree/bindings/security/tpm/google,cr50.txt new file mode 100644 index 000000000000..7aa65224c8b9 --- /dev/null +++ b/Documentation/devicetree/bindings/security/tpm/google,cr50.txt @@ -0,0 +1,19 @@ +* H1 Secure Microcontroller with Cr50 Firmware on SPI Bus. + +H1 Secure Microcontroller running Cr50 firmware provides several +functions, including TPM-like functionality. It communicates over +SPI using the FIFO protocol described in the PTP Spec, section 6. + +Required properties: +- compatible: Should be "google,cr50". +- spi-max-frequency: Maximum SPI frequency. + +Example: + +&spi0 { + tpm@0 { + compatible = "google,cr50"; + reg = <0>; + spi-max-frequency = <800000>; + }; +}; From patchwork Tue Aug 6 22:07:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Boyd X-Patchwork-Id: 11079653 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0524F14E5 for ; Tue, 6 Aug 2019 22:08:07 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E678A287DE for ; Tue, 6 Aug 2019 22:08:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DA95D288C5; Tue, 6 Aug 2019 22:08:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CAFE0288D9 for ; Tue, 6 Aug 2019 22:08:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727256AbfHFWIF (ORCPT ); Tue, 6 Aug 2019 18:08:05 -0400 Received: from mail-pg1-f193.google.com ([209.85.215.193]:37739 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727266AbfHFWH4 (ORCPT ); Tue, 6 Aug 2019 18:07:56 -0400 Received: by mail-pg1-f193.google.com with SMTP id d1so9489098pgp.4 for ; Tue, 06 Aug 2019 15:07:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=aGB1JOcWkNrmNIrAbUIMMZPjec4nmi/ieC7f+At+HTU=; b=M/DuXrEJUhbdTn1c+LtqQZn1WnnZHDGk5OY3MapcY5dR4k6PX4YAXOYyKQTCfHDiX7 E/DxWSQYug+lXCugjXbMvsVBCG6mNGmXQW5pQfTHNS7HOAVnSTXwLnOIDcSXZF0JWsHY l9aHZEZv+9W+ixqTniHebSDnTStcHMmEsLa3E= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=aGB1JOcWkNrmNIrAbUIMMZPjec4nmi/ieC7f+At+HTU=; b=Xh2P+IZSXezw1gJCM2l98B4U75OjRGK+xZUqHeO2XFNWl8ETvzgIRn1qI7EWbSLQZc KfVj4NNZrtblwB/VG3a5F5BPxM51e6AiuxzSRgwdqPpZRhX61MuS6TP8gxZmBRfQ4lya djE/xHIR3kHPo/BWe2hKnmN0RzCz6l/piMBKEUOZVmdiFXGUfzsORxQfJyvwxmtCRphN qIWLx0mawN2o/Dazn2AkeSQwbZbzRG9efKZZWiiTnpDGfIxMheVviBXD7+UVNzRiAiSp AcDeeP6/Stjk7JMwbMB4s8qVJMVlkjzRhpvGN2+fZDDHawSOmG05hRA+rabE8TevBZc5 Qjmw== X-Gm-Message-State: APjAAAX3/SxW9n9tMDE/x8/eXA7WpPiCpieu21755r2pbIocHffGbrgx vYAJNM7eHBcNeKO2j5ZkuLQfSg== X-Google-Smtp-Source: APXvYqy83kbS/+V5roUjMiIEaL/Ww+Z1+7rd67r8HAW2r9cehVOmk0XZjI4ZeCPLVdXv9yPnV4PT0A== X-Received: by 2002:a62:750c:: with SMTP id q12mr6114316pfc.59.1565129275552; Tue, 06 Aug 2019 15:07:55 -0700 (PDT) Received: from smtp.gmail.com ([2620:15c:202:1:fa53:7765:582b:82b9]) by smtp.gmail.com with ESMTPSA id p7sm98982509pfp.131.2019.08.06.15.07.54 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Tue, 06 Aug 2019 15:07:55 -0700 (PDT) From: Stephen Boyd To: Peter Huewe , Jarkko Sakkinen Cc: Andrey Pronin , linux-kernel@vger.kernel.org, linux-integrity@vger.kernel.org, Duncan Laurie , Jason Gunthorpe , Arnd Bergmann , Greg Kroah-Hartman , Guenter Roeck , Alexander Steffen Subject: [PATCH v3 4/4] tpm: add driver for cr50 on SPI Date: Tue, 6 Aug 2019 15:07:50 -0700 Message-Id: <20190806220750.86597-5-swboyd@chromium.org> X-Mailer: git-send-email 2.22.0.770.g0f2c4a37fd-goog In-Reply-To: <20190806220750.86597-1-swboyd@chromium.org> References: <20190806220750.86597-1-swboyd@chromium.org> MIME-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Andrey Pronin 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 Cc: Andrey Pronin Cc: Duncan Laurie Cc: Jason Gunthorpe Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Guenter Roeck Cc: Alexander Steffen [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, migrate to functions exported from tpm_tis_spi.h] Signed-off-by: Stephen Boyd --- drivers/char/tpm/Kconfig | 9 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/cr50_spi.c | 373 ++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 drivers/char/tpm/cr50_spi.c diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 88a3c06fc153..1fb23f145ef6 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -114,6 +114,15 @@ 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_SPI + tristate "Cr50 SPI Interface" + depends on TCG_TIS_SPI + ---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..4713e52ce1b0 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -27,6 +27,7 @@ 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_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_spi.c b/drivers/char/tpm/cr50_spi.c new file mode 100644 index 000000000000..c65ac16d22b6 --- /dev/null +++ b/drivers/char/tpm/cr50_spi.c @@ -0,0 +1,373 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include "tpm_tis_core.h" +#include "tpm_tis_spi.h" + +/* + * 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 + +struct cr50_spi_phy { + struct tpm_tis_spi_phy spi_phy; + + 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; +}; + +static inline struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_spi_phy *phy) +{ + return container_of(phy, struct cr50_spi_phy, spi_phy); +} + +/* + * 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 *cr50_phy = dev_id; + + cr50_phy->irq_confirmed = true; + complete(&cr50_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 cr50_spi_phy *phy) +{ + unsigned long allowed_access = phy->last_access + phy->access_delay; + unsigned long time_now = jiffies; + struct device *dev = &phy->spi_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 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, phy->wake_after); +} + +static void cr50_wake_if_needed(struct cr50_spi_phy *cr50_phy) +{ + struct tpm_tis_spi_phy *phy = &cr50_phy->spi_phy; + + if (cr50_needs_waking(cr50_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 */ + cr50_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 spi_transfer *spi_xfer) +{ + struct device *dev = &phy->spi_device->dev; + unsigned long timeout = jiffies + CR50_FLOW_CONTROL; + struct spi_message m; + int ret; + + spi_xfer->len = 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 void cr50_spi_reinit_completion(struct tpm_tis_spi_phy *phy) +{ + struct cr50_spi_phy *cr50_phy = to_cr50_spi_phy(phy); + reinit_completion(&cr50_phy->ready); +} + +static int tpm_tis_spi_cr50_transfer(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *in, const u8 *out) +{ + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + struct cr50_spi_phy *cr50_phy = to_cr50_spi_phy(phy); + int ret; + + mutex_lock(&cr50_phy->time_track_mutex); + /* + * Do this outside of spi_bus_lock in case cr50 is not the + * only device on that spi bus. + */ + cr50_ensure_access_delay(cr50_phy); + cr50_wake_if_needed(cr50_phy); + + ret = tpm_tis_spi_transfer(data, addr, len, in, out); + + cr50_phy->last_access = jiffies; + mutex_unlock(&cr50_phy->time_track_mutex); + + return ret; +} + +static int tpm_tis_spi_cr50_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + return tpm_tis_spi_cr50_transfer(data, addr, len, result, NULL); +} + +static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, const u8 *value) +{ + return tpm_tis_spi_cr50_transfer(data, addr, len, NULL, value); +} + +static const struct tpm_tis_phy_ops tpm_spi_cr50_phy_ops = { + .read_bytes = tpm_tis_spi_cr50_read_bytes, + .write_bytes = tpm_tis_spi_cr50_write_bytes, + .read16 = tpm_tis_spi_read16, + .read32 = tpm_tis_spi_read32, + .write32 = tpm_tis_spi_write32, +}; + +static void cr50_print_fw_version(struct tpm_tis_data *data) +{ + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + int i, len = 0; + char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1]; + 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'; + + dev_info(&phy->spi_device->dev, "Cr50 firmware version: %s\n", fw_ver); +} + +static int cr50_spi_probe(struct spi_device *spi) +{ + struct tpm_tis_spi_phy *phy; + struct cr50_spi_phy *cr50_phy; + int ret; + struct tpm_chip *chip; + + cr50_phy = devm_kzalloc(&spi->dev, sizeof(*cr50_phy), GFP_KERNEL); + if (!cr50_phy) + return -ENOMEM; + + phy = &cr50_phy->spi_phy; + phy->flow_control = cr50_spi_flow_control; + phy->pre_transfer = cr50_spi_reinit_completion; + + cr50_phy->access_delay = CR50_NOIRQ_ACCESS_DELAY; + + mutex_init(&cr50_phy->time_track_mutex); + cr50_phy->wake_after = jiffies; + cr50_phy->last_access = jiffies; + + init_completion(&cr50_phy->ready); + if (spi->irq > 0) { + ret = devm_request_irq(&spi->dev, spi->irq, cr50_spi_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "cr50_spi", cr50_phy); + if (ret < 0) { + if (ret == -EPROBE_DEFER) + return ret; + dev_warn(&spi->dev, "Requesting IRQ %d failed: %d\n", + spi->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. + */ + cr50_phy->irq_needs_confirmation = true; + } + } else { + dev_warn(&spi->dev, + "No IRQ - will use delays between transactions.\n"); + } + + ret = tpm_tis_spi_init(spi, phy, -1, &tpm_spi_cr50_phy_ops); + if (ret) + return ret; + + cr50_print_fw_version(&phy->priv); + + chip = dev_get_drvdata(&spi->dev); + chip->flags |= TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED; + + 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 tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + struct cr50_spi_phy *cr50_phy = to_cr50_spi_phy(phy); + + /* + * Jiffies not increased during suspend, so we need to reset + * the time to wake Cr50 after resume. + */ + cr50_phy->wake_after = jiffies; + + return tpm_tis_resume(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cr50_spi_pm, tpm_pm_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");