drm/radeon/kms/pm: resurrect printing power states
[safe/jmp/linux-2.6] / drivers / firmware / dcdbas.c
index 955537f..aa9bc9e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *  See Documentation/dcdbas.txt for more information.
  *
- *  Copyright (C) 1995-2005 Dell Inc.
+ *  Copyright (C) 1995-2006 Dell Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License v2.0 as published by
  *  GNU General Public License for more details.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/errno.h>
+#include <linux/gfp.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/mc146818rtc.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/mutex.h>
 #include <asm/io.h>
-#include <asm/semaphore.h>
 
 #include "dcdbas.h"
 
 #define DRIVER_NAME            "dcdbas"
-#define DRIVER_VERSION         "5.6.0-1"
+#define DRIVER_VERSION         "5.6.0-3.2"
 #define DRIVER_DESCRIPTION     "Dell Systems Management Base Driver"
 
 static struct platform_device *dcdbas_pdev;
@@ -48,7 +49,7 @@ static u8 *smi_data_buf;
 static dma_addr_t smi_data_buf_handle;
 static unsigned long smi_data_buf_size;
 static u32 smi_data_buf_phys_addr;
-static DECLARE_MUTEX(smi_data_lock);
+static DEFINE_MUTEX(smi_data_lock);
 
 static unsigned int host_control_action;
 static unsigned int host_control_smi_type;
@@ -63,7 +64,7 @@ static void smi_data_buf_free(void)
                return;
 
        dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
-               __FUNCTION__, smi_data_buf_phys_addr, smi_data_buf_size);
+               __func__, smi_data_buf_phys_addr, smi_data_buf_size);
 
        dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf,
                          smi_data_buf_handle);
@@ -92,7 +93,7 @@ static int smi_data_buf_realloc(unsigned long size)
        if (!buf) {
                dev_dbg(&dcdbas_pdev->dev,
                        "%s: failed to allocate memory size %lu\n",
-                       __FUNCTION__, size);
+                       __func__, size);
                return -ENOMEM;
        }
        /* memory zeroed by dma_alloc_coherent */
@@ -110,7 +111,7 @@ static int smi_data_buf_realloc(unsigned long size)
        smi_data_buf_size = size;
 
        dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
-               __FUNCTION__, smi_data_buf_phys_addr, smi_data_buf_size);
+               __func__, smi_data_buf_phys_addr, smi_data_buf_size);
 
        return 0;
 }
@@ -139,42 +140,38 @@ static ssize_t smi_data_buf_size_store(struct device *dev,
        buf_size = simple_strtoul(buf, NULL, 10);
 
        /* make sure SMI data buffer is at least buf_size */
-       down(&smi_data_lock);
+       mutex_lock(&smi_data_lock);
        ret = smi_data_buf_realloc(buf_size);
-       up(&smi_data_lock);
+       mutex_unlock(&smi_data_lock);
        if (ret)
                return ret;
 
        return count;
 }
 
-static ssize_t smi_data_read(struct kobject *kobj, char *buf, loff_t pos,
-                            size_t count)
+static ssize_t smi_data_read(struct file *filp, struct kobject *kobj,
+                            struct bin_attribute *bin_attr,
+                            char *buf, loff_t pos, size_t count)
 {
-       size_t max_read;
        ssize_t ret;
 
-       down(&smi_data_lock);
-
-       if (pos >= smi_data_buf_size) {
-               ret = 0;
-               goto out;
-       }
-
-       max_read = smi_data_buf_size - pos;
-       ret = min(max_read, count);
-       memcpy(buf, smi_data_buf + pos, ret);
-out:
-       up(&smi_data_lock);
+       mutex_lock(&smi_data_lock);
+       ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf,
+                                       smi_data_buf_size);
+       mutex_unlock(&smi_data_lock);
        return ret;
 }
 
-static ssize_t smi_data_write(struct kobject *kobj, char *buf, loff_t pos,
-                             size_t count)
+static ssize_t smi_data_write(struct file *filp, struct kobject *kobj,
+                             struct bin_attribute *bin_attr,
+                             char *buf, loff_t pos, size_t count)
 {
        ssize_t ret;
 
-       down(&smi_data_lock);
+       if ((pos + count) > MAX_SMI_DATA_BUF_SIZE)
+               return -EINVAL;
+
+       mutex_lock(&smi_data_lock);
 
        ret = smi_data_buf_realloc(pos + count);
        if (ret)
@@ -183,7 +180,7 @@ static ssize_t smi_data_write(struct kobject *kobj, char *buf, loff_t pos,
        memcpy(smi_data_buf + pos, buf, count);
        ret = count;
 out:
-       up(&smi_data_lock);
+       mutex_unlock(&smi_data_lock);
        return ret;
 }
 
@@ -201,9 +198,9 @@ static ssize_t host_control_action_store(struct device *dev,
        ssize_t ret;
 
        /* make sure buffer is available for host control command */
-       down(&smi_data_lock);
+       mutex_lock(&smi_data_lock);
        ret = smi_data_buf_realloc(sizeof(struct apm_cmd));
-       up(&smi_data_lock);
+       mutex_unlock(&smi_data_lock);
        if (ret)
                return ret;
 
@@ -242,27 +239,30 @@ static ssize_t host_control_on_shutdown_store(struct device *dev,
 }
 
 /**
- * smi_request: generate SMI request
+ * dcdbas_smi_request: generate SMI request
  *
  * Called with smi_data_lock.
  */
-static int smi_request(struct smi_cmd *smi_cmd)
+int dcdbas_smi_request(struct smi_cmd *smi_cmd)
 {
-       cpumask_t old_mask;
+       cpumask_var_t old_mask;
        int ret = 0;
 
        if (smi_cmd->magic != SMI_CMD_MAGIC) {
                dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
-                        __FUNCTION__);
+                        __func__);
                return -EBADR;
        }
 
        /* SMI requires CPU 0 */
-       old_mask = current->cpus_allowed;
-       set_cpus_allowed(current, cpumask_of_cpu(0));
+       if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
+               return -ENOMEM;
+
+       cpumask_copy(old_mask, &current->cpus_allowed);
+       set_cpus_allowed_ptr(current, cpumask_of(0));
        if (smp_processor_id() != 0) {
                dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n",
-                       __FUNCTION__);
+                       __func__);
                ret = -EBUSY;
                goto out;
        }
@@ -279,7 +279,8 @@ static int smi_request(struct smi_cmd *smi_cmd)
        );
 
 out:
-       set_cpus_allowed(current, old_mask);
+       set_cpus_allowed_ptr(current, old_mask);
+       free_cpumask_var(old_mask);
        return ret;
 }
 
@@ -302,7 +303,7 @@ static ssize_t smi_request_store(struct device *dev,
        unsigned long val = simple_strtoul(buf, NULL, 10);
        ssize_t ret;
 
-       down(&smi_data_lock);
+       mutex_lock(&smi_data_lock);
 
        if (smi_data_buf_size < sizeof(struct smi_cmd)) {
                ret = -ENODEV;
@@ -313,14 +314,14 @@ static ssize_t smi_request_store(struct device *dev,
        switch (val) {
        case 2:
                /* Raw SMI */
-               ret = smi_request(smi_cmd);
+               ret = dcdbas_smi_request(smi_cmd);
                if (!ret)
                        ret = count;
                break;
        case 1:
                /* Calling Interface SMI */
                smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer);
-               ret = smi_request(smi_cmd);
+               ret = dcdbas_smi_request(smi_cmd);
                if (!ret)
                        ret = count;
                break;
@@ -334,9 +335,10 @@ static ssize_t smi_request_store(struct device *dev,
        }
 
 out:
-       up(&smi_data_lock);
+       mutex_unlock(&smi_data_lock);
        return ret;
 }
+EXPORT_SYMBOL(dcdbas_smi_request);
 
 /**
  * host_control_smi: generate host control SMI
@@ -423,7 +425,7 @@ static int host_control_smi(void)
 
        default:
                dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n",
-                       __FUNCTION__, host_control_smi_type);
+                       __func__, host_control_smi_type);
                return -ENOSYS;
        }
 
@@ -451,13 +453,13 @@ static void dcdbas_host_control(void)
        host_control_action = HC_ACTION_NONE;
 
        if (!smi_data_buf) {
-               dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __FUNCTION__);
+               dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__);
                return;
        }
 
        if (smi_data_buf_size < sizeof(struct apm_cmd)) {
                dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n",
-                       __FUNCTION__);
+                       __func__);
                return;
        }
 
@@ -483,26 +485,15 @@ static void dcdbas_host_control(void)
 static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code,
                                void *unused)
 {
-       static unsigned int notify_cnt = 0;
-
        switch (code) {
        case SYS_DOWN:
        case SYS_HALT:
        case SYS_POWER_OFF:
                if (host_control_on_shutdown) {
                        /* firmware is going to perform host control action */
-                       if (++notify_cnt == 2) {
-                               printk(KERN_WARNING
-                                      "Please wait for shutdown "
-                                      "action to complete...\n");
-                               dcdbas_host_control();
-                       }
-                       /*
-                        * register again and initiate the host control
-                        * action on the second notification to allow
-                        * everyone that registered to be notified
-                        */
-                       register_reboot_notifier(nb);
+                       printk(KERN_WARNING "Please wait for shutdown "
+                              "action to complete...\n");
+                       dcdbas_host_control();
                }
                break;
        }
@@ -513,7 +504,7 @@ static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code,
 static struct notifier_block dcdbas_reboot_nb = {
        .notifier_call = dcdbas_reboot_notify,
        .next = NULL,
-       .priority = 0
+       .priority = INT_MIN
 };
 
 static DCDBAS_BIN_ATTR_RW(smi_data);
@@ -530,50 +521,107 @@ static DCDBAS_DEV_ATTR_RW(host_control_action);
 static DCDBAS_DEV_ATTR_RW(host_control_smi_type);
 static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown);
 
-static struct device_attribute *dcdbas_dev_attrs[] = {
-       &dev_attr_smi_data_buf_size,
-       &dev_attr_smi_data_buf_phys_addr,
-       &dev_attr_smi_request,
-       &dev_attr_host_control_action,
-       &dev_attr_host_control_smi_type,
-       &dev_attr_host_control_on_shutdown,
+static struct attribute *dcdbas_dev_attrs[] = {
+       &dev_attr_smi_data_buf_size.attr,
+       &dev_attr_smi_data_buf_phys_addr.attr,
+       &dev_attr_smi_request.attr,
+       &dev_attr_host_control_action.attr,
+       &dev_attr_host_control_smi_type.attr,
+       &dev_attr_host_control_on_shutdown.attr,
        NULL
 };
 
-/**
- * dcdbas_init: initialize driver
- */
-static int __init dcdbas_init(void)
+static struct attribute_group dcdbas_attr_group = {
+       .attrs = dcdbas_dev_attrs,
+};
+
+static int __devinit dcdbas_probe(struct platform_device *dev)
 {
-       int i;
+       int i, error;
 
        host_control_action = HC_ACTION_NONE;
        host_control_smi_type = HC_SMITYPE_NONE;
 
-       dcdbas_pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
-       if (IS_ERR(dcdbas_pdev))
-               return PTR_ERR(dcdbas_pdev);
-
        /*
         * BIOS SMI calls require buffer addresses be in 32-bit address space.
         * This is done by setting the DMA mask below.
         */
-       dcdbas_pdev->dev.coherent_dma_mask = DMA_32BIT_MASK;
+       dcdbas_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
        dcdbas_pdev->dev.dma_mask = &dcdbas_pdev->dev.coherent_dma_mask;
 
+       error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group);
+       if (error)
+               return error;
+
+       for (i = 0; dcdbas_bin_attrs[i]; i++) {
+               error = sysfs_create_bin_file(&dev->dev.kobj,
+                                             dcdbas_bin_attrs[i]);
+               if (error) {
+                       while (--i >= 0)
+                               sysfs_remove_bin_file(&dev->dev.kobj,
+                                                     dcdbas_bin_attrs[i]);
+                       sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
+                       return error;
+               }
+       }
+
        register_reboot_notifier(&dcdbas_reboot_nb);
 
+       dev_info(&dev->dev, "%s (version %s)\n",
+                DRIVER_DESCRIPTION, DRIVER_VERSION);
+
+       return 0;
+}
+
+static int __devexit dcdbas_remove(struct platform_device *dev)
+{
+       int i;
+
+       unregister_reboot_notifier(&dcdbas_reboot_nb);
        for (i = 0; dcdbas_bin_attrs[i]; i++)
-               sysfs_create_bin_file(&dcdbas_pdev->dev.kobj,
-                                     dcdbas_bin_attrs[i]);
+               sysfs_remove_bin_file(&dev->dev.kobj, dcdbas_bin_attrs[i]);
+       sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
 
-       for (i = 0; dcdbas_dev_attrs[i]; i++)
-               device_create_file(&dcdbas_pdev->dev, dcdbas_dev_attrs[i]);
+       return 0;
+}
 
-       dev_info(&dcdbas_pdev->dev, "%s (version %s)\n",
-                DRIVER_DESCRIPTION, DRIVER_VERSION);
+static struct platform_driver dcdbas_driver = {
+       .driver         = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = dcdbas_probe,
+       .remove         = __devexit_p(dcdbas_remove),
+};
+
+/**
+ * dcdbas_init: initialize driver
+ */
+static int __init dcdbas_init(void)
+{
+       int error;
+
+       error = platform_driver_register(&dcdbas_driver);
+       if (error)
+               return error;
+
+       dcdbas_pdev = platform_device_alloc(DRIVER_NAME, -1);
+       if (!dcdbas_pdev) {
+               error = -ENOMEM;
+               goto err_unregister_driver;
+       }
+
+       error = platform_device_add(dcdbas_pdev);
+       if (error)
+               goto err_free_device;
 
        return 0;
+
+ err_free_device:
+       platform_device_put(dcdbas_pdev);
+ err_unregister_driver:
+       platform_driver_unregister(&dcdbas_driver);
+       return error;
 }
 
 /**
@@ -581,9 +629,22 @@ static int __init dcdbas_init(void)
  */
 static void __exit dcdbas_exit(void)
 {
-       platform_device_unregister(dcdbas_pdev);
+       /*
+        * make sure functions that use dcdbas_pdev are called
+        * before platform_device_unregister
+        */
        unregister_reboot_notifier(&dcdbas_reboot_nb);
        smi_data_buf_free();
+       platform_device_unregister(dcdbas_pdev);
+       platform_driver_unregister(&dcdbas_driver);
+
+       /*
+        * We have to free the buffer here instead of dcdbas_remove
+        * because only in module exit function we can be sure that
+        * all sysfs attributes belonging to this module have been
+        * released.
+        */
+       smi_data_buf_free();
 }
 
 module_init(dcdbas_init);
@@ -593,4 +654,5 @@ MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")");
 MODULE_VERSION(DRIVER_VERSION);
 MODULE_AUTHOR("Dell Inc.");
 MODULE_LICENSE("GPL");
-
+/* Any System or BIOS claiming to be by Dell */
+MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*");