linux/drivers/char/tpm/tpm_tis_spi.c
<<
>>
Prefs
   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");
 273