* [PATCH 7/7] tpm: Driver for next generation TPM chips
@ 2006-04-03 16:42 Kylene Jo Hall
0 siblings, 0 replies; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-03 16:42 UTC (permalink / raw)
To: linux-kernel
Cc: akpm, TPM Device Driver List, Leendert Van Doorn, Marcel Selhorst
This patch contains the driver for the next generation of TPM chips
version 1.2 including support for interrupts. The Trusted Computing
Group has written the TPM Interface Specification (TIS) which defines a
common interface for all manufacturer's 1.2 TPM's thus the name
tpm_tis.
Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
drivers/char/tpm/Kconfig | 11
drivers/char/tpm/Makefile | 1
drivers/char/tpm/tpm.c | 3
drivers/char/tpm/tpm.h | 9
drivers/char/tpm/tpm_tis.c | 628 +++++++++++++++++++++++++++++++++++
5 files changed, 651 insertions(+), 1 deletion(-)
--- linux-2.6.16/drivers/char/tpm/tpm.h 2006-03-30 17:08:49.315065750 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm.h 2006-03-29 14:16:30.119053500 -0600
@@ -57,6 +57,8 @@ struct tpm_vendor_specific {
void __iomem *iobase; /* ioremapped address */
unsigned long base; /* TPM base address */
+ int irq;
+
int region_size;
int have_region;
@@ -66,8 +68,13 @@ struct tpm_vendor_specific {
u8 (*status) (struct tpm_chip *);
struct miscdevice miscdev;
struct attribute_group *attr_group;
+ struct list_head list;
+ int locality;
u32 timeout_a, timeout_b, timeout_c, timeout_d;
u32 duration[3];
+
+ wait_queue_head_t read_queue;
+ wait_queue_head_t int_queue;
};
struct tpm_chip {
@@ -93,6 +100,8 @@ struct tpm_chip {
struct list_head list;
};
+#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
+
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
--- linux-2.6.16/drivers/char/tpm/tpm.c 2006-03-30 17:08:49.315065750 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm.c 2006-03-30 16:51:48.567273000 -0600
@@ -391,6 +391,9 @@ static ssize_t tpm_transmit(struct tpm_c
goto out;
}
+ if (chip->vendor.irq)
+ goto out_recv;
+
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
do {
u8 status = chip->vendor.status(chip);
--- linux-2.6.16/drivers/char/tpm/tpm_tis.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm_tis.c 2006-03-30 10:44:31.874065750 -0600
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.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.
+ *
+ * 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/pnp.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include "tpm.h"
+
+#define TPM_HEADER_SIZE 10
+
+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_int_flags {
+ TPM_GLOBAL_INT_ENABLE = 0x80000000,
+ TPM_INTF_BURST_COUNT_STATIC = 0x100,
+ TPM_INTF_CMD_READY_INT = 0x080,
+ TPM_INTF_INT_EDGE_FALLING = 0x040,
+ TPM_INTF_INT_EDGE_RISING = 0x020,
+ TPM_INTF_INT_LEVEL_LOW = 0x010,
+ TPM_INTF_INT_LEVEL_HIGH = 0x008,
+ TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+ TPM_INTF_STS_VALID_INT = 0x002,
+ TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
+#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
+#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
+#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
+#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
+#define TPM_STS(l) (0x0018 | ((l) << 12))
+#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
+
+#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
+#define TPM_RID(l) (0x0F04 | ((l) << 12))
+
+static LIST_HEAD(tis_chips);
+static DEFINE_SPINLOCK(tis_lock);
+
+static int check_locality(struct tpm_chip *chip, int l)
+{
+ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+ return chip->vendor.locality = l;
+
+ return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int l)
+{
+ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
+ iowrite8(TPM_ACCESS_ACTIVE_LOCALITY,
+ chip->vendor.iobase + TPM_ACCESS(l));
+}
+
+static int request_locality(struct tpm_chip *chip, int l)
+{
+ unsigned long stop;
+
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ iowrite8(TPM_ACCESS_REQUEST_USE,
+ chip->vendor.iobase + TPM_ACCESS(l));
+
+ if (chip->vendor.irq) {
+ interruptible_sleep_on_timeout(&chip->vendor.int_queue,
+ HZ *
+ chip->vendor.timeout_a /
+ 1000);
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ } else {
+ /* wait for burstcount */
+ stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
+ do {
+ if (check_locality(chip, l) >= 0)
+ return l;
+ msleep(TPM_TIMEOUT);
+ }
+ while (time_before(jiffies, stop));
+ }
+ release_locality(chip, l);
+ return -1;
+}
+
+static u8 tpm_tis_status(struct tpm_chip *chip)
+{
+ return ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality));
+}
+
+static void tpm_tis_ready(struct tpm_chip *chip)
+{
+ /* this causes the current command to be aborted */
+ iowrite8(TPM_STS_COMMAND_READY,
+ chip->vendor.iobase + TPM_STS(chip->vendor.locality));
+}
+
+static int get_burstcount(struct tpm_chip *chip)
+{
+ unsigned long stop;
+ int burstcnt;
+
+ /* wait for burstcount */
+ /* which timeout value, spec has 2 answers (c & d) */
+ stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
+ do {
+ burstcnt = ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality) + 1);
+ burstcnt += ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality) +
+ 2) << 8;
+ if (burstcnt)
+ return burstcnt;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ return -EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout,
+ wait_queue_head_t * queue)
+{
+ unsigned long stop;
+ u8 status;
+
+ /* check current status */
+ status = tpm_tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+
+ if (chip->vendor.irq) {
+ interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
+ status = tpm_tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+ } else {
+ stop = jiffies + (HZ * timeout / 1000);
+ do {
+ msleep(TPM_TIMEOUT);
+ status = tpm_tis_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)
+{
+ int size = 0, burstcnt;
+ while (size < count &&
+ wait_for_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ chip->vendor.timeout_c,
+ &chip->vendor.read_queue)
+ == 0) {
+ burstcnt = get_burstcount(chip);
+ for (; burstcnt > 0 && size < count; burstcnt--)
+ buf[size++] = ioread8(chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.
+ locality));
+ }
+ return size;
+}
+
+static int tpm_tis_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 */
+ if ((size =
+ recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
+ dev_err(chip->dev, "Unable to read header\n");
+ goto out;
+ }
+
+ expected = be32_to_cpu(*(__be32 *) (buf + 2));
+ if (expected > count) {
+ size = -EIO;
+ goto out;
+ }
+
+ if ((size +=
+ recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_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,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ dev_err(chip->dev, "Error left over data\n");
+ size = -EIO;
+ goto out;
+ }
+
+out:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality);
+ return size;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len)
+{
+ int rc, status, burstcnt;
+ size_t count = 0;
+ u32 ordinal;
+
+ if (request_locality(chip, 0) < 0)
+ return -EBUSY;
+
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_tis_ready(chip);
+ if (wait_for_stat
+ (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
+ &chip->vendor.int_queue) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+
+ while (count < len - 1) {
+ burstcnt = get_burstcount(chip);
+ for (; burstcnt > 0 && count < len - 1; burstcnt--) {
+ iowrite8(buf[count], chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.locality));
+ count++;
+ }
+
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+
+ /* write last byte */
+ iowrite8(buf[count],
+ chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.locality));
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ /* go and do it */
+ iowrite8(TPM_STS_GO,
+ chip->vendor.iobase + TPM_STS(chip->vendor.locality));
+
+ if (chip->vendor.irq) {
+ ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+ if (wait_for_stat
+ (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ tpm_calc_ordinal_duration(chip, ordinal),
+ &chip->vendor.read_queue) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+ return len;
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality);
+ return rc;
+}
+
+static 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(state, S_IRUGO, tpm_show_state, 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 struct attribute *tis_attrs[] = {
+ &dev_attr_pubek.attr, &dev_attr_pcrs.attr,
+ &dev_attr_state.attr, &dev_attr_caps.attr,
+ &dev_attr_cancel.attr, NULL,
+};
+
+static struct attribute_group tis_attr_grp = {
+ .attrs = tis_attrs
+};
+
+static struct tpm_vendor_specific tpm_tis = {
+ .status = tpm_tis_status,
+ .recv = tpm_tis_recv,
+ .send = tpm_tis_send,
+ .cancel = tpm_tis_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 irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs
+ *regs)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+ u32 interrupt;
+
+ interrupt = ioread32(chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ chip->vendor.irq = irq;
+
+ /* Clear interrupts handled with TPM_EOI */
+ iowrite32(interrupt,
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs
+ *regs)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+ u32 interrupt;
+ int i;
+
+ interrupt = ioread32(chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ if (interrupt & TPM_INTF_DATA_AVAIL_INT)
+ wake_up_interruptible(&chip->vendor.read_queue);
+ if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
+ for (i = 0; i < 5; i++)
+ if (check_locality(chip, i) >= 0)
+ break;
+ if (interrupt &
+ (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+ TPM_INTF_CMD_READY_INT))
+ wake_up_interruptible(&chip->vendor.int_queue);
+
+ /* Clear interrupts handled with TPM_EOI */
+ iowrite32(interrupt,
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+ return IRQ_HANDLED;
+}
+
+static int __devinit tpm_tis_pnp_init(struct pnp_dev
+ *pnp_dev, const struct
+ pnp_device_id
+ *pnp_id)
+{
+ u32 vendor, intfcaps, intmask;
+ int rc, i;
+ unsigned long start, len;
+ struct tpm_chip *chip;
+
+ start = pnp_mem_start(pnp_dev, 0);
+ len = pnp_mem_len(pnp_dev, 0);
+
+ if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis)))
+ return -ENODEV;
+
+ chip->vendor.iobase = ioremap(start, len);
+ if (!chip->vendor.iobase) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+ if ((vendor & 0xFFFF) == 0xFFFF) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* Default timeouts */
+ chip->vendor.timeout_a = 750; /* ms */
+ chip->vendor.timeout_b = 2000; /* 2 sec */
+ chip->vendor.timeout_c = 750; /* ms */
+ chip->vendor.timeout_d = 750; /* ms */
+
+ dev_info(&pnp_dev->dev,
+ "1.2 TPM (device-id 0x%X, rev-id %d)\n",
+ vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+
+ /* Figure out the capabilities */
+ intfcaps =
+ ioread32(chip->vendor.iobase +
+ TPM_INTF_CAPS(chip->vendor.locality));
+ dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n",
+ intfcaps);
+ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+ dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n");
+ if (intfcaps & TPM_INTF_CMD_READY_INT)
+ dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n");
+ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
+ dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n");
+ if (intfcaps & TPM_INTF_STS_VALID_INT)
+ dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n");
+ if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
+ dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n");
+
+ if (request_locality(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* INTERRUPT Setup */
+ init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&chip->vendor.int_queue);
+
+ intmask =
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+ | TPM_INTF_STS_VALID_INT;
+
+ iowrite32(intmask,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ chip->vendor.irq =
+ ioread8(chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+
+ for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+ iowrite8(i,
+ chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+ if (request_irq
+ (i, tis_int_probe, SA_SHIRQ,
+ chip->vendor.miscdev.name, chip) != 0) {
+ dev_info(chip->dev,
+ "Unable to request irq: %d for probe\n",
+ i);
+ continue;
+ }
+
+ /* Clear all existing */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ /* Turn on */
+ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ /* Generate Interrupts */
+ tpm_gen_interrupt(chip);
+
+ /* Turn off */
+ iowrite32(intmask,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ free_irq(i, chip);
+ }
+ if (chip->vendor.irq) {
+ iowrite8(chip->vendor.irq,
+ chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+ if (request_irq
+ (chip->vendor.irq, tis_int_handler, SA_SHIRQ,
+ chip->vendor.miscdev.name, chip) != 0) {
+ dev_info(chip->dev,
+ "Unable to request irq: %d for use\n", i);
+ chip->vendor.irq = 0;
+ } else {
+ /* Clear all existing */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ /* Turn on */
+ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ }
+ }
+
+ INIT_LIST_HEAD(&chip->vendor.list);
+ spin_lock(&tis_lock);
+ list_add(&chip->vendor.list, &tis_chips);
+ spin_unlock(&tis_lock);
+
+ tpm_get_timeouts(chip);
+
+ return 0;
+out_err:
+ if (chip->vendor.iobase)
+ iounmap(chip->vendor.iobase);
+ tpm_remove_hardware(chip->dev);
+ return rc;
+}
+
+static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
+{
+ return tpm_pm_suspend(&dev->dev, msg);
+}
+
+static int tpm_tis_pnp_resume(struct pnp_dev *dev)
+{
+ return tpm_pm_resume(&dev->dev);
+}
+
+static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
+ {"PNP0C31", 0}, /* TPM */
+ {"", 0}
+};
+
+static struct pnp_driver tis_pnp_driver = {
+ .name = "tpm_tis",
+ .id_table = tpm_pnp_tbl,
+ .probe = tpm_tis_pnp_init,
+ .suspend = tpm_tis_pnp_suspend,
+ .resume = tpm_tis_pnp_resume,
+};
+
+static int __init init_tis(void)
+{
+ return pnp_register_driver(&tis_pnp_driver);
+}
+
+static void __exit cleanup_tis(void)
+{
+ struct tpm_vendor_specific *i, *j;
+ struct tpm_chip *chip;
+ spin_lock(&tis_lock);
+ list_for_each_entry_safe(i, j, &tis_chips, list) {
+ chip = to_tpm_chip(i);
+ iowrite32(~TPM_GLOBAL_INT_ENABLE &
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.
+ locality)),
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ if (chip->vendor.irq)
+ free_irq(chip->vendor.irq, chip);
+ iounmap(i->iobase);
+ list_del(&i->list);
+ tpm_remove_hardware(chip->dev);
+ }
+ spin_unlock(&tis_lock);
+ pnp_unregister_driver(&tis_pnp_driver);
+}
+
+module_init(init_tis);
+module_exit(cleanup_tis);
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
--- linux-2.6.16/drivers/char/tpm/Makefile 2006-03-19 23:53:29.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/Makefile 2006-03-02 16:20:06.002087500 -0600
@@ -5,6 +5,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o
ifdef CONFIG_ACPI
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
endif
+obj-$(CONFIG_TCG_TIS) += tpm_tis.o
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
--- linux-2.6.16/drivers/char/tpm/Kconfig 2006-03-19 23:53:29.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/Kconfig 2006-03-02 16:19:05.730320750 -0600
@@ -20,9 +20,18 @@ config TCG_TPM
Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI
and CONFIG_PNPACPI.
+config TCG_TIS
+ tristate "TPM Interface Specification 1.2 Interface"
+ depends on TCG_TPM
+ ---help---
+ If you have a TPM security chip that is compliant with the
+ TCG TIS 1.2 TPM specification 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.
+
config TCG_NSC
tristate "National Semiconductor TPM Interface"
- depends on TCG_TPM
+ depends on TCG_TPM && PNPACPI
---help---
If you have a TPM security chip from National Semicondutor
say Yes and it will be accessible from within Linux. To
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 7/7] tpm: Driver for next generation TPM chips
@ 2006-04-05 19:48 Kylene Jo Hall
0 siblings, 0 replies; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-05 19:48 UTC (permalink / raw)
To: linux-kernel
Cc: akpm, TPM Device Driver List, Marcel Selhorst, Leendert Van Doorn
This patch contains the driver for the next generation of TPM chips
version 1.2 including support for interrupts. The Trusted Computing
Group has written the TPM Interface Specification (TIS) which defines a
common interface for all manufacturer's 1.2 TPM's thus the name
tpm_tis.
This updated version of the patch uses the new sysfs files that came
about from the comments and changes in patch 6/7. It replaces the 7/7
patch from the original set.
Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
drivers/char/tpm/tpm_tis.c | 636 +++++++++++++++++++++++++++++++++++
drivers/char/tpm/Kconfig | 11
drivers/char/tpm/Makefile | 1
drivers/char/tpm/tpm.c | 3
drivers/char/tpm/tpm.h | 9
5 files changed, 659 insertions(+), 1 deletion(-)
--- linux-2.6.16/drivers/char/tpm/tpm.h 2006-03-30 17:08:49.315065750 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm.h 2006-03-29 14:16:30.119053500 -0600
@@ -57,6 +57,8 @@ struct tpm_vendor_specific {
void __iomem *iobase; /* ioremapped address */
unsigned long base; /* TPM base address */
+ int irq;
+
int region_size;
int have_region;
@@ -66,8 +68,13 @@ struct tpm_vendor_specific {
u8 (*status) (struct tpm_chip *);
struct miscdevice miscdev;
struct attribute_group *attr_group;
+ struct list_head list;
+ int locality;
u32 timeout_a, timeout_b, timeout_c, timeout_d;
u32 duration[3];
+
+ wait_queue_head_t read_queue;
+ wait_queue_head_t int_queue;
};
struct tpm_chip {
@@ -93,6 +100,8 @@ struct tpm_chip {
struct list_head list;
};
+#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
+
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
--- linux-2.6.16/drivers/char/tpm/tpm.c 2006-03-30 17:08:49.315065750 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm.c 2006-03-30 16:51:48.567273000 -0600
@@ -391,6 +391,9 @@ static ssize_t tpm_transmit(struct tpm_c
goto out;
}
+ if (chip->vendor.irq)
+ goto out_recv;
+
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
do {
u8 status = chip->vendor.status(chip);
--- linux-2.6.16/drivers/char/tpm/Makefile 2006-03-19 23:53:29.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/Makefile 2006-03-02 16:20:06.002087500 -0600
@@ -5,6 +5,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o
ifdef CONFIG_ACPI
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
endif
+obj-$(CONFIG_TCG_TIS) += tpm_tis.o
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
--- linux-2.6.16/drivers/char/tpm/Kconfig 2006-03-19 23:53:29.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/Kconfig 2006-03-02 16:19:05.730320750 -0600
@@ -20,9 +20,18 @@ config TCG_TPM
Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI
and CONFIG_PNPACPI.
+config TCG_TIS
+ tristate "TPM Interface Specification 1.2 Interface"
+ depends on TCG_TPM
+ ---help---
+ If you have a TPM security chip that is compliant with the
+ TCG TIS 1.2 TPM specification 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.
+
config TCG_NSC
tristate "National Semiconductor TPM Interface"
- depends on TCG_TPM
+ depends on TCG_TPM && PNPACPI
---help---
If you have a TPM security chip from National Semicondutor
say Yes and it will be accessible from within Linux. To
--- linux-2.6.16/drivers/char/tpm/tpm_tis.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.16-44/drivers/char/tpm/tpm_tis.c 2006-04-04 14:12:29.701783000 -0500
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.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.
+ *
+ * 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/pnp.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include "tpm.h"
+
+#define TPM_HEADER_SIZE 10
+
+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_int_flags {
+ TPM_GLOBAL_INT_ENABLE = 0x80000000,
+ TPM_INTF_BURST_COUNT_STATIC = 0x100,
+ TPM_INTF_CMD_READY_INT = 0x080,
+ TPM_INTF_INT_EDGE_FALLING = 0x040,
+ TPM_INTF_INT_EDGE_RISING = 0x020,
+ TPM_INTF_INT_LEVEL_LOW = 0x010,
+ TPM_INTF_INT_LEVEL_HIGH = 0x008,
+ TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+ TPM_INTF_STS_VALID_INT = 0x002,
+ TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
+#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
+#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
+#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
+#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
+#define TPM_STS(l) (0x0018 | ((l) << 12))
+#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
+
+#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
+#define TPM_RID(l) (0x0F04 | ((l) << 12))
+
+static LIST_HEAD(tis_chips);
+static DEFINE_SPINLOCK(tis_lock);
+
+static int check_locality(struct tpm_chip *chip, int l)
+{
+ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+ return chip->vendor.locality = l;
+
+ return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int l)
+{
+ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
+ iowrite8(TPM_ACCESS_ACTIVE_LOCALITY,
+ chip->vendor.iobase + TPM_ACCESS(l));
+}
+
+static int request_locality(struct tpm_chip *chip, int l)
+{
+ unsigned long stop;
+
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ iowrite8(TPM_ACCESS_REQUEST_USE,
+ chip->vendor.iobase + TPM_ACCESS(l));
+
+ if (chip->vendor.irq) {
+ interruptible_sleep_on_timeout(&chip->vendor.int_queue,
+ HZ *
+ chip->vendor.timeout_a /
+ 1000);
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ } else {
+ /* wait for burstcount */
+ stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
+ do {
+ if (check_locality(chip, l) >= 0)
+ return l;
+ msleep(TPM_TIMEOUT);
+ }
+ while (time_before(jiffies, stop));
+ }
+ release_locality(chip, l);
+ return -1;
+}
+
+static u8 tpm_tis_status(struct tpm_chip *chip)
+{
+ return ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality));
+}
+
+static void tpm_tis_ready(struct tpm_chip *chip)
+{
+ /* this causes the current command to be aborted */
+ iowrite8(TPM_STS_COMMAND_READY,
+ chip->vendor.iobase + TPM_STS(chip->vendor.locality));
+}
+
+static int get_burstcount(struct tpm_chip *chip)
+{
+ unsigned long stop;
+ int burstcnt;
+
+ /* wait for burstcount */
+ /* which timeout value, spec has 2 answers (c & d) */
+ stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
+ do {
+ burstcnt = ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality) + 1);
+ burstcnt += ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality) +
+ 2) << 8;
+ if (burstcnt)
+ return burstcnt;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ return -EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout,
+ wait_queue_head_t * queue)
+{
+ unsigned long stop;
+ u8 status;
+
+ /* check current status */
+ status = tpm_tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+
+ if (chip->vendor.irq) {
+ interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
+ status = tpm_tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+ } else {
+ stop = jiffies + (HZ * timeout / 1000);
+ do {
+ msleep(TPM_TIMEOUT);
+ status = tpm_tis_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)
+{
+ int size = 0, burstcnt;
+ while (size < count &&
+ wait_for_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ chip->vendor.timeout_c,
+ &chip->vendor.read_queue)
+ == 0) {
+ burstcnt = get_burstcount(chip);
+ for (; burstcnt > 0 && size < count; burstcnt--)
+ buf[size++] = ioread8(chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.
+ locality));
+ }
+ return size;
+}
+
+static int tpm_tis_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 */
+ if ((size =
+ recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
+ dev_err(chip->dev, "Unable to read header\n");
+ goto out;
+ }
+
+ expected = be32_to_cpu(*(__be32 *) (buf + 2));
+ if (expected > count) {
+ size = -EIO;
+ goto out;
+ }
+
+ if ((size +=
+ recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_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,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ dev_err(chip->dev, "Error left over data\n");
+ size = -EIO;
+ goto out;
+ }
+
+out:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality);
+ return size;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len)
+{
+ int rc, status, burstcnt;
+ size_t count = 0;
+ u32 ordinal;
+
+ if (request_locality(chip, 0) < 0)
+ return -EBUSY;
+
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_tis_ready(chip);
+ if (wait_for_stat
+ (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
+ &chip->vendor.int_queue) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+
+ while (count < len - 1) {
+ burstcnt = get_burstcount(chip);
+ for (; burstcnt > 0 && count < len - 1; burstcnt--) {
+ iowrite8(buf[count], chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.locality));
+ count++;
+ }
+
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+
+ /* write last byte */
+ iowrite8(buf[count],
+ chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.locality));
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ /* go and do it */
+ iowrite8(TPM_STS_GO,
+ chip->vendor.iobase + TPM_STS(chip->vendor.locality));
+
+ if (chip->vendor.irq) {
+ ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+ if (wait_for_stat
+ (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ tpm_calc_ordinal_duration(chip, ordinal),
+ &chip->vendor.read_queue) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+ return len;
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality);
+ return rc;
+}
+
+static 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 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, NULL,
+};
+
+static struct attribute_group tis_attr_grp = {
+ .attrs = tis_attrs
+};
+
+static struct tpm_vendor_specific tpm_tis = {
+ .status = tpm_tis_status,
+ .recv = tpm_tis_recv,
+ .send = tpm_tis_send,
+ .cancel = tpm_tis_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 irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs
+ *regs)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+ u32 interrupt;
+
+ interrupt = ioread32(chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ chip->vendor.irq = irq;
+
+ /* Clear interrupts handled with TPM_EOI */
+ iowrite32(interrupt,
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs
+ *regs)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+ u32 interrupt;
+ int i;
+
+ interrupt = ioread32(chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ if (interrupt & TPM_INTF_DATA_AVAIL_INT)
+ wake_up_interruptible(&chip->vendor.read_queue);
+ if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
+ for (i = 0; i < 5; i++)
+ if (check_locality(chip, i) >= 0)
+ break;
+ if (interrupt &
+ (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+ TPM_INTF_CMD_READY_INT))
+ wake_up_interruptible(&chip->vendor.int_queue);
+
+ /* Clear interrupts handled with TPM_EOI */
+ iowrite32(interrupt,
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+ return IRQ_HANDLED;
+}
+
+static int __devinit tpm_tis_pnp_init(struct pnp_dev
+ *pnp_dev, const struct
+ pnp_device_id
+ *pnp_id)
+{
+ u32 vendor, intfcaps, intmask;
+ int rc, i;
+ unsigned long start, len;
+ struct tpm_chip *chip;
+
+ start = pnp_mem_start(pnp_dev, 0);
+ len = pnp_mem_len(pnp_dev, 0);
+
+ if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis)))
+ return -ENODEV;
+
+ chip->vendor.iobase = ioremap(start, len);
+ if (!chip->vendor.iobase) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+ if ((vendor & 0xFFFF) == 0xFFFF) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* Default timeouts */
+ chip->vendor.timeout_a = 750; /* ms */
+ chip->vendor.timeout_b = 2000; /* 2 sec */
+ chip->vendor.timeout_c = 750; /* ms */
+ chip->vendor.timeout_d = 750; /* ms */
+
+ dev_info(&pnp_dev->dev,
+ "1.2 TPM (device-id 0x%X, rev-id %d)\n",
+ vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+
+ /* Figure out the capabilities */
+ intfcaps =
+ ioread32(chip->vendor.iobase +
+ TPM_INTF_CAPS(chip->vendor.locality));
+ dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n",
+ intfcaps);
+ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+ dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n");
+ if (intfcaps & TPM_INTF_CMD_READY_INT)
+ dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n");
+ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
+ dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n");
+ if (intfcaps & TPM_INTF_STS_VALID_INT)
+ dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n");
+ if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
+ dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n");
+
+ if (request_locality(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* INTERRUPT Setup */
+ init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&chip->vendor.int_queue);
+
+ intmask =
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+ | TPM_INTF_STS_VALID_INT;
+
+ iowrite32(intmask,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ chip->vendor.irq =
+ ioread8(chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+
+ for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+ iowrite8(i,
+ chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+ if (request_irq
+ (i, tis_int_probe, SA_SHIRQ,
+ chip->vendor.miscdev.name, chip) != 0) {
+ dev_info(chip->dev,
+ "Unable to request irq: %d for probe\n",
+ i);
+ continue;
+ }
+
+ /* Clear all existing */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ /* Turn on */
+ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ /* Generate Interrupts */
+ tpm_gen_interrupt(chip);
+
+ /* Turn off */
+ iowrite32(intmask,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ free_irq(i, chip);
+ }
+ if (chip->vendor.irq) {
+ iowrite8(chip->vendor.irq,
+ chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+ if (request_irq
+ (chip->vendor.irq, tis_int_handler, SA_SHIRQ,
+ chip->vendor.miscdev.name, chip) != 0) {
+ dev_info(chip->dev,
+ "Unable to request irq: %d for use\n", i);
+ chip->vendor.irq = 0;
+ } else {
+ /* Clear all existing */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ /* Turn on */
+ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ }
+ }
+
+ INIT_LIST_HEAD(&chip->vendor.list);
+ spin_lock(&tis_lock);
+ list_add(&chip->vendor.list, &tis_chips);
+ spin_unlock(&tis_lock);
+
+ tpm_get_timeouts(chip);
+
+ return 0;
+out_err:
+ if (chip->vendor.iobase)
+ iounmap(chip->vendor.iobase);
+ tpm_remove_hardware(chip->dev);
+ return rc;
+}
+
+static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
+{
+ return tpm_pm_suspend(&dev->dev, msg);
+}
+
+static int tpm_tis_pnp_resume(struct pnp_dev *dev)
+{
+ return tpm_pm_resume(&dev->dev);
+}
+
+static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
+ {"PNP0C31", 0}, /* TPM */
+ {"", 0}
+};
+
+static struct pnp_driver tis_pnp_driver = {
+ .name = "tpm_tis",
+ .id_table = tpm_pnp_tbl,
+ .probe = tpm_tis_pnp_init,
+ .suspend = tpm_tis_pnp_suspend,
+ .resume = tpm_tis_pnp_resume,
+};
+
+static int __init init_tis(void)
+{
+ return pnp_register_driver(&tis_pnp_driver);
+}
+
+static void __exit cleanup_tis(void)
+{
+ struct tpm_vendor_specific *i, *j;
+ struct tpm_chip *chip;
+ spin_lock(&tis_lock);
+ list_for_each_entry_safe(i, j, &tis_chips, list) {
+ chip = to_tpm_chip(i);
+ iowrite32(~TPM_GLOBAL_INT_ENABLE &
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.
+ locality)),
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ if (chip->vendor.irq)
+ free_irq(chip->vendor.irq, chip);
+ iounmap(i->iobase);
+ list_del(&i->list);
+ tpm_remove_hardware(chip->dev);
+ }
+ spin_unlock(&tis_lock);
+ pnp_unregister_driver(&tis_pnp_driver);
+}
+
+module_init(init_tis);
+module_exit(cleanup_tis);
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 7/7] tpm: Driver for next generation TPM chips
@ 2006-04-10 14:37 Kylene Jo Hall
2006-04-10 22:03 ` Andrew Morton
2006-04-11 23:05 ` [PATCH 7/7] tpm: Driver for next generation TPM chips Nishanth Aravamudan
0 siblings, 2 replies; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-10 14:37 UTC (permalink / raw)
To: linux-kernel; +Cc: akpm, TPM Device Driver List, Leendert Van Doorn
This patch contains the driver for the next generation of TPM chips
version 1.2 including support for interrupts. The Trusted Computing
Group has written the TPM Interface Specification (TIS) which defines a
common interface for all manufacturer's 1.2 TPM's thus the name
tpm_tis.
This updated version of the patch uses the new sysfs files that came
about from the comments and changes in patch 6/7. It replaces the 7/7
patch from the original set.
Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
---
drivers/char/tpm/tpm_tis.c | 638 +++++++++++++++++++++++++++++++++++
drivers/char/tpm/Kconfig | 11
drivers/char/tpm/Makefile | 1
drivers/char/tpm/tpm.c | 3
drivers/char/tpm/tpm.h | 9
5 files changed, 661 insertions(+), 1 deletion(-)
--- linux-2.6.16/drivers/char/tpm/tpm.h 2006-03-30 17:08:49.315065750 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm.h 2006-03-29 14:16:30.119053500 -0600
@@ -57,6 +57,8 @@ struct tpm_vendor_specific {
void __iomem *iobase; /* ioremapped address */
unsigned long base; /* TPM base address */
+ int irq;
+
int region_size;
int have_region;
@@ -66,8 +68,13 @@ struct tpm_vendor_specific {
u8 (*status) (struct tpm_chip *);
struct miscdevice miscdev;
struct attribute_group *attr_group;
+ struct list_head list;
+ int locality;
u32 timeout_a, timeout_b, timeout_c, timeout_d;
u32 duration[3];
+
+ wait_queue_head_t read_queue;
+ wait_queue_head_t int_queue;
};
struct tpm_chip {
@@ -93,6 +100,8 @@ struct tpm_chip {
struct list_head list;
};
+#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
+
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
--- linux-2.6.16/drivers/char/tpm/tpm.c 2006-03-30 17:08:49.315065750 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/tpm.c 2006-03-30 16:51:48.567273000 -0600
@@ -391,6 +391,9 @@ static ssize_t tpm_transmit(struct tpm_c
goto out;
}
+ if (chip->vendor.irq)
+ goto out_recv;
+
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
do {
u8 status = chip->vendor.status(chip);
--- linux-2.6.16/drivers/char/tpm/Makefile 2006-03-19 23:53:29.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/Makefile 2006-03-02 16:20:06.002087500 -0600
@@ -5,6 +5,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o
ifdef CONFIG_ACPI
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
endif
+obj-$(CONFIG_TCG_TIS) += tpm_tis.o
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
--- linux-2.6.16/drivers/char/tpm/Kconfig 2006-03-19 23:53:29.000000000 -0600
+++ linux-2.6.16-rc1-tpm/drivers/char/tpm/Kconfig 2006-03-02 16:19:05.730320750 -0600
@@ -20,9 +20,18 @@ config TCG_TPM
Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI
and CONFIG_PNPACPI.
+config TCG_TIS
+ tristate "TPM Interface Specification 1.2 Interface"
+ depends on TCG_TPM
+ ---help---
+ If you have a TPM security chip that is compliant with the
+ TCG TIS 1.2 TPM specification 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.
+
config TCG_NSC
tristate "National Semiconductor TPM Interface"
- depends on TCG_TPM
+ depends on TCG_TPM && PNPACPI
---help---
If you have a TPM security chip from National Semicondutor
say Yes and it will be accessible from within Linux. To
--- linux-2.6.16/drivers/char/tpm/tpm_tis.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.16-44/drivers/char/tpm/tpm_tis.c 2006-04-07 11:46:03.338057250 -0500
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.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.
+ *
+ * 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/pnp.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include "tpm.h"
+
+#define TPM_HEADER_SIZE 10
+
+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_int_flags {
+ TPM_GLOBAL_INT_ENABLE = 0x80000000,
+ TPM_INTF_BURST_COUNT_STATIC = 0x100,
+ TPM_INTF_CMD_READY_INT = 0x080,
+ TPM_INTF_INT_EDGE_FALLING = 0x040,
+ TPM_INTF_INT_EDGE_RISING = 0x020,
+ TPM_INTF_INT_LEVEL_LOW = 0x010,
+ TPM_INTF_INT_LEVEL_HIGH = 0x008,
+ TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+ TPM_INTF_STS_VALID_INT = 0x002,
+ TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
+#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
+#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
+#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
+#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
+#define TPM_STS(l) (0x0018 | ((l) << 12))
+#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
+
+#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
+#define TPM_RID(l) (0x0F04 | ((l) << 12))
+
+static LIST_HEAD(tis_chips);
+static DEFINE_SPINLOCK(tis_lock);
+
+static int check_locality(struct tpm_chip *chip, int l)
+{
+ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+ return chip->vendor.locality = l;
+
+ return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int l, int force)
+{
+ if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
+ iowrite8(TPM_ACCESS_ACTIVE_LOCALITY,
+ chip->vendor.iobase + TPM_ACCESS(l));
+}
+
+static int request_locality(struct tpm_chip *chip, int l)
+{
+ unsigned long stop;
+
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ iowrite8(TPM_ACCESS_REQUEST_USE,
+ chip->vendor.iobase + TPM_ACCESS(l));
+
+ if (chip->vendor.irq) {
+ interruptible_sleep_on_timeout(&chip->vendor.int_queue,
+ HZ *
+ chip->vendor.timeout_a /
+ 1000);
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ } else {
+ /* wait for burstcount */
+ stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
+ do {
+ if (check_locality(chip, l) >= 0)
+ return l;
+ msleep(TPM_TIMEOUT);
+ }
+ while (time_before(jiffies, stop));
+ }
+ return -1;
+}
+
+static u8 tpm_tis_status(struct tpm_chip *chip)
+{
+ return ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality));
+}
+
+static void tpm_tis_ready(struct tpm_chip *chip)
+{
+ /* this causes the current command to be aborted */
+ iowrite8(TPM_STS_COMMAND_READY,
+ chip->vendor.iobase + TPM_STS(chip->vendor.locality));
+}
+
+static int get_burstcount(struct tpm_chip *chip)
+{
+ unsigned long stop;
+ int burstcnt;
+
+ /* wait for burstcount */
+ /* which timeout value, spec has 2 answers (c & d) */
+ stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
+ do {
+ burstcnt = ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality) + 1);
+ burstcnt += ioread8(chip->vendor.iobase +
+ TPM_STS(chip->vendor.locality) +
+ 2) << 8;
+ if (burstcnt)
+ return burstcnt;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ return -EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout,
+ wait_queue_head_t * queue)
+{
+ unsigned long stop;
+ u8 status;
+
+ /* check current status */
+ status = tpm_tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+
+ if (chip->vendor.irq) {
+ interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
+ status = tpm_tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+ } else {
+ stop = jiffies + (HZ * timeout / 1000);
+ do {
+ msleep(TPM_TIMEOUT);
+ status = tpm_tis_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)
+{
+ int size = 0, burstcnt;
+ while (size < count &&
+ wait_for_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ chip->vendor.timeout_c,
+ &chip->vendor.read_queue)
+ == 0) {
+ burstcnt = get_burstcount(chip);
+ for (; burstcnt > 0 && size < count; burstcnt--)
+ buf[size++] = ioread8(chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.
+ locality));
+ }
+ return size;
+}
+
+static int tpm_tis_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 */
+ if ((size =
+ recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
+ dev_err(chip->dev, "Unable to read header\n");
+ goto out;
+ }
+
+ expected = be32_to_cpu(*(__be32 *) (buf + 2));
+ if (expected > count) {
+ size = -EIO;
+ goto out;
+ }
+
+ if ((size +=
+ recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_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,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ dev_err(chip->dev, "Error left over data\n");
+ size = -EIO;
+ goto out;
+ }
+
+out:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+ return size;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len)
+{
+ int rc, status, burstcnt;
+ size_t count = 0;
+ u32 ordinal;
+
+ if (request_locality(chip, 0) < 0)
+ return -EBUSY;
+
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_tis_ready(chip);
+ if (wait_for_stat
+ (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
+ &chip->vendor.int_queue) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+
+ while (count < len - 1) {
+ burstcnt = get_burstcount(chip);
+ for (; burstcnt > 0 && count < len - 1; burstcnt--) {
+ iowrite8(buf[count], chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.locality));
+ count++;
+ }
+
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+
+ /* write last byte */
+ iowrite8(buf[count],
+ chip->vendor.iobase +
+ TPM_DATA_FIFO(chip->vendor.locality));
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
+ &chip->vendor.int_queue);
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ /* go and do it */
+ iowrite8(TPM_STS_GO,
+ chip->vendor.iobase + TPM_STS(chip->vendor.locality));
+
+ if (chip->vendor.irq) {
+ ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+ if (wait_for_stat
+ (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ tpm_calc_ordinal_duration(chip, ordinal),
+ &chip->vendor.read_queue) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+ return len;
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+ return rc;
+}
+
+static 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 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, NULL,
+};
+
+static struct attribute_group tis_attr_grp = {
+ .attrs = tis_attrs
+};
+
+static struct tpm_vendor_specific tpm_tis = {
+ .status = tpm_tis_status,
+ .recv = tpm_tis_recv,
+ .send = tpm_tis_send,
+ .cancel = tpm_tis_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 irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs
+ *regs)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+ u32 interrupt;
+
+ interrupt = ioread32(chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ chip->vendor.irq = irq;
+
+ /* Clear interrupts handled with TPM_EOI */
+ iowrite32(interrupt,
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs
+ *regs)
+{
+ struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+ u32 interrupt;
+ int i;
+
+ interrupt = ioread32(chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ if (interrupt & TPM_INTF_DATA_AVAIL_INT)
+ wake_up_interruptible(&chip->vendor.read_queue);
+ if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
+ for (i = 0; i < 5; i++)
+ if (check_locality(chip, i) >= 0)
+ break;
+ if (interrupt &
+ (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+ TPM_INTF_CMD_READY_INT))
+ wake_up_interruptible(&chip->vendor.int_queue);
+
+ /* Clear interrupts handled with TPM_EOI */
+ iowrite32(interrupt,
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+ return IRQ_HANDLED;
+}
+
+static int __devinit tpm_tis_pnp_init(struct pnp_dev
+ *pnp_dev, const struct
+ pnp_device_id
+ *pnp_id)
+{
+ u32 vendor, intfcaps, intmask;
+ int rc, i;
+ unsigned long start, len;
+ struct tpm_chip *chip;
+
+ start = pnp_mem_start(pnp_dev, 0);
+ len = pnp_mem_len(pnp_dev, 0);
+
+ if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis)))
+ return -ENODEV;
+
+ chip->vendor.iobase = ioremap(start, len);
+ if (!chip->vendor.iobase) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+ if ((vendor & 0xFFFF) == 0xFFFF) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* Default timeouts */
+ chip->vendor.timeout_a = 750; /* ms */
+ chip->vendor.timeout_b = 2000; /* 2 sec */
+ chip->vendor.timeout_c = 750; /* ms */
+ chip->vendor.timeout_d = 750; /* ms */
+
+ dev_info(&pnp_dev->dev,
+ "1.2 TPM (device-id 0x%X, rev-id %d)\n",
+ vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+
+ /* Figure out the capabilities */
+ intfcaps =
+ ioread32(chip->vendor.iobase +
+ TPM_INTF_CAPS(chip->vendor.locality));
+ dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n",
+ intfcaps);
+ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+ dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n");
+ if (intfcaps & TPM_INTF_CMD_READY_INT)
+ dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+ dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n");
+ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
+ dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n");
+ if (intfcaps & TPM_INTF_STS_VALID_INT)
+ dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n");
+ if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
+ dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n");
+
+ if (request_locality(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* INTERRUPT Setup */
+ init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&chip->vendor.int_queue);
+
+ intmask =
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+ | TPM_INTF_STS_VALID_INT;
+
+ iowrite32(intmask,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ chip->vendor.irq =
+ ioread8(chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+
+ for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+ iowrite8(i,
+ chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+ if (request_irq
+ (i, tis_int_probe, SA_SHIRQ,
+ chip->vendor.miscdev.name, chip) != 0) {
+ dev_info(chip->dev,
+ "Unable to request irq: %d for probe\n",
+ i);
+ continue;
+ }
+
+ /* Clear all existing */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ /* Turn on */
+ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ /* Generate Interrupts */
+ tpm_gen_interrupt(chip);
+
+ /* Turn off */
+ iowrite32(intmask,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ free_irq(i, chip);
+ }
+ if (chip->vendor.irq) {
+ iowrite8(chip->vendor.irq,
+ chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+ if (request_irq
+ (chip->vendor.irq, tis_int_handler, SA_SHIRQ,
+ chip->vendor.miscdev.name, chip) != 0) {
+ dev_info(chip->dev,
+ "Unable to request irq: %d for use\n", i);
+ chip->vendor.irq = 0;
+ } else {
+ /* Clear all existing */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
+ /* Turn on */
+ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ }
+ }
+
+ INIT_LIST_HEAD(&chip->vendor.list);
+ spin_lock(&tis_lock);
+ list_add(&chip->vendor.list, &tis_chips);
+ spin_unlock(&tis_lock);
+
+ tpm_get_timeouts(chip);
+ tpm_continue_selftest(chip);
+
+ return 0;
+out_err:
+ if (chip->vendor.iobase)
+ iounmap(chip->vendor.iobase);
+ tpm_remove_hardware(chip->dev);
+ return rc;
+}
+
+static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
+{
+ return tpm_pm_suspend(&dev->dev, msg);
+}
+
+static int tpm_tis_pnp_resume(struct pnp_dev *dev)
+{
+ return tpm_pm_resume(&dev->dev);
+}
+
+static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
+ {"PNP0C31", 0}, /* TPM */
+ {"", 0}
+};
+
+static struct pnp_driver tis_pnp_driver = {
+ .name = "tpm_tis",
+ .id_table = tpm_pnp_tbl,
+ .probe = tpm_tis_pnp_init,
+ .suspend = tpm_tis_pnp_suspend,
+ .resume = tpm_tis_pnp_resume,
+};
+
+static int __init init_tis(void)
+{
+ return pnp_register_driver(&tis_pnp_driver);
+}
+
+static void __exit cleanup_tis(void)
+{
+ struct tpm_vendor_specific *i, *j;
+ struct tpm_chip *chip;
+ spin_lock(&tis_lock);
+ list_for_each_entry_safe(i, j, &tis_chips, list) {
+ chip = to_tpm_chip(i);
+ iowrite32(~TPM_GLOBAL_INT_ENABLE &
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.
+ locality)),
+ chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+ release_locality(chip, chip->vendor.locality, 1);
+ if (chip->vendor.irq)
+ free_irq(chip->vendor.irq, chip);
+ iounmap(i->iobase);
+ list_del(&i->list);
+ tpm_remove_hardware(chip->dev);
+ }
+ spin_unlock(&tis_lock);
+ pnp_unregister_driver(&tis_pnp_driver);
+}
+
+module_init(init_tis);
+module_exit(cleanup_tis);
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 7/7] tpm: Driver for next generation TPM chips
2006-04-10 14:37 [PATCH 7/7] tpm: Driver for next generation TPM chips Kylene Jo Hall
@ 2006-04-10 22:03 ` Andrew Morton
2006-04-11 20:15 ` [PATCH] tpm: update to use wait_event calls Kylene Jo Hall
2006-04-11 23:05 ` [PATCH 7/7] tpm: Driver for next generation TPM chips Nishanth Aravamudan
1 sibling, 1 reply; 12+ messages in thread
From: Andrew Morton @ 2006-04-10 22:03 UTC (permalink / raw)
To: Kylene Jo Hall; +Cc: linux-kernel, tpmdd-devel, leendert
Kylene Jo Hall <kjhall@us.ibm.com> wrote:
>
> This patch contains the driver for the next generation of TPM chips
> version 1.2 including support for interrupts. The Trusted Computing
> Group has written the TPM Interface Specification (TIS) which defines a
> common interface for all manufacturer's 1.2 TPM's thus the name
> tpm_tis.
>
> This updated version of the patch uses the new sysfs files that came
> about from the comments and changes in patch 6/7. It replaces the 7/7
> patch from the original set.
>
> Signed-off-by: Leendert van Doorn <leendert@watson.ibm.com>
> Signed-off-by: Kylene Hall <kjhall@us.ibm.com>
I've assumed that Leendert is the author of this driver. If incorrect,
please let me know. If correct then the way in which we indicate that is
to put a From: line right at the top of the changelog.
> + interruptible_sleep_on_timeout(&chip->vendor.int_queue,
> + HZ *
> + chip->vendor.timeout_a /
> + 1000);
>
> ...
>
> + interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
Please don't use the sleep_on functions. They are racy unless (iirc) both
the waker and wakee are holding lock_kernel(). If the race hits, we miss a
wakeup.
These should be converted to the not-racy wait_event_interruptible().
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH] tpm: update to use wait_event calls
2006-04-10 22:03 ` Andrew Morton
@ 2006-04-11 20:15 ` Kylene Jo Hall
2006-04-11 20:31 ` Nish Aravamudan
2006-04-11 20:40 ` [PATCH] tpm: update to use wait_event calls Ingo Oeser
0 siblings, 2 replies; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-11 20:15 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, TPM Device Driver List
On Mon, 2006-04-10 at 15:03 -0700, Andrew Morton wrote:
>
> > + interruptible_sleep_on_timeout(&chip->vendor.int_queue,
> > + HZ *
> > + chip->vendor.timeout_a /
> > + 1000);
> >
> > ...
> >
> > + interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
>
> Please don't use the sleep_on functions. They are racy unless (iirc) both
> the waker and wakee are holding lock_kernel(). If the race hits, we miss a
> wakeup.
>
> These should be converted to the not-racy wait_event_interruptible().
Changed in this patch.
Use wait_event_interruptible_timeout in place of
interruptible_sleep_on_timeout due to its racy nature.
Signed-off-by: Kylie Hall <kjhall@us.ibm.com>
---
drivers/char/tpm/tpm_tis.c | 15 +++++++++------
1 files changed, 9 insertions(+), 6 deletions(-)
--- linux-2.6.17-rc1/drivers/char/tpm/tpm_tis.c 2006-04-11 12:18:35.573996500 -0500
+++ linux-2.6.16-44/drivers/char/tpm/tpm_tis.c 2006-04-11 14:00:04.341229250 -0500
@@ -95,10 +95,10 @@ static int request_locality(struct tpm_c
chip->vendor.iobase + TPM_ACCESS(l));
if (chip->vendor.irq) {
- interruptible_sleep_on_timeout(&chip->vendor.int_queue,
- HZ *
- chip->vendor.timeout_a /
- 1000);
+ wait_event_interruptible_timeout(chip->vendor.int_queue,
+ (check_locality(chip, l) >= 0),
+ HZ * chip->vendor.timeout_a /
+ 1000);
if (check_locality(chip, l) >= 0)
return l;
@@ -150,7 +150,7 @@ static int get_burstcount(struct tpm_chi
}
static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout,
- wait_queue_head_t * queue)
+ wait_queue_head_t *queue)
{
unsigned long stop;
u8 status;
@@ -161,7 +161,10 @@ static int wait_for_stat(struct tpm_chip
return 0;
if (chip->vendor.irq) {
- interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
+ wait_event_interruptible_timeout(*queue,
+ ((tpm_tis_status(chip) &
+ mask) == mask),
+ HZ * timeout / 1000);
status = tpm_tis_status(chip);
if ((status & mask) == mask)
return 0;
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] tpm: update to use wait_event calls
2006-04-11 20:15 ` [PATCH] tpm: update to use wait_event calls Kylene Jo Hall
@ 2006-04-11 20:31 ` Nish Aravamudan
2006-04-11 22:32 ` [PATCH] tpm: use wait_event return code and msecs_to_jiffies Kylene Jo Hall
2006-04-11 20:40 ` [PATCH] tpm: update to use wait_event calls Ingo Oeser
1 sibling, 1 reply; 12+ messages in thread
From: Nish Aravamudan @ 2006-04-11 20:31 UTC (permalink / raw)
To: Kylene Jo Hall; +Cc: Andrew Morton, linux-kernel, TPM Device Driver List
On 4/11/06, Kylene Jo Hall <kjhall@us.ibm.com> wrote:
> On Mon, 2006-04-10 at 15:03 -0700, Andrew Morton wrote:
> >
> > > + interruptible_sleep_on_timeout(&chip->vendor.int_queue,
> > > + HZ *
> > > + chip->vendor.timeout_a /
> > > + 1000);
> > >
> > > ...
> > >
> > > + interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
> >
> > Please don't use the sleep_on functions. They are racy unless (iirc) both
> > the waker and wakee are holding lock_kernel(). If the race hits, we miss a
> > wakeup.
> >
> > These should be converted to the not-racy wait_event_interruptible().
>
> Changed in this patch.
>
> Use wait_event_interruptible_timeout in place of
> interruptible_sleep_on_timeout due to its racy nature.
>
> Signed-off-by: Kylie Hall <kjhall@us.ibm.com>
> ---
> drivers/char/tpm/tpm_tis.c | 15 +++++++++------
> 1 files changed, 9 insertions(+), 6 deletions(-)
>
> --- linux-2.6.17-rc1/drivers/char/tpm/tpm_tis.c 2006-04-11 12:18:35.573996500 -0500
> +++ linux-2.6.16-44/drivers/char/tpm/tpm_tis.c 2006-04-11 14:00:04.341229250 -0500
> @@ -95,10 +95,10 @@ static int request_locality(struct tpm_c
> chip->vendor.iobase + TPM_ACCESS(l));
>
> if (chip->vendor.irq) {
> - interruptible_sleep_on_timeout(&chip->vendor.int_queue,
> - HZ *
> - chip->vendor.timeout_a /
> - 1000);
> + wait_event_interruptible_timeout(chip->vendor.int_queue,
> + (check_locality(chip, l) >= 0),
> + HZ * chip->vendor.timeout_a /
> + 1000);
> if (check_locality(chip, l) >= 0)
> return l;
Rather than check the condition you slept on right away, couldn't you
just store the return value of wait_event_interruptible_timeout()? If
it's positive, the condition should be true, if it's negative, then
you got a signal, if it's 0, then you timed out. Same would go for the
other change.
Thanks,
Nish
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] tpm: update to use wait_event calls
2006-04-11 20:15 ` [PATCH] tpm: update to use wait_event calls Kylene Jo Hall
2006-04-11 20:31 ` Nish Aravamudan
@ 2006-04-11 20:40 ` Ingo Oeser
1 sibling, 0 replies; 12+ messages in thread
From: Ingo Oeser @ 2006-04-11 20:40 UTC (permalink / raw)
To: Kylene Jo Hall; +Cc: Andrew Morton, linux-kernel, TPM Device Driver List
Hi Kylene,
On Tuesday, 11. April 2006 22:15, Kylene Jo Hall wrote:
> Signed-off-by: Kylie Hall <kjhall@us.ibm.com>
> ---
> drivers/char/tpm/tpm_tis.c | 15 +++++++++------
> 1 files changed, 9 insertions(+), 6 deletions(-)
>
> --- linux-2.6.17-rc1/drivers/char/tpm/tpm_tis.c 2006-04-11 12:18:35.573996500 -0500
> +++ linux-2.6.16-44/drivers/char/tpm/tpm_tis.c 2006-04-11 14:00:04.341229250 -0500
> @@ -95,10 +95,10 @@ static int request_locality(struct tpm_c
> chip->vendor.iobase + TPM_ACCESS(l));
>
> if (chip->vendor.irq) {
> - interruptible_sleep_on_timeout(&chip->vendor.int_queue,
> - HZ *
> - chip->vendor.timeout_a /
> - 1000);
> + wait_event_interruptible_timeout(chip->vendor.int_queue,
> + (check_locality(chip, l) >= 0),
> + HZ * chip->vendor.timeout_a /
> + 1000);
> if (check_locality(chip, l) >= 0)
> return l;
what about using msecs_to_jiffies(chip->vendor.timeout_a) for this?
>
> @@ -150,7 +150,7 @@ static int get_burstcount(struct tpm_chi
> }
>
> static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout,
> - wait_queue_head_t * queue)
> + wait_queue_head_t *queue)
> {
> unsigned long stop;
> u8 status;
> @@ -161,7 +161,10 @@ static int wait_for_stat(struct tpm_chip
> return 0;
>
> if (chip->vendor.irq) {
> - interruptible_sleep_on_timeout(queue, HZ * timeout / 1000);
> + wait_event_interruptible_timeout(*queue,
> + ((tpm_tis_status(chip) &
> + mask) == mask),
> + HZ * timeout / 1000);
> status = tpm_tis_status(chip);
> if ((status & mask) == mask)
> return 0;
msecs_to_jiffies(timeout)?
Regards
Ingo Oeser
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH] tpm: use wait_event return code and msecs_to_jiffies
2006-04-11 20:31 ` Nish Aravamudan
@ 2006-04-11 22:32 ` Kylene Jo Hall
0 siblings, 0 replies; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-11 22:32 UTC (permalink / raw)
To: Nish Aravamudan, Ingo Oeser
Cc: Andrew Morton, linux-kernel, TPM Device Driver List
On Tue, 2006-04-11 at 13:31 -0700, Nish Aravamudan wrote:
> Rather than check the condition you slept on right away, couldn't you
> just store the return value of wait_event_interruptible_timeout()? If
> it's positive, the condition should be true, if it's negative, then
> you got a signal, if it's 0, then you timed out. Same would go for the
> other change.
On Tue, 2006-04-11 at 22:40 +0200, Ingo Oeser wrote:
> what about using msecs_to_jiffies(chip->vendor.timeout_a) for this?
Great ideas, patch included below.
Update the usage of wait_event calls to utilize the return code and
msecs_to_jiffies.
Signed-off-by: Kylie Hall <kjhall@us.ibm.com>
---
drivers/char/tpm/tpm_tis.c | 28 +++++++++++++++++-----------
1 files changed, 17 insertions(+), 11 deletions(-)
--- linux-2.6.17-rc1-mm2/drivers/char/tpm/tpm_tis.c 2006-04-11 17:36:08.247422750 -0500
+++ linux-2.6.17-rc1/drivers/char/tpm/tpm_tis.c 2006-04-11 17:26:24.134918000 -0500
@@ -87,6 +87,7 @@ static void release_locality(struct tpm_
static int request_locality(struct tpm_chip *chip, int l)
{
unsigned long stop;
+ long rc;
if (check_locality(chip, l) >= 0)
return l;
@@ -95,11 +96,14 @@ static int request_locality(struct tpm_c
chip->vendor.iobase + TPM_ACCESS(l));
if (chip->vendor.irq) {
- wait_event_interruptible_timeout(chip->vendor.int_queue,
- (check_locality(chip, l) >= 0),
- HZ * chip->vendor.timeout_a /
- 1000);
- if (check_locality(chip, l) >= 0)
+ rc = wait_event_interruptible_timeout(chip->vendor.
+ int_queue,
+ (check_locality
+ (chip, l) >= 0),
+ msecs_to_jiffies
+ (chip->vendor.
+ timeout_a));
+ if (rc > 0)
return l;
} else {
@@ -153,6 +157,7 @@ static int wait_for_stat(struct tpm_chip
wait_queue_head_t *queue)
{
unsigned long stop;
+ long rc;
u8 status;
/* check current status */
@@ -161,12 +166,13 @@ static int wait_for_stat(struct tpm_chip
return 0;
if (chip->vendor.irq) {
- wait_event_interruptible_timeout(*queue,
- ((tpm_tis_status(chip) &
- mask) == mask),
- HZ * timeout / 1000);
- status = tpm_tis_status(chip);
- if ((status & mask) == mask)
+ rc = wait_event_interruptible_timeout(*queue,
+ ((tpm_tis_status
+ (chip) & mask) ==
+ mask),
+ msecs_to_jiffies
+ (timeout));
+ if (rc > 0)
return 0;
} else {
stop = jiffies + (HZ * timeout / 1000);
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 7/7] tpm: Driver for next generation TPM chips
2006-04-10 14:37 [PATCH 7/7] tpm: Driver for next generation TPM chips Kylene Jo Hall
2006-04-10 22:03 ` Andrew Morton
@ 2006-04-11 23:05 ` Nishanth Aravamudan
2006-04-12 17:29 ` Kylene Jo Hall
1 sibling, 1 reply; 12+ messages in thread
From: Nishanth Aravamudan @ 2006-04-11 23:05 UTC (permalink / raw)
To: Kylene Jo Hall
Cc: linux-kernel, akpm, TPM Device Driver List, Leendert Van Doorn
On 10.04.2006 [09:37:28 -0500], Kylene Jo Hall wrote:
> This patch contains the driver for the next generation of TPM chips
> version 1.2 including support for interrupts. The Trusted Computing
> Group has written the TPM Interface Specification (TIS) which defines a
> common interface for all manufacturer's 1.2 TPM's thus the name
> tpm_tis.
>
> This updated version of the patch uses the new sysfs files that came
> about from the comments and changes in patch 6/7. It replaces the 7/7
> patch from the original set.
<snip>
> +static int request_locality(struct tpm_chip *chip, int l)
> +{
> + unsigned long stop;
> +
> + if (check_locality(chip, l) >= 0)
> + return l;
> +
> + iowrite8(TPM_ACCESS_REQUEST_USE,
> + chip->vendor.iobase + TPM_ACCESS(l));
> +
> + if (chip->vendor.irq) {
> + interruptible_sleep_on_timeout(&chip->vendor.int_queue,
> + HZ *
> + chip->vendor.timeout_a /
> + 1000);
> + if (check_locality(chip, l) >= 0)
> + return l;
> +
> + } else {
> + /* wait for burstcount */
> + stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
> + do {
> + if (check_locality(chip, l) >= 0)
> + return l;
> + msleep(TPM_TIMEOUT);
> + }
> + while (time_before(jiffies, stop));
> + }
This looks like it could take the msecs_to_jiffies() conversion as well.
Might as well cache it before the if/else, as both clauses use it?
Really, it is just wait_event*() without the wait-queue. Well, this is
at least one more consumer potentially of the poll_event*() API I had
written a while back, I'll dust it off again if I have the time.
<snip>
> +static int get_burstcount(struct tpm_chip *chip)
> +{
> + unsigned long stop;
> + int burstcnt;
> +
> + /* wait for burstcount */
> + /* which timeout value, spec has 2 answers (c & d) */
> + stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
msecs_to_jiffies().
<snip>
With the changes you've already made, that should clean up the sleeping
code a bit, at least.
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 7/7] tpm: Driver for next generation TPM chips
2006-04-11 23:05 ` [PATCH 7/7] tpm: Driver for next generation TPM chips Nishanth Aravamudan
@ 2006-04-12 17:29 ` Kylene Jo Hall
2006-04-12 17:32 ` Nishanth Aravamudan
0 siblings, 1 reply; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-12 17:29 UTC (permalink / raw)
To: Nishanth Aravamudan; +Cc: linux-kernel, akpm, TPM Device Driver List
On Tue, 2006-04-11 at 16:05 -0700, Nishanth Aravamudan wrote:
> return l;
> > +
> > + } else {
> > + /* wait for burstcount */
> > + stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
> > + do {
> > + if (check_locality(chip, l) >= 0)
> > + return l;
> > + msleep(TPM_TIMEOUT);
> > + }
> > + while (time_before(jiffies, stop));
> > + }
>
> This looks like it could take the msecs_to_jiffies() conversion as well.
> Might as well cache it before the if/else, as both clauses use it?
> Really, it is just wait_event*() without the wait-queue. Well, this is
> at least one more consumer potentially of the poll_event*() API I had
> written a while back, I'll dust it off again if I have the time.
>
> <snip>
>
> > +static int get_burstcount(struct tpm_chip *chip)
> > +{
> > + unsigned long stop;
> > + int burstcnt;
> > +
> > + /* wait for burstcount */
> > + /* which timeout value, spec has 2 answers (c & d) */
> > + stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
>
> msecs_to_jiffies().
>
Since the timeout and duration values are always used in jiffies I think
I'll just convert them to those values when I store them in the chip
struct to cut way down on the number of conversions all together. Sound
reasonable?
Thanks,
Kylie
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 7/7] tpm: Driver for next generation TPM chips
2006-04-12 17:29 ` Kylene Jo Hall
@ 2006-04-12 17:32 ` Nishanth Aravamudan
2006-04-12 21:48 ` [PATCH] tpm: msecs_to_jiffies cleanups Kylene Jo Hall
0 siblings, 1 reply; 12+ messages in thread
From: Nishanth Aravamudan @ 2006-04-12 17:32 UTC (permalink / raw)
To: Kylene Jo Hall; +Cc: linux-kernel, akpm, TPM Device Driver List
On 12.04.2006 [12:29:17 -0500], Kylene Jo Hall wrote:
> On Tue, 2006-04-11 at 16:05 -0700, Nishanth Aravamudan wrote:
> > return l;
> > > +
> > > + } else {
> > > + /* wait for burstcount */
> > > + stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
> > > + do {
> > > + if (check_locality(chip, l) >= 0)
> > > + return l;
> > > + msleep(TPM_TIMEOUT);
> > > + }
> > > + while (time_before(jiffies, stop));
> > > + }
> >
> > This looks like it could take the msecs_to_jiffies() conversion as well.
> > Might as well cache it before the if/else, as both clauses use it?
> > Really, it is just wait_event*() without the wait-queue. Well, this is
> > at least one more consumer potentially of the poll_event*() API I had
> > written a while back, I'll dust it off again if I have the time.
> >
> > <snip>
> >
> > > +static int get_burstcount(struct tpm_chip *chip)
> > > +{
> > > + unsigned long stop;
> > > + int burstcnt;
> > > +
> > > + /* wait for burstcount */
> > > + /* which timeout value, spec has 2 answers (c & d) */
> > > + stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
> >
> > msecs_to_jiffies().
>
> >
>
> Since the timeout and duration values are always used in jiffies I
> think I'll just convert them to those values when I store them in the
> chip struct to cut way down on the number of conversions all together.
> Sound reasonable?
Probably, as long as they aren't exposed to userspace in any way. I
don't think userspace should do any calculations in jiffies units.
Thanks,
Nish
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH] tpm: msecs_to_jiffies cleanups
2006-04-12 17:32 ` Nishanth Aravamudan
@ 2006-04-12 21:48 ` Kylene Jo Hall
0 siblings, 0 replies; 12+ messages in thread
From: Kylene Jo Hall @ 2006-04-12 21:48 UTC (permalink / raw)
To: Nishanth Aravamudan; +Cc: linux-kernel, akpm, TPM Device Driver List
On Wed, 2006-04-12 at 10:32 -0700, Nishanth Aravamudan wrote:
> On 12.04.2006 [12:29:17 -0500], Kylene Jo Hall wrote:
> > On Tue, 2006-04-11 at 16:05 -0700, Nishanth Aravamudan wrote:
> > > This looks like it could take the msecs_to_jiffies() conversion as well.
> > > Might as well cache it before the if/else, as both clauses use it?
> > > Really, it is just wait_event*() without the wait-queue. Well, this is
> > > at least one more consumer potentially of the poll_event*() API I had
> > > written a while back, I'll dust it off again if I have the time.
> > >
> > Since the timeout and duration values are always used in jiffies I
> > think I'll just convert them to those values when I store them in the
> > chip struct to cut way down on the number of conversions all together.
> > Sound reasonable?
>
> Probably, as long as they aren't exposed to userspace in any way. I
> don't think userspace should do any calculations in jiffies units.
The timeout and duration values used in the tpm driver are not exposed
to userspace. This patch converts the storage units to jiffies with
msecs_to_jiffies. They were always being used in jiffies so this
simplifies things removing the need for calculation all over the place.
The change necessitated a type change in the tpm_chip struct to hold
jiffies.
Signed-off-by: Kylie Hall <kjhall@us.ibm.com>
---
drivers/char/tpm/tpm.c | 22 ++++++++++++++--------
drivers/char/tpm/tpm.h | 4 ++--
drivers/char/tpm/tpm_tis.c | 32 ++++++++++++++++----------------
3 files changed, 32 insertions(+), 26 deletions(-)
--- linux-2.6.17-rc1-mm2/drivers/char/tpm/tpm.h 2006-04-11 12:18:35.569996000 -0500
+++ linux-2.6.17-rc1/drivers/char/tpm/tpm.h 2006-04-12 13:58:17.558218250 -0500
@@ -77,8 +77,8 @@ struct tpm_vendor_specific {
struct attribute_group *attr_group;
struct list_head list;
int locality;
- u32 timeout_a, timeout_b, timeout_c, timeout_d;
- u32 duration[3];
+ unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
+ unsigned long duration[3]; /* jiffies */
wait_queue_head_t read_queue;
wait_queue_head_t int_queue;
--- linux-2.6.17-rc1-mm2/drivers/char/tpm/tpm.c 2006-04-12 16:36:51.868825500 -0500
+++ linux-2.6.17-rc1/drivers/char/tpm/tpm.c 2006-04-12 14:11:16.150877250 -0500
@@ -354,7 +354,7 @@ unsigned long tpm_calc_ordinal_duration(
TPM_PROTECTED_ORDINAL_MASK];
if (duration_idx != TPM_UNDEFINED)
- duration = chip->vendor.duration[duration_idx] * HZ / 1000;
+ duration = chip->vendor.duration[duration_idx];
if (duration <= 0)
return 2 * 60 * HZ;
else
@@ -524,19 +524,19 @@ void tpm_get_timeouts(struct tpm_chip *c
timeout =
be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
if (timeout)
- chip->vendor.timeout_a = timeout;
+ chip->vendor.timeout_a = msecs_to_jiffies(timeout);
timeout =
be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
if (timeout)
- chip->vendor.timeout_b = timeout;
+ chip->vendor.timeout_b = msecs_to_jiffies(timeout);
timeout =
be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
if (timeout)
- chip->vendor.timeout_c = timeout;
+ chip->vendor.timeout_c = msecs_to_jiffies(timeout);
timeout =
be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
if (timeout)
- chip->vendor.timeout_d = timeout;
+ chip->vendor.timeout_d = msecs_to_jiffies(timeout);
duration:
memcpy(data, tpm_cap, sizeof(tpm_cap));
@@ -553,11 +553,17 @@ duration:
return;
chip->vendor.duration[TPM_SHORT] =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
+ msecs_to_jiffies(be32_to_cpu
+ (*((__be32 *) (data +
+ TPM_GET_CAP_RET_UINT32_1_IDX))));
chip->vendor.duration[TPM_MEDIUM] =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
+ msecs_to_jiffies(be32_to_cpu
+ (*((__be32 *) (data +
+ TPM_GET_CAP_RET_UINT32_2_IDX))));
chip->vendor.duration[TPM_LONG] =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
+ msecs_to_jiffies(be32_to_cpu
+ (*((__be32 *) (data +
+ TPM_GET_CAP_RET_UINT32_3_IDX))));
}
EXPORT_SYMBOL_GPL(tpm_get_timeouts);
--- linux-2.6.17-rc1-mm2/drivers/char/tpm/tpm_tis.c 2006-04-12 11:45:37.288732500 -0500
+++ linux-2.6.17-rc1/drivers/char/tpm/tpm_tis.c 2006-04-12 14:49:13.033173500 -0500
@@ -51,6 +51,11 @@ enum tis_int_flags {
TPM_INTF_DATA_AVAIL_INT = 0x001,
};
+enum tis_defaults {
+ TIS_SHORT_TIMEOUT = 750, /* ms */
+ TIS_LONG_TIMEOUT = 2000, /* 2 sec */
+};
+
#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
@@ -96,19 +103,16 @@ static int request_locality(struct tpm_c
chip->vendor.iobase + TPM_ACCESS(l));
if (chip->vendor.irq) {
- rc = wait_event_interruptible_timeout(chip->vendor.
- int_queue,
+ rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
(check_locality
(chip, l) >= 0),
- msecs_to_jiffies
- (chip->vendor.
- timeout_a));
+ chip->vendor.timeout_a);
if (rc > 0)
return l;
} else {
/* wait for burstcount */
- stop = jiffies + (HZ * chip->vendor.timeout_a / 1000);
+ stop = jiffies + chip->vendor.timeout_a;
do {
if (check_locality(chip, l) >= 0)
return l;
@@ -139,7 +143,7 @@ static int get_burstcount(struct tpm_chi
/* wait for burstcount */
/* which timeout value, spec has 2 answers (c & d) */
- stop = jiffies + (HZ * chip->vendor.timeout_d / 1000);
+ stop = jiffies + chip->vendor.timeout_d;
do {
burstcnt = ioread8(chip->vendor.iobase +
TPM_STS(chip->vendor.locality) + 1);
@@ -153,7 +157,7 @@ static int get_burstcount(struct tpm_chi
return -EBUSY;
}
-static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout,
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
wait_queue_head_t *queue)
{
unsigned long stop;
@@ -169,13 +173,11 @@ static int wait_for_stat(struct tpm_chip
rc = wait_event_interruptible_timeout(*queue,
((tpm_tis_status
(chip) & mask) ==
- mask),
- msecs_to_jiffies
- (timeout));
+ mask), timeout);
if (rc > 0)
return 0;
} else {
- stop = jiffies + (HZ * timeout / 1000);
+ stop = jiffies + timeout;
do {
msleep(TPM_TIMEOUT);
status = tpm_tis_status(chip);
@@ -453,10 +460,10 @@ static int __devinit tpm_tis_pnp_init(st
}
/* Default timeouts */
- chip->vendor.timeout_a = 750; /* ms */
- chip->vendor.timeout_b = 2000; /* 2 sec */
- chip->vendor.timeout_c = 750; /* ms */
- chip->vendor.timeout_d = 750; /* ms */
+ 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);
dev_info(&pnp_dev->dev,
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2006-04-12 21:47 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-10 14:37 [PATCH 7/7] tpm: Driver for next generation TPM chips Kylene Jo Hall
2006-04-10 22:03 ` Andrew Morton
2006-04-11 20:15 ` [PATCH] tpm: update to use wait_event calls Kylene Jo Hall
2006-04-11 20:31 ` Nish Aravamudan
2006-04-11 22:32 ` [PATCH] tpm: use wait_event return code and msecs_to_jiffies Kylene Jo Hall
2006-04-11 20:40 ` [PATCH] tpm: update to use wait_event calls Ingo Oeser
2006-04-11 23:05 ` [PATCH 7/7] tpm: Driver for next generation TPM chips Nishanth Aravamudan
2006-04-12 17:29 ` Kylene Jo Hall
2006-04-12 17:32 ` Nishanth Aravamudan
2006-04-12 21:48 ` [PATCH] tpm: msecs_to_jiffies cleanups Kylene Jo Hall
-- strict thread matches above, loose matches on Subject: below --
2006-04-05 19:48 [PATCH 7/7] tpm: Driver for next generation TPM chips Kylene Jo Hall
2006-04-03 16:42 Kylene Jo Hall
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox