Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.11-rc6 271 lines 6.8 kB view raw
1/* 2 * Copyright (C) 2015 Infineon Technologies AG 3 * Copyright (C) 2016 STMicroelectronics SAS 4 * 5 * Authors: 6 * Peter Huewe <peter.huewe@infineon.com> 7 * Christophe Ricard <christophe-h.ricard@st.com> 8 * 9 * Maintained by: <tpmdd-devel@lists.sourceforge.net> 10 * 11 * Device driver for TCG/TCPA TPM (trusted platform module). 12 * Specifications at www.trustedcomputinggroup.org 13 * 14 * This device driver implements the TPM interface as defined in 15 * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native 16 * SPI access_. 17 * 18 * It is based on the original tpm_tis device driver from Leendert van 19 * Dorn and Kyleen Hall and Jarko Sakkinnen. 20 * 21 * This program is free software; you can redistribute it and/or 22 * modify it under the terms of the GNU General Public License as 23 * published by the Free Software Foundation, version 2 of the 24 * License. 25 */ 26 27#include <linux/init.h> 28#include <linux/module.h> 29#include <linux/moduleparam.h> 30#include <linux/slab.h> 31#include <linux/interrupt.h> 32#include <linux/wait.h> 33#include <linux/acpi.h> 34#include <linux/freezer.h> 35 36#include <linux/spi/spi.h> 37#include <linux/gpio.h> 38#include <linux/of_irq.h> 39#include <linux/of_gpio.h> 40#include <linux/tpm.h> 41#include "tpm.h" 42#include "tpm_tis_core.h" 43 44#define MAX_SPI_FRAMESIZE 64 45 46struct tpm_tis_spi_phy { 47 struct tpm_tis_data priv; 48 struct spi_device *spi_device; 49 50 u8 tx_buf[MAX_SPI_FRAMESIZE + 4]; 51 u8 rx_buf[MAX_SPI_FRAMESIZE + 4]; 52}; 53 54static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) 55{ 56 return container_of(data, struct tpm_tis_spi_phy, priv); 57} 58 59static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, 60 u16 len, u8 *result) 61{ 62 struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); 63 int ret, i; 64 struct spi_message m; 65 struct spi_transfer spi_xfer = { 66 .tx_buf = phy->tx_buf, 67 .rx_buf = phy->rx_buf, 68 .len = 4, 69 }; 70 71 if (len > MAX_SPI_FRAMESIZE) 72 return -ENOMEM; 73 74 phy->tx_buf[0] = 0x80 | (len - 1); 75 phy->tx_buf[1] = 0xd4; 76 phy->tx_buf[2] = (addr >> 8) & 0xFF; 77 phy->tx_buf[3] = addr & 0xFF; 78 79 spi_xfer.cs_change = 1; 80 spi_message_init(&m); 81 spi_message_add_tail(&spi_xfer, &m); 82 83 spi_bus_lock(phy->spi_device->master); 84 ret = spi_sync_locked(phy->spi_device, &m); 85 if (ret < 0) 86 goto exit; 87 88 memset(phy->tx_buf, 0, len); 89 90 /* According to TCG PTP specification, if there is no TPM present at 91 * all, then the design has a weak pull-up on MISO. If a TPM is not 92 * present, a pull-up on MISO means that the SB controller sees a 1, 93 * and will latch in 0xFF on the read. 94 */ 95 for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { 96 spi_xfer.len = 1; 97 spi_message_init(&m); 98 spi_message_add_tail(&spi_xfer, &m); 99 ret = spi_sync_locked(phy->spi_device, &m); 100 if (ret < 0) 101 goto exit; 102 } 103 104 spi_xfer.cs_change = 0; 105 spi_xfer.len = len; 106 spi_xfer.rx_buf = result; 107 108 spi_message_init(&m); 109 spi_message_add_tail(&spi_xfer, &m); 110 ret = spi_sync_locked(phy->spi_device, &m); 111 112exit: 113 spi_bus_unlock(phy->spi_device->master); 114 return ret; 115} 116 117static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, 118 u16 len, u8 *value) 119{ 120 struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); 121 int ret, i; 122 struct spi_message m; 123 struct spi_transfer spi_xfer = { 124 .tx_buf = phy->tx_buf, 125 .rx_buf = phy->rx_buf, 126 .len = 4, 127 }; 128 129 if (len > MAX_SPI_FRAMESIZE) 130 return -ENOMEM; 131 132 phy->tx_buf[0] = len - 1; 133 phy->tx_buf[1] = 0xd4; 134 phy->tx_buf[2] = (addr >> 8) & 0xFF; 135 phy->tx_buf[3] = addr & 0xFF; 136 137 spi_xfer.cs_change = 1; 138 spi_message_init(&m); 139 spi_message_add_tail(&spi_xfer, &m); 140 141 spi_bus_lock(phy->spi_device->master); 142 ret = spi_sync_locked(phy->spi_device, &m); 143 if (ret < 0) 144 goto exit; 145 146 memset(phy->tx_buf, 0, len); 147 148 /* According to TCG PTP specification, if there is no TPM present at 149 * all, then the design has a weak pull-up on MISO. If a TPM is not 150 * present, a pull-up on MISO means that the SB controller sees a 1, 151 * and will latch in 0xFF on the read. 152 */ 153 for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { 154 spi_xfer.len = 1; 155 spi_message_init(&m); 156 spi_message_add_tail(&spi_xfer, &m); 157 ret = spi_sync_locked(phy->spi_device, &m); 158 if (ret < 0) 159 goto exit; 160 } 161 162 spi_xfer.len = len; 163 spi_xfer.tx_buf = value; 164 spi_xfer.cs_change = 0; 165 spi_xfer.tx_buf = value; 166 spi_message_init(&m); 167 spi_message_add_tail(&spi_xfer, &m); 168 ret = spi_sync_locked(phy->spi_device, &m); 169 170exit: 171 spi_bus_unlock(phy->spi_device->master); 172 return ret; 173} 174 175static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) 176{ 177 int rc; 178 179 rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)result); 180 if (!rc) 181 *result = le16_to_cpu(*result); 182 return rc; 183} 184 185static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) 186{ 187 int rc; 188 189 rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)result); 190 if (!rc) 191 *result = le32_to_cpu(*result); 192 return rc; 193} 194 195static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) 196{ 197 value = cpu_to_le32(value); 198 return data->phy_ops->write_bytes(data, addr, sizeof(u32), 199 (u8 *)&value); 200} 201 202static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { 203 .read_bytes = tpm_tis_spi_read_bytes, 204 .write_bytes = tpm_tis_spi_write_bytes, 205 .read16 = tpm_tis_spi_read16, 206 .read32 = tpm_tis_spi_read32, 207 .write32 = tpm_tis_spi_write32, 208}; 209 210static int tpm_tis_spi_probe(struct spi_device *dev) 211{ 212 struct tpm_tis_spi_phy *phy; 213 214 phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), 215 GFP_KERNEL); 216 if (!phy) 217 return -ENOMEM; 218 219 phy->spi_device = dev; 220 221 return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops, 222 NULL); 223} 224 225static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); 226 227static int tpm_tis_spi_remove(struct spi_device *dev) 228{ 229 struct tpm_chip *chip = spi_get_drvdata(dev); 230 231 tpm_chip_unregister(chip); 232 tpm_tis_remove(chip); 233 return 0; 234} 235 236static const struct spi_device_id tpm_tis_spi_id[] = { 237 {"tpm_tis_spi", 0}, 238 {} 239}; 240MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); 241 242static const struct of_device_id of_tis_spi_match[] = { 243 { .compatible = "st,st33htpm-spi", }, 244 { .compatible = "infineon,slb9670", }, 245 { .compatible = "tcg,tpm_tis-spi", }, 246 {} 247}; 248MODULE_DEVICE_TABLE(of, of_tis_spi_match); 249 250static const struct acpi_device_id acpi_tis_spi_match[] = { 251 {"SMO0768", 0}, 252 {} 253}; 254MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match); 255 256static struct spi_driver tpm_tis_spi_driver = { 257 .driver = { 258 .owner = THIS_MODULE, 259 .name = "tpm_tis_spi", 260 .pm = &tpm_tis_pm, 261 .of_match_table = of_match_ptr(of_tis_spi_match), 262 .acpi_match_table = ACPI_PTR(acpi_tis_spi_match), 263 }, 264 .probe = tpm_tis_spi_probe, 265 .remove = tpm_tis_spi_remove, 266 .id_table = tpm_tis_spi_id, 267}; 268module_spi_driver(tpm_tis_spi_driver); 269 270MODULE_DESCRIPTION("TPM Driver for native SPI access"); 271MODULE_LICENSE("GPL");