mlx4_en: Fix a kernel panic when waking tx queue
[safe/jmp/linux-2.6] / drivers / firewire / fw-device.c
index ac5043c..a47e212 100644 (file)
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#include <linux/module.h>
-#include <linux/wait.h>
-#include <linux/errno.h>
-#include <linux/kthread.h>
-#include <linux/device.h>
+#include <linux/ctype.h>
 #include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
 #include <linux/idr.h>
 #include <linux/jiffies.h>
-#include <linux/string.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/rwsem.h>
 #include <linux/semaphore.h>
 #include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+
 #include <asm/system.h>
-#include <linux/ctype.h>
-#include "fw-transaction.h"
-#include "fw-topology.h"
+
 #include "fw-device.h"
+#include "fw-topology.h"
+#include "fw-transaction.h"
 
 void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p)
 {
@@ -153,27 +155,6 @@ struct bus_type fw_bus_type = {
 };
 EXPORT_SYMBOL(fw_bus_type);
 
-static void fw_device_release(struct device *dev)
-{
-       struct fw_device *device = fw_device(dev);
-       struct fw_card *card = device->card;
-       unsigned long flags;
-
-       /*
-        * Take the card lock so we don't set this to NULL while a
-        * FW_NODE_UPDATED callback is being handled or while the
-        * bus manager work looks at this node.
-        */
-       spin_lock_irqsave(&card->lock, flags);
-       device->node->data = NULL;
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       fw_node_put(device->node);
-       kfree(device->config_rom);
-       kfree(device);
-       fw_card_put(card);
-}
-
 int fw_device_enable_phys_dma(struct fw_device *device)
 {
        int generation = device->generation;
@@ -537,7 +518,7 @@ static int read_bus_info_block(struct fw_device *device, int generation)
 
        kfree(old_rom);
        ret = 0;
-       device->cmc = rom[2] & 1 << 30;
+       device->cmc = rom[2] >> 30 & 1;
  out:
        kfree(rom);
 
@@ -677,11 +658,53 @@ static void fw_device_shutdown(struct work_struct *work)
        fw_device_put(device);
 }
 
+static void fw_device_release(struct device *dev)
+{
+       struct fw_device *device = fw_device(dev);
+       struct fw_card *card = device->card;
+       unsigned long flags;
+
+       /*
+        * Take the card lock so we don't set this to NULL while a
+        * FW_NODE_UPDATED callback is being handled or while the
+        * bus manager work looks at this node.
+        */
+       spin_lock_irqsave(&card->lock, flags);
+       device->node->data = NULL;
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       fw_node_put(device->node);
+       kfree(device->config_rom);
+       kfree(device);
+       fw_card_put(card);
+}
+
 static struct device_type fw_device_type = {
-       .release        = fw_device_release,
+       .release = fw_device_release,
 };
 
-static void fw_device_update(struct work_struct *work);
+static int update_unit(struct device *dev, void *data)
+{
+       struct fw_unit *unit = fw_unit(dev);
+       struct fw_driver *driver = (struct fw_driver *)dev->driver;
+
+       if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
+               down(&dev->sem);
+               driver->update(unit);
+               up(&dev->sem);
+       }
+
+       return 0;
+}
+
+static void fw_device_update(struct work_struct *work)
+{
+       struct fw_device *device =
+               container_of(work, struct fw_device, work.work);
+
+       fw_device_cdev_update(device);
+       device_for_each_child(&device->device, NULL, update_unit);
+}
 
 /*
  * If a device was pending for deletion because its node went away but its
@@ -733,12 +756,50 @@ static int lookup_existing_device(struct device *dev, void *data)
        return match;
 }
 
+enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, };
+
+void fw_device_set_broadcast_channel(struct fw_device *device, int generation)
+{
+       struct fw_card *card = device->card;
+       __be32 data;
+       int rcode;
+
+       if (!card->broadcast_channel_allocated)
+               return;
+
+       if (device->bc_implemented == BC_UNKNOWN) {
+               rcode = fw_run_transaction(card, TCODE_READ_QUADLET_REQUEST,
+                               device->node_id, generation, device->max_speed,
+                               CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
+                               &data, 4);
+               switch (rcode) {
+               case RCODE_COMPLETE:
+                       if (data & cpu_to_be32(1 << 31)) {
+                               device->bc_implemented = BC_IMPLEMENTED;
+                               break;
+                       }
+                       /* else fall through to case address error */
+               case RCODE_ADDRESS_ERROR:
+                       device->bc_implemented = BC_UNIMPLEMENTED;
+               }
+       }
+
+       if (device->bc_implemented == BC_IMPLEMENTED) {
+               data = cpu_to_be32(BROADCAST_CHANNEL_INITIAL |
+                                  BROADCAST_CHANNEL_VALID);
+               fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST,
+                               device->node_id, generation, device->max_speed,
+                               CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
+                               &data, 4);
+       }
+}
+
 static void fw_device_init(struct work_struct *work)
 {
        struct fw_device *device =
                container_of(work, struct fw_device, work.work);
        struct device *revived_dev;
-       int minor, err;
+       int minor, ret;
 
        /*
         * All failure paths here set node->data to NULL, so that we
@@ -774,12 +835,12 @@ static void fw_device_init(struct work_struct *work)
 
        fw_device_get(device);
        down_write(&fw_device_rwsem);
-       err = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
+       ret = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
              idr_get_new(&fw_device_idr, device, &minor) :
              -ENOMEM;
        up_write(&fw_device_rwsem);
 
-       if (err < 0)
+       if (ret < 0)
                goto error;
 
        device->device.bus = &fw_bus_type;
@@ -826,6 +887,8 @@ static void fw_device_init(struct work_struct *work)
                                  device->config_rom[3], device->config_rom[4],
                                  1 << device->max_speed);
                device->config_rom_retries = 0;
+
+               fw_device_set_broadcast_channel(device, device->generation);
        }
 
        /*
@@ -849,29 +912,6 @@ static void fw_device_init(struct work_struct *work)
        put_device(&device->device);    /* our reference */
 }
 
-static int update_unit(struct device *dev, void *data)
-{
-       struct fw_unit *unit = fw_unit(dev);
-       struct fw_driver *driver = (struct fw_driver *)dev->driver;
-
-       if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
-               down(&dev->sem);
-               driver->update(unit);
-               up(&dev->sem);
-       }
-
-       return 0;
-}
-
-static void fw_device_update(struct work_struct *work)
-{
-       struct fw_device *device =
-               container_of(work, struct fw_device, work.work);
-
-       fw_device_cdev_update(device);
-       device_for_each_child(&device->device, NULL, update_unit);
-}
-
 enum {
        REREAD_BIB_ERROR,
        REREAD_BIB_GONE,
@@ -892,7 +932,7 @@ static int reread_bus_info_block(struct fw_device *device, int generation)
                if (i == 0 && q == 0)
                        return REREAD_BIB_GONE;
 
-               if (i > device->config_rom_length || q != device->config_rom[i])
+               if (q != device->config_rom[i])
                        return REREAD_BIB_CHANGED;
        }