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.8-rc4 272 lines 6.9 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/module.h> 37#include <linux/spi/spi.h> 38#include <linux/gpio.h> 39#include <linux/of_irq.h> 40#include <linux/of_gpio.h> 41#include <linux/tpm.h> 42#include "tpm.h" 43#include "tpm_tis_core.h" 44 45#define MAX_SPI_FRAMESIZE 64 46 47struct tpm_tis_spi_phy { 48 struct tpm_tis_data priv; 49 struct spi_device *spi_device; 50 51 u8 tx_buf[MAX_SPI_FRAMESIZE + 4]; 52 u8 rx_buf[MAX_SPI_FRAMESIZE + 4]; 53}; 54 55static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) 56{ 57 return container_of(data, struct tpm_tis_spi_phy, priv); 58} 59 60static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, 61 u16 len, u8 *result) 62{ 63 struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); 64 int ret, i; 65 struct spi_message m; 66 struct spi_transfer spi_xfer = { 67 .tx_buf = phy->tx_buf, 68 .rx_buf = phy->rx_buf, 69 .len = 4, 70 }; 71 72 if (len > MAX_SPI_FRAMESIZE) 73 return -ENOMEM; 74 75 phy->tx_buf[0] = 0x80 | (len - 1); 76 phy->tx_buf[1] = 0xd4; 77 phy->tx_buf[2] = (addr >> 8) & 0xFF; 78 phy->tx_buf[3] = addr & 0xFF; 79 80 spi_xfer.cs_change = 1; 81 spi_message_init(&m); 82 spi_message_add_tail(&spi_xfer, &m); 83 84 spi_bus_lock(phy->spi_device->master); 85 ret = spi_sync_locked(phy->spi_device, &m); 86 if (ret < 0) 87 goto exit; 88 89 memset(phy->tx_buf, 0, len); 90 91 /* According to TCG PTP specification, if there is no TPM present at 92 * all, then the design has a weak pull-up on MISO. If a TPM is not 93 * present, a pull-up on MISO means that the SB controller sees a 1, 94 * and will latch in 0xFF on the read. 95 */ 96 for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { 97 spi_xfer.len = 1; 98 spi_message_init(&m); 99 spi_message_add_tail(&spi_xfer, &m); 100 ret = spi_sync_locked(phy->spi_device, &m); 101 if (ret < 0) 102 goto exit; 103 } 104 105 spi_xfer.cs_change = 0; 106 spi_xfer.len = len; 107 spi_xfer.rx_buf = result; 108 109 spi_message_init(&m); 110 spi_message_add_tail(&spi_xfer, &m); 111 ret = spi_sync_locked(phy->spi_device, &m); 112 113exit: 114 spi_bus_unlock(phy->spi_device->master); 115 return ret; 116} 117 118static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, 119 u16 len, u8 *value) 120{ 121 struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); 122 int ret, i; 123 struct spi_message m; 124 struct spi_transfer spi_xfer = { 125 .tx_buf = phy->tx_buf, 126 .rx_buf = phy->rx_buf, 127 .len = 4, 128 }; 129 130 if (len > MAX_SPI_FRAMESIZE) 131 return -ENOMEM; 132 133 phy->tx_buf[0] = len - 1; 134 phy->tx_buf[1] = 0xd4; 135 phy->tx_buf[2] = (addr >> 8) & 0xFF; 136 phy->tx_buf[3] = addr & 0xFF; 137 138 spi_xfer.cs_change = 1; 139 spi_message_init(&m); 140 spi_message_add_tail(&spi_xfer, &m); 141 142 spi_bus_lock(phy->spi_device->master); 143 ret = spi_sync_locked(phy->spi_device, &m); 144 if (ret < 0) 145 goto exit; 146 147 memset(phy->tx_buf, 0, len); 148 149 /* According to TCG PTP specification, if there is no TPM present at 150 * all, then the design has a weak pull-up on MISO. If a TPM is not 151 * present, a pull-up on MISO means that the SB controller sees a 1, 152 * and will latch in 0xFF on the read. 153 */ 154 for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { 155 spi_xfer.len = 1; 156 spi_message_init(&m); 157 spi_message_add_tail(&spi_xfer, &m); 158 ret = spi_sync_locked(phy->spi_device, &m); 159 if (ret < 0) 160 goto exit; 161 } 162 163 spi_xfer.len = len; 164 spi_xfer.tx_buf = value; 165 spi_xfer.cs_change = 0; 166 spi_xfer.tx_buf = value; 167 spi_message_init(&m); 168 spi_message_add_tail(&spi_xfer, &m); 169 ret = spi_sync_locked(phy->spi_device, &m); 170 171exit: 172 spi_bus_unlock(phy->spi_device->master); 173 return ret; 174} 175 176static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) 177{ 178 int rc; 179 180 rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)result); 181 if (!rc) 182 *result = le16_to_cpu(*result); 183 return rc; 184} 185 186static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) 187{ 188 int rc; 189 190 rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)result); 191 if (!rc) 192 *result = le32_to_cpu(*result); 193 return rc; 194} 195 196static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) 197{ 198 value = cpu_to_le32(value); 199 return data->phy_ops->write_bytes(data, addr, sizeof(u32), 200 (u8 *)&value); 201} 202 203static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { 204 .read_bytes = tpm_tis_spi_read_bytes, 205 .write_bytes = tpm_tis_spi_write_bytes, 206 .read16 = tpm_tis_spi_read16, 207 .read32 = tpm_tis_spi_read32, 208 .write32 = tpm_tis_spi_write32, 209}; 210 211static int tpm_tis_spi_probe(struct spi_device *dev) 212{ 213 struct tpm_tis_spi_phy *phy; 214 215 phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), 216 GFP_KERNEL); 217 if (!phy) 218 return -ENOMEM; 219 220 phy->spi_device = dev; 221 222 return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops, 223 NULL); 224} 225 226static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); 227 228static int tpm_tis_spi_remove(struct spi_device *dev) 229{ 230 struct tpm_chip *chip = spi_get_drvdata(dev); 231 232 tpm_chip_unregister(chip); 233 tpm_tis_remove(chip); 234 return 0; 235} 236 237static const struct spi_device_id tpm_tis_spi_id[] = { 238 {"tpm_tis_spi", 0}, 239 {} 240}; 241MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); 242 243static const struct of_device_id of_tis_spi_match[] = { 244 { .compatible = "st,st33htpm-spi", }, 245 { .compatible = "infineon,slb9670", }, 246 { .compatible = "tcg,tpm_tis-spi", }, 247 {} 248}; 249MODULE_DEVICE_TABLE(of, of_tis_spi_match); 250 251static const struct acpi_device_id acpi_tis_spi_match[] = { 252 {"SMO0768", 0}, 253 {} 254}; 255MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match); 256 257static struct spi_driver tpm_tis_spi_driver = { 258 .driver = { 259 .owner = THIS_MODULE, 260 .name = "tpm_tis_spi", 261 .pm = &tpm_tis_pm, 262 .of_match_table = of_match_ptr(of_tis_spi_match), 263 .acpi_match_table = ACPI_PTR(acpi_tis_spi_match), 264 }, 265 .probe = tpm_tis_spi_probe, 266 .remove = tpm_tis_spi_remove, 267 .id_table = tpm_tis_spi_id, 268}; 269module_spi_driver(tpm_tis_spi_driver); 270 271MODULE_DESCRIPTION("TPM Driver for native SPI access"); 272MODULE_LICENSE("GPL");