linux/drivers/char/tpm/tpm_i2c_atmel.c
<<
>>
Prefs
   1/*
   2 * ATMEL I2C TPM AT97SC3204T
   3 *
   4 * Copyright (C) 2012 V Lab Technologies
   5 *  Teddy Reed <teddy@prosauce.org>
   6 * Copyright (C) 2013, Obsidian Research Corp.
   7 *  Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
   8 * Device driver for ATMEL I2C TPMs.
   9 *
  10 * Teddy Reed determined the basic I2C command flow, unlike other I2C TPM
  11 * devices the raw TCG formatted TPM command data is written via I2C and then
  12 * raw TCG formatted TPM command data is returned via I2C.
  13 *
  14 * TGC status/locality/etc functions seen in the LPC implementation do not
  15 * seem to be present.
  16 *
  17 * This program is free software: you can redistribute it and/or modify
  18 * it under the terms of the GNU General Public License as published by
  19 * the Free Software Foundation, either version 2 of the License, or
  20 * (at your option) any later version.
  21 *
  22 * This program is distributed in the hope that it will be useful,
  23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25 * GNU General Public License for more details.
  26 *
  27 * You should have received a copy of the GNU General Public License
  28 * along with this program.  If not, see http://www.gnu.org/licenses/>.
  29 */
  30#include <linux/init.h>
  31#include <linux/module.h>
  32#include <linux/moduleparam.h>
  33#include <linux/slab.h>
  34#include <linux/i2c.h>
  35#include "tpm.h"
  36
  37#define I2C_DRIVER_NAME "tpm_i2c_atmel"
  38
  39#define TPM_I2C_SHORT_TIMEOUT  750     /* ms */
  40#define TPM_I2C_LONG_TIMEOUT   2000    /* 2 sec */
  41
  42#define ATMEL_STS_OK 1
  43
  44struct priv_data {
  45        size_t len;
  46        /* This is the amount we read on the first try. 25 was chosen to fit a
  47         * fair number of read responses in the buffer so a 2nd retry can be
  48         * avoided in small message cases. */
  49        u8 buffer[sizeof(struct tpm_output_header) + 25];
  50};
  51
  52static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
  53{
  54        struct priv_data *priv = chip->vendor.priv;
  55        struct i2c_client *client = to_i2c_client(chip->pdev);
  56        s32 status;
  57
  58        priv->len = 0;
  59
  60        if (len <= 2)
  61                return -EIO;
  62
  63        status = i2c_master_send(client, buf, len);
  64
  65        dev_dbg(chip->pdev,
  66                "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
  67                (int)min_t(size_t, 64, len), buf, len, status);
  68        return status;
  69}
  70
  71static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
  72{
  73        struct priv_data *priv = chip->vendor.priv;
  74        struct i2c_client *client = to_i2c_client(chip->pdev);
  75        struct tpm_output_header *hdr =
  76                (struct tpm_output_header *)priv->buffer;
  77        u32 expected_len;
  78        int rc;
  79
  80        if (priv->len == 0)
  81                return -EIO;
  82
  83        /* Get the message size from the message header, if we didn't get the
  84         * whole message in read_status then we need to re-read the
  85         * message. */
  86        expected_len = be32_to_cpu(hdr->length);
  87        if (expected_len > count)
  88                return -ENOMEM;
  89
  90        if (priv->len >= expected_len) {
  91                dev_dbg(chip->pdev,
  92                        "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
  93                        (int)min_t(size_t, 64, expected_len), buf, count,
  94                        expected_len);
  95                memcpy(buf, priv->buffer, expected_len);
  96                return expected_len;
  97        }
  98
  99        rc = i2c_master_recv(client, buf, expected_len);
 100        dev_dbg(chip->pdev,
 101                "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
 102                (int)min_t(size_t, 64, expected_len), buf, count,
 103                expected_len);
 104        return rc;
 105}
 106
 107static void i2c_atmel_cancel(struct tpm_chip *chip)
 108{
 109        dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported");
 110}
 111
 112static u8 i2c_atmel_read_status(struct tpm_chip *chip)
 113{
 114        struct priv_data *priv = chip->vendor.priv;
 115        struct i2c_client *client = to_i2c_client(chip->pdev);
 116        int rc;
 117
 118        /* The TPM fails the I2C read until it is ready, so we do the entire
 119         * transfer here and buffer it locally. This way the common code can
 120         * properly handle the timeouts. */
 121        priv->len = 0;
 122        memset(priv->buffer, 0, sizeof(priv->buffer));
 123
 124
 125        /* Once the TPM has completed the command the command remains readable
 126         * until another command is issued. */
 127        rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
 128        dev_dbg(chip->pdev,
 129                "%s: sts=%d", __func__, rc);
 130        if (rc <= 0)
 131                return 0;
 132
 133        priv->len = rc;
 134
 135        return ATMEL_STS_OK;
 136}
 137
 138static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
 139{
 140        return false;
 141}
 142
 143static const struct tpm_class_ops i2c_atmel = {
 144        .status = i2c_atmel_read_status,
 145        .recv = i2c_atmel_recv,
 146        .send = i2c_atmel_send,
 147        .cancel = i2c_atmel_cancel,
 148        .req_complete_mask = ATMEL_STS_OK,
 149        .req_complete_val = ATMEL_STS_OK,
 150        .req_canceled = i2c_atmel_req_canceled,
 151};
 152
 153static int i2c_atmel_probe(struct i2c_client *client,
 154                           const struct i2c_device_id *id)
 155{
 156        struct tpm_chip *chip;
 157        struct device *dev = &client->dev;
 158
 159        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 160                return -ENODEV;
 161
 162        chip = tpmm_chip_alloc(dev, &i2c_atmel);
 163        if (IS_ERR(chip))
 164                return PTR_ERR(chip);
 165
 166        chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
 167                                         GFP_KERNEL);
 168        if (!chip->vendor.priv)
 169                return -ENOMEM;
 170
 171        /* Default timeouts */
 172        chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
 173        chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
 174        chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
 175        chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
 176        chip->vendor.irq = 0;
 177
 178        /* There is no known way to probe for this device, and all version
 179         * information seems to be read via TPM commands. Thus we rely on the
 180         * TPM startup process in the common code to detect the device. */
 181        if (tpm_get_timeouts(chip))
 182                return -ENODEV;
 183
 184        if (tpm_do_selftest(chip))
 185                return -ENODEV;
 186
 187        return tpm_chip_register(chip);
 188}
 189
 190static int i2c_atmel_remove(struct i2c_client *client)
 191{
 192        struct device *dev = &(client->dev);
 193        struct tpm_chip *chip = dev_get_drvdata(dev);
 194        tpm_chip_unregister(chip);
 195        return 0;
 196}
 197
 198static const struct i2c_device_id i2c_atmel_id[] = {
 199        {I2C_DRIVER_NAME, 0},
 200        {}
 201};
 202MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
 203
 204#ifdef CONFIG_OF
 205static const struct of_device_id i2c_atmel_of_match[] = {
 206        {.compatible = "atmel,at97sc3204t"},
 207        {},
 208};
 209MODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
 210#endif
 211
 212static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
 213
 214static struct i2c_driver i2c_atmel_driver = {
 215        .id_table = i2c_atmel_id,
 216        .probe = i2c_atmel_probe,
 217        .remove = i2c_atmel_remove,
 218        .driver = {
 219                .name = I2C_DRIVER_NAME,
 220                .pm = &i2c_atmel_pm_ops,
 221                .of_match_table = of_match_ptr(i2c_atmel_of_match),
 222        },
 223};
 224
 225module_i2c_driver(i2c_atmel_driver);
 226
 227MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
 228MODULE_DESCRIPTION("Atmel TPM I2C Driver");
 229MODULE_LICENSE("GPL");
 230