* 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"
};
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))
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 &&
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;
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;
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;
}
return rc;
}
-static struct file_operations tis_ops = {
+static const struct file_operations tis_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = tpm_open,
.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 +
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;
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);
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);
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 */
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);
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",
.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);
}
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.
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);