include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / char / tpm / tpm_tis.c
index 1cb5a7f..9434599 100644 (file)
@@ -5,6 +5,8 @@
  * Leendert van Doorn <leendert@watson.ibm.com>
  * Kylene Hall <kjhall@us.ibm.com>
  *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
  * Device driver for TCG/TCPA TPM (trusted platform module).
  * Specifications at www.trustedcomputinggroup.org
  *
  * published by the Free Software Foundation, version 2 of the
  * License.
  */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/pnp.h>
+#include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include "tpm.h"
@@ -52,8 +58,10 @@ enum tis_int_flags {
 };
 
 enum tis_defaults {
-       TIS_SHORT_TIMEOUT = 750, /* ms */
-       TIS_LONG_TIMEOUT = 2000, /* 2 sec */
+       TIS_MEM_BASE = 0xFED40000,
+       TIS_MEM_LEN = 0x5000,
+       TIS_SHORT_TIMEOUT = 750,        /* ms */
+       TIS_LONG_TIMEOUT = 2000,        /* 2 sec */
 };
 
 #define        TPM_ACCESS(l)                   (0x0000 | ((l) << 12))
@@ -186,7 +194,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
        return -ETIME;
 }
 
-static int recv_data(struct tpm_chip *chip, u8 * buf, size_t count)
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
 {
        int size = 0, burstcnt;
        while (size < count &&
@@ -204,7 +212,7 @@ static int recv_data(struct tpm_chip *chip, u8 * buf, size_t count)
        return size;
 }
 
-static int tpm_tis_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 {
        int size = 0;
        int expected, status;
@@ -250,12 +258,16 @@ out:
        return size;
 }
 
+static int itpm;
+module_param(itpm, bool, 0444);
+MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
+
 /*
  * 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)
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
 {
        int rc, status, burstcnt;
        size_t count = 0;
@@ -286,7 +298,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len)
                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) {
+               if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
                        rc = -EIO;
                        goto out_err;
                }
@@ -325,7 +337,7 @@ out_err:
        return rc;
 }
 
-static struct file_operations tis_ops = {
+static const struct file_operations tis_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
        .open = tpm_open,
@@ -372,10 +384,9 @@ static struct tpm_vendor_specific tpm_tis = {
                    .fops = &tis_ops,},
 };
 
-static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs
-                                *regs)
+static irqreturn_t tis_int_probe(int irq, void *dev_id)
 {
-       struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+       struct tpm_chip *chip = dev_id;
        u32 interrupt;
 
        interrupt = ioread32(chip->vendor.iobase +
@@ -393,10 +404,9 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs
        return IRQ_HANDLED;
 }
 
-static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs
-                                  *regs)
+static irqreturn_t tis_int_handler(int dummy, void *dev_id)
 {
-       struct tpm_chip *chip = (struct tpm_chip *) dev_id;
+       struct tpm_chip *chip = dev_id;
        u32 interrupt;
        int i;
 
@@ -421,23 +431,22 @@ static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs
        iowrite32(interrupt,
                  chip->vendor.iobase +
                  TPM_INT_STATUS(chip->vendor.locality));
+       ioread32(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)
+static int interrupts = 1;
+module_param(interrupts, bool, 0444);
+MODULE_PARM_DESC(interrupts, "Enable interrupts");
+
+static int tpm_tis_init(struct device *dev, resource_size_t start,
+                       resource_size_t len, unsigned int irq)
 {
        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)))
+       if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
                return -ENODEV;
 
        chip->vendor.iobase = ioremap(start, len);
@@ -446,51 +455,51 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev
                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 = 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,
+       if (request_locality(chip, 0) != 0) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+
+       dev_info(dev,
                 "1.2 TPM (device-id 0x%X, rev-id %d)\n",
                 vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
 
+       if (itpm)
+               dev_info(dev, "Intel iTPM workaround enabled\n");
+
+
        /* 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",
+       dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
                intfcaps);
        if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
-               dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n");
+               dev_dbg(dev, "\tBurst Count Static\n");
        if (intfcaps & TPM_INTF_CMD_READY_INT)
-               dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n");
+               dev_dbg(dev, "\tCommand Ready Int Support\n");
        if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
-               dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n");
+               dev_dbg(dev, "\tInterrupt Edge Falling\n");
        if (intfcaps & TPM_INTF_INT_EDGE_RISING)
-               dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n");
+               dev_dbg(dev, "\tInterrupt Edge Rising\n");
        if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
-               dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n");
+               dev_dbg(dev, "\tInterrupt Level Low\n");
        if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
-               dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n");
+               dev_dbg(dev, "\tInterrupt Level High\n");
        if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
-               dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n");
+               dev_dbg(dev, "\tLocality Change Int Support\n");
        if (intfcaps & TPM_INTF_STS_VALID_INT)
-               dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n");
+               dev_dbg(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;
-       }
+               dev_dbg(dev, "\tData Avail Int Support\n");
 
        /* INTERRUPT Setup */
        init_waitqueue_head(&chip->vendor.read_queue);
@@ -507,54 +516,57 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev
        iowrite32(intmask,
                  chip->vendor.iobase +
                  TPM_INT_ENABLE(chip->vendor.locality));
+       if (interrupts)
+               chip->vendor.irq = irq;
+       if (interrupts && !chip->vendor.irq) {
+               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, IRQF_SHARED,
+                            chip->vendor.miscdev.name, chip) != 0) {
+                               dev_info(chip->dev,
+                                        "Unable to request irq: %d for probe\n",
+                                        i);
+                               continue;
+                       }
 
-       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));
+                       /* 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));
+                       /* Turn on */
+                       iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
+                                 chip->vendor.iobase +
+                                 TPM_INT_ENABLE(chip->vendor.locality));
 
-               /* Generate Interrupts */
-               tpm_gen_interrupt(chip);
+                       /* Generate Interrupts */
+                       tpm_gen_interrupt(chip);
 
-               /* Turn off */
-               iowrite32(intmask,
-                         chip->vendor.iobase +
-                         TPM_INT_ENABLE(chip->vendor.locality));
-               free_irq(i, 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.irq, tis_int_handler, IRQF_SHARED,
                     chip->vendor.miscdev.name, chip) != 0) {
                        dev_info(chip->dev,
-                                "Unable to request irq: %d for use\n", i);
+                                "Unable to request irq: %d for use\n",
+                                chip->vendor.irq);
                        chip->vendor.irq = 0;
                } else {
                        /* Clear all existing */
@@ -587,6 +599,23 @@ out_err:
        return rc;
 }
 
+static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
+                                     const struct pnp_device_id *pnp_id)
+{
+       resource_size_t start, len;
+       unsigned int irq = 0;
+
+       start = pnp_mem_start(pnp_dev, 0);
+       len = pnp_mem_len(pnp_dev, 0);
+
+       if (pnp_irq_valid(pnp_dev, 0))
+               irq = pnp_irq(pnp_dev, 0);
+       else
+               interrupts = 0;
+
+       return tpm_tis_init(&pnp_dev->dev, start, len, irq);
+}
+
 static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg)
 {
        return tpm_pm_suspend(&dev->dev, msg);
@@ -599,8 +628,27 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
 
 static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
        {"PNP0C31", 0},         /* TPM */
-       {"", 0}
+       {"ATM1200", 0},         /* Atmel */
+       {"IFX0102", 0},         /* Infineon */
+       {"BCM0101", 0},         /* Broadcom */
+       {"BCM0102", 0},         /* Broadcom */
+       {"NSC1200", 0},         /* National */
+       {"ICO0102", 0},         /* Intel */
+       /* Add new here */
+       {"", 0},                /* User Specified */
+       {"", 0}                 /* Terminator */
 };
+MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
+
+static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
+{
+       struct tpm_chip *chip = pnp_get_drvdata(dev);
+
+       tpm_dev_vendor_release(chip);
+
+       kfree(chip);
+}
+
 
 static struct pnp_driver tis_pnp_driver = {
        .name = "tpm_tis",
@@ -608,10 +656,54 @@ static struct pnp_driver tis_pnp_driver = {
        .probe = tpm_tis_pnp_init,
        .suspend = tpm_tis_pnp_suspend,
        .resume = tpm_tis_pnp_resume,
+       .remove = tpm_tis_pnp_remove,
+};
+
+#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2
+module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
+                   sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
+MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
+
+static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
+{
+       return tpm_pm_suspend(&dev->dev, msg);
+}
+
+static int tpm_tis_resume(struct platform_device *dev)
+{
+       return tpm_pm_resume(&dev->dev);
+}
+static struct platform_driver tis_drv = {
+       .driver = {
+               .name = "tpm_tis",
+               .owner          = THIS_MODULE,
+       },
+       .suspend = tpm_tis_suspend,
+       .resume = tpm_tis_resume,
 };
 
+static struct platform_device *pdev;
+
+static int force;
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
 static int __init init_tis(void)
 {
+       int rc;
+
+       if (force) {
+               rc = platform_driver_register(&tis_drv);
+               if (rc < 0)
+                       return rc;
+               if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0)))
+                       return PTR_ERR(pdev);
+               if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) {
+                       platform_device_unregister(pdev);
+                       platform_driver_unregister(&tis_drv);
+               }
+               return rc;
+       }
+
        return pnp_register_driver(&tis_pnp_driver);
 }
 
@@ -622,6 +714,7 @@ static void __exit cleanup_tis(void)
        spin_lock(&tis_lock);
        list_for_each_entry_safe(i, j, &tis_chips, list) {
                chip = to_tpm_chip(i);
+               tpm_remove_hardware(chip->dev);
                iowrite32(~TPM_GLOBAL_INT_ENABLE &
                          ioread32(chip->vendor.iobase +
                                   TPM_INT_ENABLE(chip->vendor.
@@ -633,10 +726,14 @@ static void __exit cleanup_tis(void)
                        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);
+
+       if (force) {
+               platform_device_unregister(pdev);
+               platform_driver_unregister(&tis_drv);
+       } else
+               pnp_unregister_driver(&tis_pnp_driver);
 }
 
 module_init(init_tis);