lkml.org 
[lkml]   [2012]   [Jun]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    From
    SubjectRe: [PATCH 1/2] char/tpm: Add new driver for Infineon I2C TIS TPM

    Signed-off-by: Marcel Selhorst <tpmdd@selhorst.net>

    Zitat von Peter Huewe <peter.huewe@infineon.com>:

    > This patch adds a driver to support Infineon's SLB 9635 TT 1.2 Soft I2C TPMs
    > which follow the TGC TIS 1.2 TPM specification[1] and Infineon's I2C Protocol
    > Stack Specification 0.20.
    > The I2C Protocol Stack Specification is a simple adaption of the LPC TIS
    > Protocol to the I2C Bus.
    > The I2C TPMs can be used when LPC Bus is not available (i.e. non x86
    > architectures like ARM).
    >
    > The driver is based on the tpm_tis.c driver by Leendert van Dorn and Kyleen
    > Hall and has quite similar functionality.
    >
    > Tested on Nvidia ARM Tegra2 Development Platform and Beagleboard (ARM OMAP)
    > Tested with the Trousers[2] TSS API Testsuite v 0.3 [3]
    > Compile-tested on x86 (32/64-bit)
    >
    > Updates since version 2.1.3:
    > - use proper probing mechanism
    > * either add the tpm using I2C_BOARD_INFO to your board file or probe it
    > * during runtime e.g on BeagleBoard using :
    > * "echo tpm_i2c_infineon 0x20 > /sys/bus/i2c/devices/i2c-2/new_device"
    > - fix possible endless loop if hardware misbehaves
    > - improved return codes
    > - consistent spelling i2c/tpm -> I2C/TPM
    > - remove hardcoded sleep values and msleep usage
    > - removed debug statements
    > - added check for I2C functionality
    > - renaming to tpm_i2c_infineon
    >
    > Updates since version 2.1.2:
    > - added sysfs entries for duration and timeouts
    > - updated to new tpm_do_selftest
    >
    > Updates since version 2.1.0:
    > - improved error handling
    > - implemented workarounds needed by the tpm
    > - fixed typos
    >
    > References:
    > [1]
    > http://www.trustedcomputinggroup.org/resources/pc_client_work_group_pc_client_
    > specific_tpm_interface_specification_tis_version_12/
    > [2] http://trousers.sourceforge.net/
    > [3]
    > http://sourceforge.net/projects/trousers/files/TSS%20API%20test%20suite/0.3/
    >
    > Signed-off-by: Peter Huewe <peter.huewe@infineon.com>
    > ---
    > drivers/char/tpm/Kconfig | 11 +
    > drivers/char/tpm/Makefile | 1 +
    > drivers/char/tpm/tpm_i2c_infineon.c | 711
    > +++++++++++++++++++++++++++++++++++
    > 3 files changed, 723 insertions(+), 0 deletions(-)
    > create mode 100644 drivers/char/tpm/tpm_i2c_infineon.c
    >
    > diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
    > index a048199..c4aac48 100644
    > --- a/drivers/char/tpm/Kconfig
    > +++ b/drivers/char/tpm/Kconfig
    > @@ -33,6 +33,17 @@ config TCG_TIS
    > from within Linux. To compile this driver as a module, choose
    > M here; the module will be called tpm_tis.
    >
    > +config TCG_TIS_I2C_INFINEON
    > + tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
    > + depends on I2C
    > + ---help---
    > + If you have a TPM security chip that is compliant with the
    > + TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack
    > + Specification 0.20 say Yes and it will be accessible from within
    > + Linux.
    > + To compile this driver as a module, choose M here; the module
    > + will be called tpm_tis_i2c_infineon.
    > +
    > config TCG_NSC
    > tristate "National Semiconductor TPM Interface"
    > depends on X86
    > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
    > index ea3a1e0..a9c3afc 100644
    > --- a/drivers/char/tpm/Makefile
    > +++ b/drivers/char/tpm/Makefile
    > @@ -6,6 +6,7 @@ ifdef CONFIG_ACPI
    > obj-$(CONFIG_TCG_TPM) += tpm_bios.o
    > endif
    > obj-$(CONFIG_TCG_TIS) += tpm_tis.o
    > +obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
    > obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
    > obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
    > obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
    > diff --git a/drivers/char/tpm/tpm_i2c_infineon.c
    > b/drivers/char/tpm/tpm_i2c_infineon.c
    > new file mode 100644
    > index 0000000..54b9a5e
    > --- /dev/null
    > +++ b/drivers/char/tpm/tpm_i2c_infineon.c
    > @@ -0,0 +1,711 @@
    > +/*
    > + * Copyright (C) 2012 Infineon Technologies
    > + *
    > + * Authors:
    > + * Peter Huewe <peter.huewe@infineon.com>
    > + *
    > + * Device driver for TCG/TCPA TPM (trusted platform module).
    > + * Specifications at www.trustedcomputinggroup.org
    > + *
    > + * This device driver implements the TPM interface as defined in
    > + * the TCG TPM Interface Spec version 1.2, revision 1.0 and the
    > + * Infineon I2C Protocol Stack Specification v0.20.
    > + *
    > + * It is based on the original tpm_tis device driver from Leendert van
    > + * Dorn and Kyleen Hall.
    > + *
    > + * This program is free software; you can redistribute it and/or
    > + * modify it under the terms of the GNU General Public License as
    > + * published by the Free Software Foundation, version 2 of the
    > + * License.
    > + *
    > + *
    > + */
    > +#include <linux/init.h>
    > +#include <linux/i2c.h>
    > +#include <linux/module.h>
    > +#include <linux/moduleparam.h>
    > +#include <linux/wait.h>
    > +#include "tpm.h"
    > +
    > +/* max. buffer size supported by our TPM */
    > +#define TPM_BUFSIZE 1260
    > +
    > +/* max. number of iterations after I2C NAK */
    > +#define MAX_COUNT 3
    > +
    > +#define SLEEP_DURATION_LOW 55
    > +#define SLEEP_DURATION_HI 65
    > +
    > +/* max. number of iterations after I2C NAK for 'long' commands
    > + * we need this especially for sending TPM_READY, since the cleanup
    > after the
    > + * transtion to the ready state may take some time, but it is unpredictable
    > + * how long it will take.
    > + */
    > +#define MAX_COUNT_LONG 50
    > +
    > +#define SLEEP_DURATION_LONG_LOW 200
    > +#define SLEEP_DURATION_LONG_HI 220
    > +
    > +/* After sending TPM_READY to 'reset' the TPM we have to sleep even
    > longer */
    > +#define SLEEP_DURATION_RESET_LOW 2400
    > +#define SLEEP_DURATION_RESET_HI 2600
    > +
    > +/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */
    > +#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000)
    > +#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000)
    > +
    > +/* expected value for DIDVID register */
    > +#define TPM_TIS_I2C_DID_VID 0x000b15d1L
    > +
    > +/* Structure to store I2C TPM specific stuff */
    > +struct tpm_inf_dev {
    > + struct i2c_client *client;
    > + u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
    > + struct tpm_chip *chip;
    > +};
    > +
    > +static struct tpm_inf_dev tpm_dev;
    > +static struct i2c_driver tpm_tis_i2c_driver;
    > +
    > +/*
    > + * iic_tpm_read() - read from TPM register
    > + * @addr: register address to read from
    > + * @buffer: provided by caller
    > + * @len: number of bytes to read
    > + *
    > + * Read len bytes from TPM register and put them into
    > + * buffer (little-endian format, i.e. first byte is put into buffer[0]).
    > + *
    > + * NOTE: TPM is big-endian for multi-byte values. Multi-byte
    > + * values have to be swapped.
    > + *
    > + * NOTE: We can't unfortunately use the combined read/write functions
    > + * provided by the i2c core as the TPM currently does not support the
    > + * repeated start condition and due to it's special requirements.
    > + * The i2c_smbus* functions do not work for this chip.
    > + *
    > + * Return -EIO on error, 0 on success.
    > + */
    > +static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
    > +{
    > +
    > + struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr };
    > + struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer };
    > +
    > + int rc;
    > + int count;
    > +
    > + for (count = 0; count < MAX_COUNT; count++) {
    > + rc = i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
    > + if (rc > 0)
    > + break; /* break here to skip sleep */
    > +
    > + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
    > + }
    > +
    > + if (rc <= 0)
    > + return -EIO;
    > +
    > + /* After the TPM has successfully received the register address it needs
    > + * some time, thus we're sleeping here again, before retrieving the data
    > + */
    > + for (count = 0; count < MAX_COUNT; count++) {
    > + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
    > + rc = i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
    > + if (rc > 0)
    > + break;
    > +
    > + }
    > +
    > + if (rc <= 0)
    > + return -EIO;
    > +
    > + return 0;
    > +}
    > +
    > +static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
    > + unsigned int sleep_low,
    > + unsigned int sleep_hi, u8 max_count)
    > +{
    > + int rc = -EIO;
    > + int count;
    > +
    > + struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf };
    > +
    > + if (len > TPM_BUFSIZE)
    > + return -EINVAL;
    > +
    > + /* prepend the 'register address' to the buffer */
    > + tpm_dev.buf[0] = addr;
    > + memcpy(&(tpm_dev.buf[1]), buffer, len);
    > +
    > + /*
    > + * NOTE: We have to use these special mechanisms here and unfortunately
    > + * cannot rely on the standard behavior of i2c_transfer.
    > + */
    > + for (count = 0; count < max_count; count++) {
    > + rc = i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
    > + if (rc > 0)
    > + break;
    > +
    > + usleep_range(sleep_low, sleep_hi);
    > + }
    > +
    > + if (rc <= 0)
    > + return -EIO;
    > +
    > + return 0;
    > +}
    > +
    > +/*
    > + * iic_tpm_write() - write to TPM register
    > + * @addr: register address to write to
    > + * @buffer: containing data to be written
    > + * @len: number of bytes to write
    > + *
    > + * Write len bytes from provided buffer to TPM register (little
    > + * endian format, i.e. buffer[0] is written as first byte).
    > + *
    > + * NOTE: TPM is big-endian for multi-byte values. Multi-byte
    > + * values have to be swapped.
    > + *
    > + * NOTE: use this function instead of the iic_tpm_write_generic function.
    > + *
    > + * Return -EIO on error, 0 on success
    > + */
    > +static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
    > +{
    > + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW,
    > + SLEEP_DURATION_HI, MAX_COUNT);
    > +}
    > +
    > +/*
    > + * This function is needed especially for the cleanup situation after
    > + * sending TPM_READY
    > + * */
    > +static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
    > +{
    > + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW,
    > + SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG);
    > +}
    > +
    > +enum tis_access {
    > + TPM_ACCESS_VALID = 0x80,
    > + TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
    > + TPM_ACCESS_REQUEST_PENDING = 0x04,
    > + TPM_ACCESS_REQUEST_USE = 0x02,
    > +};
    > +
    > +enum tis_status {
    > + TPM_STS_VALID = 0x80,
    > + TPM_STS_COMMAND_READY = 0x40,
    > + TPM_STS_GO = 0x20,
    > + TPM_STS_DATA_AVAIL = 0x10,
    > + TPM_STS_DATA_EXPECT = 0x08,
    > +};
    > +
    > +enum tis_defaults {
    > + TIS_SHORT_TIMEOUT = 750, /* ms */
    > + TIS_LONG_TIMEOUT = 2000, /* 2 sec */
    > +};
    > +
    > +#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
    > +#define TPM_STS(l) (0x0001 | ((l) << 4))
    > +#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
    > +#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
    > +
    > +static int check_locality(struct tpm_chip *chip, int loc)
    > +{
    > + u8 buf;
    > + int rc;
    > +
    > + rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
    > + if (rc < 0)
    > + return rc;
    > +
    > + if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
    > + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
    > + chip->vendor.locality = loc;
    > + return loc;
    > + }
    > +
    > + return -EIO;
    > +}
    > +
    > +/* implementation similar to tpm_tis */
    > +static void release_locality(struct tpm_chip *chip, int loc, int force)
    > +{
    > + u8 buf;
    > + if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
    > + return;
    > +
    > + if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
    > + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
    > + buf = TPM_ACCESS_ACTIVE_LOCALITY;
    > + iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
    > + }
    > +}
    > +
    > +static int request_locality(struct tpm_chip *chip, int loc)
    > +{
    > + unsigned long stop;
    > + u8 buf = TPM_ACCESS_REQUEST_USE;
    > +
    > + if (check_locality(chip, loc) >= 0)
    > + return loc;
    > +
    > + iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
    > +
    > + /* wait for burstcount */
    > + stop = jiffies + chip->vendor.timeout_a;
    > + do {
    > + if (check_locality(chip, loc) >= 0)
    > + return loc;
    > + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
    > + } while (time_before(jiffies, stop));
    > +
    > + return -ETIME;
    > +}
    > +
    > +static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
    > +{
    > + /* NOTE: since I2C read may fail, return 0 in this case --> time-out */
    > + u8 buf;
    > + if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
    > + return 0;
    > + else
    > + return buf;
    > +}
    > +
    > +static void tpm_tis_i2c_ready(struct tpm_chip *chip)
    > +{
    > + /* this causes the current command to be aborted */
    > + u8 buf = TPM_STS_COMMAND_READY;
    > + iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
    > +}
    > +
    > +static ssize_t get_burstcount(struct tpm_chip *chip)
    > +{
    > + unsigned long stop;
    > + ssize_t burstcnt;
    > + u8 buf[3];
    > +
    > + /* wait for burstcount */
    > + /* which timeout value, spec has 2 answers (c & d) */
    > + stop = jiffies + chip->vendor.timeout_d;
    > + do {
    > + /* Note: STS is little endian */
    > + if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
    > + burstcnt = 0;
    > + else
    > + burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
    > +
    > + if (burstcnt)
    > + return burstcnt;
    > +
    > + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
    > + } while (time_before(jiffies, stop));
    > + return -EBUSY;
    > +}
    > +
    > +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned
    > long timeout,
    > + int *status)
    > +{
    > + unsigned long stop;
    > +
    > + /* check current status */
    > + *status = tpm_tis_i2c_status(chip);
    > + if ((*status & mask) == mask)
    > + return 0;
    > +
    > + stop = jiffies + timeout;
    > + do {
    > + /* since we just checked the status, give the TPM some time */
    > + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
    > + *status = tpm_tis_i2c_status(chip);
    > + if ((*status & mask) == mask)
    > + return 0;
    > +
    > + } while (time_before(jiffies, stop));
    > +
    > + return -ETIME;
    > +}
    > +
    > +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
    > +{
    > + size_t size = 0;
    > + ssize_t burstcnt;
    > + u8 retries = 0;
    > + int rc;
    > +
    > + while (size < count) {
    > + burstcnt = get_burstcount(chip);
    > +
    > + /* burstcnt < 0 = TPM is busy */
    > + if (burstcnt < 0)
    > + return burstcnt;
    > +
    > + /* limit received data to max. left */
    > + if (burstcnt > (count - size))
    > + burstcnt = count - size;
    > +
    > + rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
    > + &(buf[size]), burstcnt);
    > + if (rc == 0)
    > + size += burstcnt;
    > + else if (rc < 0)
    > + retries++;
    > +
    > + /* avoid endless loop in case of broken HW */
    > + if (retries > MAX_COUNT_LONG)
    > + return -EIO;
    > +
    > + }
    > + return size;
    > +}
    > +
    > +static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
    > +{
    > + int size = 0;
    > + int expected, status;
    > +
    > + if (count < TPM_HEADER_SIZE) {
    > + size = -EIO;
    > + goto out;
    > + }
    > +
    > + /* read first 10 bytes, including tag, paramsize, and result */
    > + size = recv_data(chip, buf, TPM_HEADER_SIZE);
    > + if (size < TPM_HEADER_SIZE) {
    > + dev_err(chip->dev, "Unable to read header\n");
    > + goto out;
    > + }
    > +
    > + expected = be32_to_cpu(*(__be32 *)(buf + 2));
    > + if ((size_t) expected > count) {
    > + size = -EIO;
    > + goto out;
    > + }
    > +
    > + size += recv_data(chip, &buf[TPM_HEADER_SIZE],
    > + expected - TPM_HEADER_SIZE);
    > + if (size < expected) {
    > + dev_err(chip->dev, "Unable to read remainder of result\n");
    > + size = -ETIME;
    > + goto out;
    > + }
    > +
    > + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
    > + if (status & TPM_STS_DATA_AVAIL) { /* retry? */
    > + dev_err(chip->dev, "Error left over data\n");
    > + size = -EIO;
    > + goto out;
    > + }
    > +
    > +out:
    > + tpm_tis_i2c_ready(chip);
    > + /* The TPM needs some time to clean up here,
    > + * so we sleep rather than keeping the bus busy
    > + */
    > + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
    > + release_locality(chip, chip->vendor.locality, 0);
    > + return size;
    > +}
    > +
    > +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
    > +{
    > + int rc, status;
    > + ssize_t burstcnt;
    > + size_t count = 0;
    > + u8 retries = 0;
    > + u8 sts = TPM_STS_GO;
    > +
    > + if (len > TPM_BUFSIZE)
    > + return -E2BIG; /* command is too long for our tpm, sorry */
    > +
    > + if (request_locality(chip, 0) < 0)
    > + return -EBUSY;
    > +
    > + status = tpm_tis_i2c_status(chip);
    > + if ((status & TPM_STS_COMMAND_READY) == 0) {
    > + tpm_tis_i2c_ready(chip);
    > + if (wait_for_stat
    > + (chip, TPM_STS_COMMAND_READY,
    > + chip->vendor.timeout_b, &status) < 0) {
    > + rc = -ETIME;
    > + goto out_err;
    > + }
    > + }
    > +
    > + while (count < len - 1) {
    > + burstcnt = get_burstcount(chip);
    > +
    > + /* burstcnt < 0 = TPM is busy */
    > + if (burstcnt < 0)
    > + return burstcnt;
    > +
    > + if (burstcnt > (len - 1 - count))
    > + burstcnt = len - 1 - count;
    > +
    > + rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
    > + &(buf[count]), burstcnt);
    > + if (rc == 0)
    > + count += burstcnt;
    > + else if (rc < 0)
    > + retries++;
    > +
    > + /* avoid endless loop in case of broken HW */
    > + if (retries > MAX_COUNT_LONG) {
    > + rc = -EIO;
    > + goto out_err;
    > + }
    > +
    > + wait_for_stat(chip, TPM_STS_VALID,
    > + chip->vendor.timeout_c, &status);
    > +
    > + if ((status & TPM_STS_DATA_EXPECT) == 0) {
    > + rc = -EIO;
    > + goto out_err;
    > + }
    > +
    > + }
    > +
    > + /* write last byte */
    > + iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
    > + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
    > + if ((status & TPM_STS_DATA_EXPECT) != 0) {
    > + rc = -EIO;
    > + goto out_err;
    > + }
    > +
    > + /* go and do it */
    > + iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
    > +
    > + return len;
    > +out_err:
    > + tpm_tis_i2c_ready(chip);
    > + /* The TPM needs some time to clean up here,
    > + * so we sleep rather than keeping the bus busy
    > + */
    > + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
    > + release_locality(chip, chip->vendor.locality, 0);
    > + return rc;
    > +}
    > +
    > +static const struct file_operations tis_ops = {
    > + .owner = THIS_MODULE,
    > + .llseek = no_llseek,
    > + .open = tpm_open,
    > + .read = tpm_read,
    > + .write = tpm_write,
    > + .release = tpm_release,
    > +};
    > +
    > +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
    > +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
    > +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
    > +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
    > +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
    > +static DEVICE_ATTR(temp_deactivated, S_IRUGO,
    > tpm_show_temp_deactivated, NULL);
    > +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
    > +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
    > +static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
    > +static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
    > +
    > +static struct attribute *tis_attrs[] = {
    > + &dev_attr_pubek.attr,
    > + &dev_attr_pcrs.attr,
    > + &dev_attr_enabled.attr,
    > + &dev_attr_active.attr,
    > + &dev_attr_owned.attr,
    > + &dev_attr_temp_deactivated.attr,
    > + &dev_attr_caps.attr,
    > + &dev_attr_cancel.attr,
    > + &dev_attr_durations.attr,
    > + &dev_attr_timeouts.attr,
    > + NULL,
    > +};
    > +
    > +static struct attribute_group tis_attr_grp = {
    > + .attrs = tis_attrs
    > +};
    > +
    > +static struct tpm_vendor_specific tpm_tis_i2c = {
    > + .status = tpm_tis_i2c_status,
    > + .recv = tpm_tis_i2c_recv,
    > + .send = tpm_tis_i2c_send,
    > + .cancel = tpm_tis_i2c_ready,
    > + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
    > + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
    > + .req_canceled = TPM_STS_COMMAND_READY,
    > + .attr_group = &tis_attr_grp,
    > + .miscdev.fops = &tis_ops,
    > +};
    > +
    > +static int __devinit tpm_tis_i2c_init(struct device *dev)
    > +{
    > + u32 vendor;
    > + int rc = 0;
    > + struct tpm_chip *chip;
    > +
    > + chip = tpm_register_hardware(dev, &tpm_tis_i2c);
    > + if (!chip) {
    > + rc = -ENODEV;
    > + goto out_err;
    > + }
    > +
    > + /* Disable interrupts */
    > + chip->vendor.irq = 0;
    > +
    > + /* Default timeouts */
    > + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
    > + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
    > + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
    > + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
    > +
    > + if (request_locality(chip, 0) != 0) {
    > + rc = -ENODEV;
    > + goto out_vendor;
    > + }
    > +
    > + /* read four bytes from DID_VID register */
    > + if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) {
    > + rc = -EIO;
    > + goto out_release;
    > + }
    > +
    > + /* create DID_VID register value, after swapping to little-endian */
    > + vendor = be32_to_cpu((__be32) vendor);
    > +
    > + if (vendor != TPM_TIS_I2C_DID_VID) {
    > + rc = -ENODEV;
    > + goto out_release;
    > + }
    > +
    > + dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
    > +
    > + INIT_LIST_HEAD(&chip->vendor.list);
    > + tpm_dev.chip = chip;
    > +
    > + tpm_get_timeouts(chip);
    > + tpm_do_selftest(chip);
    > +
    > + return 0;
    > +
    > +out_release:
    > + release_locality(chip, chip->vendor.locality, 1);
    > +
    > +out_vendor:
    > + /* close file handles */
    > + tpm_dev_vendor_release(chip);
    > +
    > + /* remove hardware */
    > + tpm_remove_hardware(chip->dev);
    > +
    > + /* reset these pointers, otherwise we oops */
    > + chip->dev->release = NULL;
    > + chip->release = NULL;
    > + tpm_dev.client = NULL;
    > + dev_set_drvdata(chip->dev, chip);
    > +out_err:
    > + return rc;
    > +}
    > +
    > +static const struct i2c_device_id tpm_tis_i2c_table[] = {
    > + {"tpm_i2c_infineon", 0},
    > + {},
    > +};
    > +
    > +MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
    > +
    > +#ifdef CONFIG_PM
    > +/* NOTE:
    > + * Suspend does currently not work Nvidias Tegra2 Platform
    > + * but works fine on Beagleboard (arm omap).
    > + *
    > + * This function will block System Suspend if TPM is not initialized,
    > + * however the TPM is usually initialized by BIOS/u-boot or by sending
    > + * a TPM_Startup command.
    > + */
    > +static int tpm_tis_i2c_suspend(struct device *dev)
    > +{
    > + return tpm_pm_suspend(dev, dev->power.power_state);
    > +}
    > +
    > +static int tpm_tis_i2c_resume(struct device *dev)
    > +{
    > + return tpm_pm_resume(dev);
    > +}
    > +
    > +static const struct dev_pm_ops tpm_tis_i2c_ops = {
    > + .suspend = tpm_tis_i2c_suspend,
    > + .resume = tpm_tis_i2c_resume,
    > +};
    > +#else
    > +#define tpm_tis_i2c_suspend NULL
    > +#define tpm_tis_i2c_resume NULL
    > +#define tpm_tis_i2c_ops NULL
    > +#endif
    > +
    > +static int __devinit tpm_tis_i2c_probe(struct i2c_client *client,
    > + const struct i2c_device_id *id)
    > +{
    > + int rc;
    > + if (tpm_dev.client != NULL)
    > + return -EBUSY; /* We only support one client */
    > +
    > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    > + dev_err(&client->dev,
    > + "no algorithms associated to the i2c bus\n");
    > + return -ENODEV;
    > + }
    > +
    > + client->driver = &tpm_tis_i2c_driver;
    > + tpm_dev.client = client;
    > + rc = tpm_tis_i2c_init(&client->dev);
    > + if (rc != 0) {
    > + client->driver = NULL;
    > + tpm_dev.client = NULL;
    > + rc = -ENODEV;
    > + }
    > + return rc;
    > +}
    > +
    > +static int __devexit tpm_tis_i2c_remove(struct i2c_client *client)
    > +{
    > + struct tpm_chip *chip = tpm_dev.chip;
    > + release_locality(chip, chip->vendor.locality, 1);
    > +
    > + /* close file handles */
    > + tpm_dev_vendor_release(chip);
    > +
    > + /* remove hardware */
    > + tpm_remove_hardware(chip->dev);
    > +
    > + /* reset these pointers, otherwise we oops */
    > + chip->dev->release = NULL;
    > + chip->release = NULL;
    > + tpm_dev.client = NULL;
    > + dev_set_drvdata(chip->dev, chip);
    > +
    > + return 0;
    > +}
    > +
    > +static struct i2c_driver tpm_tis_i2c_driver = {
    > +
    > + .id_table = tpm_tis_i2c_table,
    > + .probe = tpm_tis_i2c_probe,
    > + .remove = tpm_tis_i2c_remove,
    > + .driver = {
    > + .name = "tpm_i2c_infineon",
    > + .owner = THIS_MODULE,
    > + .pm = &tpm_tis_i2c_ops,
    > + },
    > +};
    > +
    > +module_i2c_driver(tpm_tis_i2c_driver);
    > +MODULE_AUTHOR("Peter Huewe <peter.huewe@infineon.com>");
    > +MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver");
    > +MODULE_VERSION("2.1.4");
    > +MODULE_LICENSE("GPL");
    > --
    > 1.7.6.msysgit.0





    \
     
     \ /
      Last update: 2012-06-13 10:21    [W:0.083 / U:30.648 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site