Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[pandora-kernel.git] / drivers / char / tpm / tpm.c
index 39564b7..e1fc193 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/poll.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
+#include <linux/smp_lock.h>
 
 #include "tpm.h"
 
@@ -897,6 +898,7 @@ int tpm_open(struct inode *inode, struct file *file)
        int rc = 0, minor = iminor(inode);
        struct tpm_chip *chip = NULL, *pos;
 
+       lock_kernel();
        spin_lock(&driver_lock);
 
        list_for_each_entry(pos, &tpm_chip_list, list) {
@@ -926,16 +928,19 @@ int tpm_open(struct inode *inode, struct file *file)
        if (chip->data_buffer == NULL) {
                chip->num_opens--;
                put_device(chip->dev);
+               unlock_kernel();
                return -ENOMEM;
        }
 
        atomic_set(&chip->data_pending, 0);
 
        file->private_data = chip;
+       unlock_kernel();
        return 0;
 
 err_out:
        spin_unlock(&driver_lock);
+       unlock_kernel();
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_open);
@@ -1031,27 +1036,16 @@ void tpm_remove_hardware(struct device *dev)
 
        spin_unlock(&driver_lock);
 
-       dev_set_drvdata(dev, NULL);
        misc_deregister(&chip->vendor.miscdev);
-       kfree(chip->vendor.miscdev.name);
 
        sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
        tpm_bios_log_teardown(chip->bios_dir);
 
-       clear_bit(chip->dev_num, dev_mask);
-
-       kfree(chip);
-
-       put_device(dev);
+       /* write it this way to be explicit (chip->dev == dev) */
+       put_device(chip->dev);
 }
 EXPORT_SYMBOL_GPL(tpm_remove_hardware);
 
-static u8 savestate[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 10,            /* blob length (in bytes) */
-       0, 0, 0, 152            /* TPM_ORD_SaveState */
-};
-
 /*
  * We are about to suspend. Save the TPM state
  * so that it can be restored.
@@ -1059,6 +1053,12 @@ static u8 savestate[] = {
 int tpm_pm_suspend(struct device *dev, pm_message_t pm_state)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
+       u8 savestate[] = {
+               0, 193,         /* TPM_TAG_RQU_COMMAND */
+               0, 0, 0, 10,    /* blob length (in bytes) */
+               0, 0, 0, 152    /* TPM_ORD_SaveState */
+       };
+
        if (chip == NULL)
                return -ENODEV;
 
@@ -1082,6 +1082,26 @@ int tpm_pm_resume(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(tpm_pm_resume);
 
+/*
+ * Once all references to platform device are down to 0,
+ * release all allocated structures.
+ * In case vendor provided release function,
+ * call it too.
+ */
+static void tpm_dev_release(struct device *dev)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+
+       if (chip->vendor.release)
+               chip->vendor.release(dev);
+
+       chip->release(dev);
+
+       clear_bit(chip->dev_num, dev_mask);
+       kfree(chip->vendor.miscdev.name);
+       kfree(chip);
+}
+
 /*
  * Called from tpm_<specific>.c probe function only for devices 
  * the driver has determined it should claim.  Prior to calling
@@ -1136,23 +1156,21 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
 
        chip->vendor.miscdev.parent = dev;
        chip->dev = get_device(dev);
+       chip->release = dev->release;
+       dev->release = tpm_dev_release;
+       dev_set_drvdata(dev, chip);
 
        if (misc_register(&chip->vendor.miscdev)) {
                dev_err(chip->dev,
                        "unable to misc_register %s, minor %d\n",
                        chip->vendor.miscdev.name,
                        chip->vendor.miscdev.minor);
-               put_device(dev);
-               clear_bit(chip->dev_num, dev_mask);
-               kfree(chip);
-               kfree(devname);
+               put_device(chip->dev);
                return NULL;
        }
 
        spin_lock(&driver_lock);
 
-       dev_set_drvdata(dev, chip);
-
        list_add(&chip->list, &tpm_chip_list);
 
        spin_unlock(&driver_lock);
@@ -1160,10 +1178,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend
        if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) {
                list_del(&chip->list);
                misc_deregister(&chip->vendor.miscdev);
-               put_device(dev);
-               clear_bit(chip->dev_num, dev_mask);
-               kfree(chip);
-               kfree(devname);
+               put_device(chip->dev);
                return NULL;
        }