18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Infineon Technologies AG 48c2ecf20Sopenharmony_ci * Copyright (C) 2016 STMicroelectronics SAS 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Peter Huewe <peter.huewe@infineon.com> 88c2ecf20Sopenharmony_ci * Christophe Ricard <christophe-h.ricard@st.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Device driver for TCG/TCPA TPM (trusted platform module). 138c2ecf20Sopenharmony_ci * Specifications at www.trustedcomputinggroup.org 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This device driver implements the TPM interface as defined in 168c2ecf20Sopenharmony_ci * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native 178c2ecf20Sopenharmony_ci * SPI access_. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * It is based on the original tpm_tis device driver from Leendert van 208c2ecf20Sopenharmony_ci * Dorn and Kyleen Hall and Jarko Sakkinnen. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/acpi.h> 248c2ecf20Sopenharmony_ci#include <linux/completion.h> 258c2ecf20Sopenharmony_ci#include <linux/init.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/of_device.h> 328c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 338c2ecf20Sopenharmony_ci#include <linux/tpm.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include "tpm.h" 368c2ecf20Sopenharmony_ci#include "tpm_tis_core.h" 378c2ecf20Sopenharmony_ci#include "tpm_tis_spi.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MAX_SPI_FRAMESIZE 64 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * TCG SPI flow control is documented in section 6.4 of the spec[1]. In short, 438c2ecf20Sopenharmony_ci * keep trying to read from the device until MISO goes high indicating the 448c2ecf20Sopenharmony_ci * wait state has ended. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * [1] https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/ 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy, 498c2ecf20Sopenharmony_ci struct spi_transfer *spi_xfer) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct spi_message m; 528c2ecf20Sopenharmony_ci int ret, i; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if ((phy->iobuf[3] & 0x01) == 0) { 558c2ecf20Sopenharmony_ci // handle SPI wait states 568c2ecf20Sopenharmony_ci for (i = 0; i < TPM_RETRY; i++) { 578c2ecf20Sopenharmony_ci spi_xfer->len = 1; 588c2ecf20Sopenharmony_ci spi_message_init(&m); 598c2ecf20Sopenharmony_ci spi_message_add_tail(spi_xfer, &m); 608c2ecf20Sopenharmony_ci ret = spi_sync_locked(phy->spi_device, &m); 618c2ecf20Sopenharmony_ci if (ret < 0) 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci if (phy->iobuf[0] & 0x01) 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (i == TPM_RETRY) 688c2ecf20Sopenharmony_ci return -ETIMEDOUT; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciint tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 758c2ecf20Sopenharmony_ci u8 *in, const u8 *out) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); 788c2ecf20Sopenharmony_ci int ret = 0; 798c2ecf20Sopenharmony_ci struct spi_message m; 808c2ecf20Sopenharmony_ci struct spi_transfer spi_xfer; 818c2ecf20Sopenharmony_ci u8 transfer_len; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci spi_bus_lock(phy->spi_device->master); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci while (len) { 868c2ecf20Sopenharmony_ci transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1); 898c2ecf20Sopenharmony_ci phy->iobuf[1] = 0xd4; 908c2ecf20Sopenharmony_ci phy->iobuf[2] = addr >> 8; 918c2ecf20Sopenharmony_ci phy->iobuf[3] = addr; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci memset(&spi_xfer, 0, sizeof(spi_xfer)); 948c2ecf20Sopenharmony_ci spi_xfer.tx_buf = phy->iobuf; 958c2ecf20Sopenharmony_ci spi_xfer.rx_buf = phy->iobuf; 968c2ecf20Sopenharmony_ci spi_xfer.len = 4; 978c2ecf20Sopenharmony_ci spi_xfer.cs_change = 1; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci spi_message_init(&m); 1008c2ecf20Sopenharmony_ci spi_message_add_tail(&spi_xfer, &m); 1018c2ecf20Sopenharmony_ci ret = spi_sync_locked(phy->spi_device, &m); 1028c2ecf20Sopenharmony_ci if (ret < 0) 1038c2ecf20Sopenharmony_ci goto exit; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Flow control transfers are receive only */ 1068c2ecf20Sopenharmony_ci spi_xfer.tx_buf = NULL; 1078c2ecf20Sopenharmony_ci ret = phy->flow_control(phy, &spi_xfer); 1088c2ecf20Sopenharmony_ci if (ret < 0) 1098c2ecf20Sopenharmony_ci goto exit; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci spi_xfer.cs_change = 0; 1128c2ecf20Sopenharmony_ci spi_xfer.len = transfer_len; 1138c2ecf20Sopenharmony_ci spi_xfer.delay.value = 5; 1148c2ecf20Sopenharmony_ci spi_xfer.delay.unit = SPI_DELAY_UNIT_USECS; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (out) { 1178c2ecf20Sopenharmony_ci spi_xfer.tx_buf = phy->iobuf; 1188c2ecf20Sopenharmony_ci spi_xfer.rx_buf = NULL; 1198c2ecf20Sopenharmony_ci memcpy(phy->iobuf, out, transfer_len); 1208c2ecf20Sopenharmony_ci out += transfer_len; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spi_message_init(&m); 1248c2ecf20Sopenharmony_ci spi_message_add_tail(&spi_xfer, &m); 1258c2ecf20Sopenharmony_ci reinit_completion(&phy->ready); 1268c2ecf20Sopenharmony_ci ret = spi_sync_locked(phy->spi_device, &m); 1278c2ecf20Sopenharmony_ci if (ret < 0) 1288c2ecf20Sopenharmony_ci goto exit; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (in) { 1318c2ecf20Sopenharmony_ci memcpy(in, phy->iobuf, transfer_len); 1328c2ecf20Sopenharmony_ci in += transfer_len; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci len -= transfer_len; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciexit: 1398c2ecf20Sopenharmony_ci spi_bus_unlock(phy->spi_device->master); 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, 1448c2ecf20Sopenharmony_ci u16 len, u8 *result) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return tpm_tis_spi_transfer(data, addr, len, result, NULL); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, 1508c2ecf20Sopenharmony_ci u16 len, const u8 *value) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci return tpm_tis_spi_transfer(data, addr, len, NULL, value); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci __le16 result_le; 1588c2ecf20Sopenharmony_ci int rc; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), 1618c2ecf20Sopenharmony_ci (u8 *)&result_le); 1628c2ecf20Sopenharmony_ci if (!rc) 1638c2ecf20Sopenharmony_ci *result = le16_to_cpu(result_le); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return rc; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciint tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci __le32 result_le; 1718c2ecf20Sopenharmony_ci int rc; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), 1748c2ecf20Sopenharmony_ci (u8 *)&result_le); 1758c2ecf20Sopenharmony_ci if (!rc) 1768c2ecf20Sopenharmony_ci *result = le32_to_cpu(result_le); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return rc; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciint tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci __le32 value_le; 1848c2ecf20Sopenharmony_ci int rc; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci value_le = cpu_to_le32(value); 1878c2ecf20Sopenharmony_ci rc = data->phy_ops->write_bytes(data, addr, sizeof(u32), 1888c2ecf20Sopenharmony_ci (u8 *)&value_le); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return rc; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy, 1948c2ecf20Sopenharmony_ci int irq, const struct tpm_tis_phy_ops *phy_ops) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL); 1978c2ecf20Sopenharmony_ci if (!phy->iobuf) 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci phy->spi_device = spi; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return tpm_tis_core_init(&spi->dev, &phy->priv, irq, phy_ops, NULL); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic const struct tpm_tis_phy_ops tpm_spi_phy_ops = { 2068c2ecf20Sopenharmony_ci .read_bytes = tpm_tis_spi_read_bytes, 2078c2ecf20Sopenharmony_ci .write_bytes = tpm_tis_spi_write_bytes, 2088c2ecf20Sopenharmony_ci .read16 = tpm_tis_spi_read16, 2098c2ecf20Sopenharmony_ci .read32 = tpm_tis_spi_read32, 2108c2ecf20Sopenharmony_ci .write32 = tpm_tis_spi_write32, 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int tpm_tis_spi_probe(struct spi_device *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct tpm_tis_spi_phy *phy; 2168c2ecf20Sopenharmony_ci int irq; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), 2198c2ecf20Sopenharmony_ci GFP_KERNEL); 2208c2ecf20Sopenharmony_ci if (!phy) 2218c2ecf20Sopenharmony_ci return -ENOMEM; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci phy->flow_control = tpm_tis_spi_flow_control; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* If the SPI device has an IRQ then use that */ 2268c2ecf20Sopenharmony_ci if (dev->irq > 0) 2278c2ecf20Sopenharmony_ci irq = dev->irq; 2288c2ecf20Sopenharmony_ci else 2298c2ecf20Sopenharmony_ci irq = -1; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci init_completion(&phy->ready); 2328c2ecf20Sopenharmony_ci return tpm_tis_spi_init(dev, phy, irq, &tpm_spi_phy_ops); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_citypedef int (*tpm_tis_spi_probe_func)(struct spi_device *); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int tpm_tis_spi_driver_probe(struct spi_device *spi) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci const struct spi_device_id *spi_dev_id = spi_get_device_id(spi); 2408c2ecf20Sopenharmony_ci tpm_tis_spi_probe_func probe_func; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci probe_func = of_device_get_match_data(&spi->dev); 2438c2ecf20Sopenharmony_ci if (!probe_func && spi_dev_id) 2448c2ecf20Sopenharmony_ci probe_func = (tpm_tis_spi_probe_func)spi_dev_id->driver_data; 2458c2ecf20Sopenharmony_ci if (!probe_func) 2468c2ecf20Sopenharmony_ci return -ENODEV; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return probe_func(spi); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_spi_resume); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int tpm_tis_spi_remove(struct spi_device *dev) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct tpm_chip *chip = spi_get_drvdata(dev); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci tpm_chip_unregister(chip); 2588c2ecf20Sopenharmony_ci tpm_tis_remove(chip); 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic const struct spi_device_id tpm_tis_spi_id[] = { 2638c2ecf20Sopenharmony_ci { "st33htpm-spi", (unsigned long)tpm_tis_spi_probe }, 2648c2ecf20Sopenharmony_ci { "slb9670", (unsigned long)tpm_tis_spi_probe }, 2658c2ecf20Sopenharmony_ci { "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe }, 2668c2ecf20Sopenharmony_ci { "tpm_tis-spi", (unsigned long)tpm_tis_spi_probe }, 2678c2ecf20Sopenharmony_ci { "cr50", (unsigned long)cr50_spi_probe }, 2688c2ecf20Sopenharmony_ci {} 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const struct of_device_id of_tis_spi_match[] = { 2738c2ecf20Sopenharmony_ci { .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe }, 2748c2ecf20Sopenharmony_ci { .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe }, 2758c2ecf20Sopenharmony_ci { .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe }, 2768c2ecf20Sopenharmony_ci { .compatible = "google,cr50", .data = cr50_spi_probe }, 2778c2ecf20Sopenharmony_ci {} 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_tis_spi_match); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic const struct acpi_device_id acpi_tis_spi_match[] = { 2828c2ecf20Sopenharmony_ci {"SMO0768", 0}, 2838c2ecf20Sopenharmony_ci {} 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic struct spi_driver tpm_tis_spi_driver = { 2888c2ecf20Sopenharmony_ci .driver = { 2898c2ecf20Sopenharmony_ci .name = "tpm_tis_spi", 2908c2ecf20Sopenharmony_ci .pm = &tpm_tis_pm, 2918c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(of_tis_spi_match), 2928c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(acpi_tis_spi_match), 2938c2ecf20Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 2948c2ecf20Sopenharmony_ci }, 2958c2ecf20Sopenharmony_ci .probe = tpm_tis_spi_driver_probe, 2968c2ecf20Sopenharmony_ci .remove = tpm_tis_spi_remove, 2978c2ecf20Sopenharmony_ci .id_table = tpm_tis_spi_id, 2988c2ecf20Sopenharmony_ci}; 2998c2ecf20Sopenharmony_cimodule_spi_driver(tpm_tis_spi_driver); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPM Driver for native SPI access"); 3028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 303