]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/firmware/dell_rbu.c
Merge master.kernel.org:/home/rmk/linux-2.6-mmc
[linux-2.6-omap-h63xx.git] / drivers / firmware / dell_rbu.c
index 125929c9048f0be47b4e59543d79a5b62a0e0ff6..fdb8b042e64d121f3e4a9be60719efc25cf17f0c 100644 (file)
@@ -34,7 +34,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-#include <linux/version.h>
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -50,7 +49,7 @@
 MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
 MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
 MODULE_LICENSE("GPL");
-MODULE_VERSION("3.0");
+MODULE_VERSION("3.2");
 
 #define BIOS_SCAN_LIMIT 0xffffffff
 #define MAX_IMAGE_LENGTH 16
@@ -73,6 +72,11 @@ module_param_string(image_type, image_type, sizeof (image_type), 0);
 MODULE_PARM_DESC(image_type,
        "BIOS image type. choose- mono or packet or init");
 
+static unsigned long allocation_floor = 0x100000;
+module_param(allocation_floor, ulong, 0644);
+MODULE_PARM_DESC(allocation_floor,
+    "Minimum address for allocations when using Packet mode");
+
 struct packet_data {
        struct list_head list;
        size_t length;
@@ -99,61 +103,122 @@ static int create_packet(void *data, size_t length)
 {
        struct packet_data *newpacket;
        int ordernum = 0;
+       int retval = 0;
+       unsigned int packet_array_size = 0;
+       void **invalid_addr_packet_array = NULL;
+       void *packet_data_temp_buf = NULL;
+       unsigned int idx = 0;
 
        pr_debug("create_packet: entry \n");
 
        if (!rbu_data.packetsize) {
                pr_debug("create_packet: packetsize not specified\n");
-               return -EINVAL;
+               retval = -EINVAL;
+               goto out_noalloc;
        }
+
        spin_unlock(&rbu_data.lock);
-       newpacket = kmalloc(sizeof (struct packet_data), GFP_KERNEL);
-       spin_lock(&rbu_data.lock);
+
+       newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL);
 
        if (!newpacket) {
                printk(KERN_WARNING
                        "dell_rbu:%s: failed to allocate new "
                        "packet\n", __FUNCTION__);
-               return -ENOMEM;
+               retval = -ENOMEM;
+               spin_lock(&rbu_data.lock);
+               goto out_noalloc;
        }
 
        ordernum = get_order(length);
+
        /*
-        * there is no upper limit on memory
-        * address for packetized mechanism
+        * BIOS errata mean we cannot allocate packets below 1MB or they will
+        * be overwritten by BIOS.
+        *
+        * array to temporarily hold packets
+        * that are below the allocation floor
+        *
+        * NOTE: very simplistic because we only need the floor to be at 1MB
+        *       due to BIOS errata. This shouldn't be used for higher floors
+        *       or you will run out of mem trying to allocate the array.
         */
-       spin_unlock(&rbu_data.lock);
-       newpacket->data = (unsigned char *) __get_free_pages(GFP_KERNEL,
-               ordernum);
-       spin_lock(&rbu_data.lock);
-
-       pr_debug("create_packet: newpacket %p\n", newpacket->data);
+       packet_array_size = max(
+                       (unsigned int)(allocation_floor / rbu_data.packetsize),
+                       (unsigned int)1);
+       invalid_addr_packet_array = kzalloc(packet_array_size * sizeof(void*),
+                                               GFP_KERNEL);
 
-       if (!newpacket->data) {
+       if (!invalid_addr_packet_array) {
                printk(KERN_WARNING
-                       "dell_rbu:%s: failed to allocate new "
-                       "packet\n", __FUNCTION__);
-               kfree(newpacket);
-               return -ENOMEM;
+                       "dell_rbu:%s: failed to allocate "
+                       "invalid_addr_packet_array \n",
+                       __FUNCTION__);
+               retval = -ENOMEM;
+               spin_lock(&rbu_data.lock);
+               goto out_alloc_packet;
+       }
+
+       while (!packet_data_temp_buf) {
+               packet_data_temp_buf = (unsigned char *)
+                       __get_free_pages(GFP_KERNEL, ordernum);
+               if (!packet_data_temp_buf) {
+                       printk(KERN_WARNING
+                               "dell_rbu:%s: failed to allocate new "
+                               "packet\n", __FUNCTION__);
+                       retval = -ENOMEM;
+                       spin_lock(&rbu_data.lock);
+                       goto out_alloc_packet_array;
+               }
+
+               if ((unsigned long)virt_to_phys(packet_data_temp_buf)
+                               < allocation_floor) {
+                       pr_debug("packet 0x%lx below floor at 0x%lx.\n",
+                                       (unsigned long)virt_to_phys(
+                                               packet_data_temp_buf),
+                                       allocation_floor);
+                       invalid_addr_packet_array[idx++] = packet_data_temp_buf;
+                       packet_data_temp_buf = NULL;
+               }
        }
+       spin_lock(&rbu_data.lock);
+
+       newpacket->data = packet_data_temp_buf;
+
+       pr_debug("create_packet: newpacket at physical addr %lx\n",
+               (unsigned long)virt_to_phys(newpacket->data));
 
+       /* packets may not have fixed size */
+       newpacket->length = length;
        newpacket->ordernum = ordernum;
        ++rbu_data.num_packets;
-       /*
-        * initialize the newly created packet headers
-        */
+
+       /* initialize the newly created packet headers */
        INIT_LIST_HEAD(&newpacket->list);
        list_add_tail(&newpacket->list, &packet_data_head.list);
-       /*
-        * packets may not have fixed size
-        */
-       newpacket->length = length;
 
        memcpy(newpacket->data, data, length);
 
        pr_debug("create_packet: exit \n");
 
-       return 0;
+out_alloc_packet_array:
+       /* always free packet array */
+       for (;idx>0;idx--) {
+               pr_debug("freeing unused packet below floor 0x%lx.\n",
+                       (unsigned long)virt_to_phys(
+                               invalid_addr_packet_array[idx-1]));
+               free_pages((unsigned long)invalid_addr_packet_array[idx-1],
+                       ordernum);
+       }
+       kfree(invalid_addr_packet_array);
+
+out_alloc_packet:
+       /* if error, free data */
+       if (retval)
+               kfree(newpacket);
+
+out_noalloc:
+       return retval;
 }
 
 static int packetize_data(void *data, size_t length)
@@ -499,12 +564,10 @@ static ssize_t read_rbu_data(struct kobject *kobj, char *buffer,
 
 static void callbackfn_rbu(const struct firmware *fw, void *context)
 {
-       int rc = 0;
+       rbu_data.entry_created = 0;
 
-       if (!fw || !fw->size) {
-               rbu_data.entry_created = 0;
+       if (!fw || !fw->size)
                return;
-       }
 
        spin_lock(&rbu_data.lock);
        if (!strcmp(image_type, "mono")) {
@@ -527,15 +590,6 @@ static void callbackfn_rbu(const struct firmware *fw, void *context)
        } else
                pr_debug("invalid image type specified.\n");
        spin_unlock(&rbu_data.lock);
-
-       rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
-               "dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
-       if (rc)
-               printk(KERN_ERR
-                       "dell_rbu:%s request_firmware_nowait failed"
-                       " %d\n", __FUNCTION__, rc);
-       else
-               rbu_data.entry_created = 1;
 }
 
 static ssize_t read_rbu_image_type(struct kobject *kobj, char *buffer,
@@ -670,14 +724,7 @@ static int __init dcdrbu_init(void)
        sysfs_create_bin_file(&rbu_device->dev.kobj,
                &rbu_packet_size_attr);
 
-       rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
-               "dell_rbu", &rbu_device->dev, &context, callbackfn_rbu);
-       if (rc)
-               printk(KERN_ERR "dell_rbu:%s:request_firmware_nowait"
-                       " failed %d\n", __FUNCTION__, rc);
-       else
-               rbu_data.entry_created = 1;
-
+       rbu_data.entry_created = 0;
        return rc;
 
 }
@@ -693,3 +740,6 @@ static __exit void dcdrbu_exit(void)
 
 module_exit(dcdrbu_exit);
 module_init(dcdrbu_init);
+
+/* vim:noet:ts=8:sw=8
+*/