]> err.no Git - linux-2.6/blobdiff - drivers/firewire/fw-card.c
firewire: Rename 'send_iso' to 'start_iso'.
[linux-2.6] / drivers / firewire / fw-card.c
index 79773907e10ddaeb51c60d245f200aa7f8d0a737..3f8661a52acce16af5e49e1876325d245051ef3e 100644 (file)
@@ -76,12 +76,12 @@ generate_config_rom (struct fw_card *card, size_t *config_rom_length)
        static u32 config_rom[256];
        int i, j, length;
 
-        /* Initialize contents of config rom buffer.  On the OHCI
-         * controller, block reads to the config rom accesses the host
-         * memory, but quadlet read access the hardware bus info block
-         * registers.  That's just crack, but it means we should make
-         * sure the contents of bus info block in host memory mathces
-         * the version stored in the OHCI registers. */
+       /* Initialize contents of config rom buffer.  On the OHCI
+        * controller, block reads to the config rom accesses the host
+        * memory, but quadlet read access the hardware bus info block
+        * registers.  That's just crack, but it means we should make
+        * sure the contents of bus info block in host memory mathces
+        * the version stored in the OHCI registers. */
 
        memset(config_rom, 0, sizeof config_rom);
        config_rom[0] = bib_crc_length(4) | bib_info_length(4) | bib_crc(0);
@@ -92,7 +92,7 @@ generate_config_rom (struct fw_card *card, size_t *config_rom_length)
                bib_generation(card->config_rom_generation++ % 14 + 2) |
                bib_max_rom(2) |
                bib_max_receive(card->max_receive) |
-               bib_isc | bib_cmc | bib_imc;
+               bib_bmc | bib_isc | bib_cmc | bib_imc;
        config_rom[3] = card->guid >> 32;
        config_rom[4] = card->guid;
 
@@ -186,55 +186,162 @@ fw_core_remove_descriptor (struct fw_descriptor *desc)
 }
 EXPORT_SYMBOL(fw_core_remove_descriptor);
 
+static const char gap_count_table[] = {
+       63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
+};
+
+struct bm_data {
+       struct fw_transaction t;
+       struct {
+               __be32 arg;
+               __be32 data;
+       } lock;
+       u32 old;
+       int rcode;
+       struct completion done;
+};
+
 static void
-fw_card_irm_work(struct work_struct *work)
+complete_bm_lock(struct fw_card *card, int rcode,
+                void *payload, size_t length, void *data)
 {
-       struct fw_card *card =
-               container_of(work, struct fw_card, work.work);
+       struct bm_data *bmd = data;
+
+       if (rcode == RCODE_COMPLETE)
+               bmd->old = be32_to_cpu(*(__be32 *) payload);
+       bmd->rcode = rcode;
+       complete(&bmd->done);
+}
+
+static void
+fw_card_bm_work(struct work_struct *work)
+{
+       struct fw_card *card = container_of(work, struct fw_card, work.work);
        struct fw_device *root;
+       struct bm_data bmd;
        unsigned long flags;
-       int new_irm_id, generation;
-
-       /* FIXME: This simple bus management unconditionally picks a
-        * cycle master if the current root can't do it.  We need to
-        * not do this if there is a bus manager already.  Also, some
-        * hubs set the contender bit, which is bogus, so we should
-        * probably do a little sanity check on the IRM (like, read
-        * the bandwidth register) if it's not us. */
+       int root_id, new_root_id, irm_id, gap_count, generation, grace;
+       int do_reset = 0;
 
        spin_lock_irqsave(&card->lock, flags);
 
        generation = card->generation;
        root = card->root_node->data;
+       root_id = card->root_node->node_id;
+       grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10));
+
+       if (card->bm_generation + 1 == generation ||
+           (card->bm_generation != generation && grace)) {
+               /* This first step is to figure out who is IRM and
+                * then try to become bus manager.  If the IRM is not
+                * well defined (e.g. does not have an active link
+                * layer or does not responds to our lock request, we
+                * will have to do a little vigilante bus management.
+                * In that case, we do a goto into the gap count logic
+                * so that when we do the reset, we still optimize the
+                * gap count.  That could well save a reset in the
+                * next generation. */
+
+               irm_id = card->irm_node->node_id;
+               if (!card->irm_node->link_on) {
+                       new_root_id = card->local_node->node_id;
+                       fw_notify("IRM has link off, making local node (%02x) root.\n",
+                                 new_root_id);
+                       goto pick_me;
+               }
+
+               bmd.lock.arg = cpu_to_be32(0x3f);
+               bmd.lock.data = cpu_to_be32(card->local_node->node_id);
+
+               spin_unlock_irqrestore(&card->lock, flags);
+
+               init_completion(&bmd.done);
+               fw_send_request(card, &bmd.t, TCODE_LOCK_COMPARE_SWAP,
+                               irm_id, generation,
+                               SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID,
+                               &bmd.lock, sizeof bmd.lock,
+                               complete_bm_lock, &bmd);
+               wait_for_completion(&bmd.done);
+
+               if (bmd.rcode == RCODE_GENERATION) {
+                       /* Another bus reset happened. Just return,
+                        * the BM work has been rescheduled. */
+                       return;
+               }
+
+               if (bmd.rcode == RCODE_COMPLETE && bmd.old != 0x3f)
+                       /* Somebody else is BM, let them do the work. */
+                       return;
+
+               spin_lock_irqsave(&card->lock, flags);
+               if (bmd.rcode != RCODE_COMPLETE) {
+                       /* The lock request failed, maybe the IRM
+                        * isn't really IRM capable after all. Let's
+                        * do a bus reset and pick the local node as
+                        * root, and thus, IRM. */
+                       new_root_id = card->local_node->node_id;
+                       fw_notify("BM lock failed, making local node (%02x) root.\n",
+                                 new_root_id);
+                       goto pick_me;
+               }
+       } else if (card->bm_generation != generation) {
+               /* OK, we weren't BM in the last generation, and it's
+                * less than 100ms since last bus reset. Reschedule
+                * this task 100ms from now. */
+               spin_unlock_irqrestore(&card->lock, flags);
+               schedule_delayed_work(&card->work, DIV_ROUND_UP(HZ, 10));
+               return;
+       }
 
-       if (root == NULL)
+       /* We're bus manager for this generation, so next step is to
+        * make sure we have an active cycle master and do gap count
+        * optimization. */
+       card->bm_generation = generation;
+
+       if (root == NULL) {
                /* Either link_on is false, or we failed to read the
                 * config rom.  In either case, pick another root. */
-               new_irm_id = card->local_node->node_id;
-       else if (root->state != FW_DEVICE_RUNNING)
+               new_root_id = card->local_node->node_id;
+       } else if (atomic_read(&root->state) != FW_DEVICE_RUNNING) {
                /* If we haven't probed this device yet, bail out now
                 * and let's try again once that's done. */
-               new_irm_id = -1;
-       else if (root->config_rom[2] & bib_cmc)
+               spin_unlock_irqrestore(&card->lock, flags);
+               return;
+       } else if (root->config_rom[2] & bib_cmc) {
                /* FIXME: I suppose we should set the cmstr bit in the
                 * STATE_CLEAR register of this node, as described in
                 * 1394-1995, 8.4.2.6.  Also, send out a force root
                 * packet for this node. */
-               new_irm_id = -1;
-       else
+               new_root_id = root_id;
+       } else {
                /* Current root has an active link layer and we
                 * successfully read the config rom, but it's not
                 * cycle master capable. */
-               new_irm_id = card->local_node->node_id;
+               new_root_id = card->local_node->node_id;
+       }
+
+ pick_me:
+       /* Now figure out what gap count to set. */
+       if (card->topology_type == FW_TOPOLOGY_A &&
+           card->root_node->max_hops < ARRAY_SIZE(gap_count_table))
+               gap_count = gap_count_table[card->root_node->max_hops];
+       else
+               gap_count = 63;
+
+       /* Finally, figure out if we should do a reset or not.  If we've
+        * done less that 5 resets with the same physical topology and we
+        * have either a new root or a new gap count setting, let's do it. */
 
-       if (card->irm_retries++ > 5)
-               new_irm_id = -1;
+       if (card->bm_retries++ < 5 &&
+           (card->gap_count != gap_count || new_root_id != root_id))
+               do_reset = 1;
 
        spin_unlock_irqrestore(&card->lock, flags);
 
-       if (new_irm_id > 0) {
-               fw_notify("Trying to become root (card %d)\n", card->index);
-               fw_send_force_root(card, new_irm_id, generation);
+       if (do_reset) {
+               fw_notify("phy config: card %d, new root=%x, gap_count=%d\n",
+                         card->index, new_root_id, gap_count);
+               fw_send_phy_config(card, new_root_id, generation, gap_count);
                fw_core_initiate_bus_reset(card, 1);
        }
 }
@@ -257,26 +364,26 @@ flush_timer_callback(unsigned long data)
 }
 
 void
-fw_card_initialize(struct fw_card *card, struct fw_card_driver *driver,
+fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
                   struct device *device)
 {
-       static int index;
+       static atomic_t index = ATOMIC_INIT(-1);
 
-       card->index = index++;
-        card->driver = driver;
+       card->index = atomic_inc_return(&index);
+       card->driver = driver;
        card->device = device;
-        card->current_tlabel = 0;
-        card->tlabel_mask = 0;
+       card->current_tlabel = 0;
+       card->tlabel_mask = 0;
        card->color = 0;
 
-        INIT_LIST_HEAD(&card->transaction_list);
+       INIT_LIST_HEAD(&card->transaction_list);
        spin_lock_init(&card->lock);
        setup_timer(&card->flush_timer,
                    flush_timer_callback, (unsigned long)card);
 
        card->local_node = NULL;
 
-       INIT_DELAYED_WORK(&card->work, fw_card_irm_work);
+       INIT_DELAYED_WORK(&card->work, fw_card_bm_work);
 
        card->card_device.bus     = &fw_bus_type;
        card->card_device.release = release_card;
@@ -307,7 +414,7 @@ fw_card_add(struct fw_card *card,
 
        retval = device_add(&card->card_device);
        if (retval < 0) {
-                fw_error("Failed to register card device.");
+               fw_error("Failed to register card device.");
                return retval;
        }
 
@@ -358,13 +465,19 @@ dummy_set_config_rom(struct fw_card *card,
 static void
 dummy_send_request(struct fw_card *card, struct fw_packet *packet)
 {
-        packet->callback(packet, card, -ENODEV);
+       packet->callback(packet, card, -ENODEV);
 }
 
 static void
 dummy_send_response(struct fw_card *card, struct fw_packet *packet)
 {
-        packet->callback(packet, card, -ENODEV);
+       packet->callback(packet, card, -ENODEV);
+}
+
+static int
+dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+{
+       return -ENOENT;
 }
 
 static int
@@ -375,13 +488,14 @@ dummy_enable_phys_dma(struct fw_card *card,
 }
 
 static struct fw_card_driver dummy_driver = {
-        .name            = "dummy",
+       .name            = "dummy",
        .enable          = dummy_enable,
        .update_phy_reg  = dummy_update_phy_reg,
        .set_config_rom  = dummy_set_config_rom,
-        .send_request    = dummy_send_request,
-        .send_response   = dummy_send_response,
-       .enable_phys_dma = dummy_enable_phys_dma
+       .send_request    = dummy_send_request,
+       .cancel_packet   = dummy_cancel_packet,
+       .send_response   = dummy_send_response,
+       .enable_phys_dma = dummy_enable_phys_dma,
 };
 
 void
@@ -428,13 +542,6 @@ EXPORT_SYMBOL(fw_card_put);
 int
 fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
 {
-        u32 address;
-
-        if (short_reset)
-                address = 5;
-        else
-                address = 1;
-
-        return card->driver->update_phy_reg(card, address, 0, 0x40);
+       return card->driver->update_phy_reg(card, short_reset ? 5 : 1, 0, 0x40);
 }
 EXPORT_SYMBOL(fw_core_initiate_bus_reset);