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 = dev_get_drvdata(&chip->dev);
  55        struct i2c_client *client = to_i2c_client(chip->dev.parent);
  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->dev,
  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 = dev_get_drvdata(&chip->dev);
  74        struct i2c_client *client = to_i2c_client(chip->dev.parent);
  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->dev,
  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->dev,
 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->dev, "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 = dev_get_drvdata(&chip->dev);
 115        struct i2c_client *client = to_i2c_client(chip->dev.parent);
 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->dev,
 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        .flags = TPM_OPS_AUTO_STARTUP,
 145        .status = i2c_atmel_read_status,
 146        .recv = i2c_atmel_recv,
 147        .send = i2c_atmel_send,
 148        .cancel = i2c_atmel_cancel,
 149        .req_complete_mask = ATMEL_STS_OK,
 150        .req_complete_val = ATMEL_STS_OK,
 151        .req_canceled = i2c_atmel_req_canceled,
 152};
 153
 154static int i2c_atmel_probe(struct i2c_client *client,
 155                           const struct i2c_device_id *id)
 156{
 157        struct tpm_chip *chip;
 158        struct device *dev = &client->dev;
 159        struct priv_data *priv;
 160
 161        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 162                return -ENODEV;
 163
 164        chip = tpmm_chip_alloc(dev, &i2c_atmel);
 165        if (IS_ERR(chip))
 166                return PTR_ERR(chip);
 167
 168        priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
 169        if (!priv)
 170                return -ENOMEM;
 171
 172        /* Default timeouts */
 173        chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
 174        chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
 175        chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
 176        chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
 177
 178        dev_set_drvdata(&chip->dev, priv);
 179
 180        /* There is no known way to probe for this device, and all version
 181         * information seems to be read via TPM commands. Thus we rely on the
 182         * TPM startup process in the common code to detect the device. */
 183
 184        return tpm_chip_register(chip);
 185}
 186
 187static int i2c_atmel_remove(struct i2c_client *client)
 188{
 189        struct device *dev = &(client->dev);
 190        struct tpm_chip *chip = dev_get_drvdata(dev);
 191        tpm_chip_unregister(chip);
 192        return 0;
 193}
 194
 195static const struct i2c_device_id i2c_atmel_id[] = {
 196        {I2C_DRIVER_NAME, 0},
 197        {}
 198};
 199MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
 200
 201#ifdef CONFIG_OF
 202static const struct of_device_id i2c_atmel_of_match[] = {
 203        {.compatible = "atmel,at97sc3204t"},
 204        {},
 205};
 206MODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
 207#endif
 208
 209static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
 210
 211static struct i2c_driver i2c_atmel_driver = {
 212        .id_table = i2c_atmel_id,
 213        .probe = i2c_atmel_probe,
 214        .remove = i2c_atmel_remove,
 215        .driver = {
 216                .name = I2C_DRIVER_NAME,
 217                .pm = &i2c_atmel_pm_ops,
 218                .of_match_table = of_match_ptr(i2c_atmel_of_match),
 219        },
 220};
 221
 222module_i2c_driver(i2c_atmel_driver);
 223
 224MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
 225MODULE_DESCRIPTION("Atmel TPM I2C Driver");
 226MODULE_LICENSE("GPL");
 227