firewire: Fix for broken configrom updates in quick succession
[pandora-kernel.git] / drivers / firewire / ohci.c
index cfe5afe..23d1468 100644 (file)
@@ -125,6 +125,7 @@ struct context {
        struct fw_ohci *ohci;
        u32 regs;
        int total_allocation;
+       bool running;
        bool flushing;
 
        /*
@@ -155,7 +156,6 @@ struct context {
        descriptor_callback_t callback;
 
        struct tasklet_struct tasklet;
-       bool active;
 };
 
 #define IT_HEADER_SY(v)          ((v) <<  0)
@@ -208,9 +208,11 @@ struct fw_ohci {
        struct context at_request_ctx;
        struct context at_response_ctx;
 
+       u32 it_context_support;
        u32 it_context_mask;     /* unoccupied IT contexts */
        struct iso_context *it_context_list;
        u64 ir_context_channels; /* unoccupied channels */
+       u32 ir_context_support;
        u32 ir_context_mask;     /* unoccupied IR contexts */
        struct iso_context *ir_context_list;
        u64 mc_channels; /* channels in use by the multichannel IR context */
@@ -338,7 +340,7 @@ static void log_irqs(u32 evt)
            !(evt & OHCI1394_busReset))
                return;
 
-       fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt,
+       fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt,
            evt & OHCI1394_selfIDComplete       ? " selfID"             : "",
            evt & OHCI1394_RQPkt                ? " AR_req"             : "",
            evt & OHCI1394_RSPkt                ? " AR_resp"            : "",
@@ -351,6 +353,7 @@ static void log_irqs(u32 evt)
            evt & OHCI1394_cycle64Seconds       ? " cycle64Seconds"     : "",
            evt & OHCI1394_cycleInconsistent    ? " cycleInconsistent"  : "",
            evt & OHCI1394_regAccessFail        ? " regAccessFail"      : "",
+           evt & OHCI1394_unrecoverableError   ? " unrecoverableError" : "",
            evt & OHCI1394_busReset             ? " busReset"           : "",
            evt & ~(OHCI1394_selfIDComplete | OHCI1394_RQPkt |
                    OHCI1394_RSPkt | OHCI1394_reqTxComplete |
@@ -961,7 +964,7 @@ static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci,
        for (i = 0; i < AR_WRAPAROUND_PAGES; i++)
                pages[AR_BUFFERS + i] = ctx->pages[i];
        ctx->buffer = vm_map_ram(pages, AR_BUFFERS + AR_WRAPAROUND_PAGES,
-                                -1, PAGE_KERNEL_RO);
+                                -1, PAGE_KERNEL);
        if (!ctx->buffer)
                goto out_of_memory;
 
@@ -1168,12 +1171,12 @@ static struct descriptor *context_get_descriptors(struct context *ctx,
 static void context_run(struct context *ctx, u32 extra)
 {
        struct fw_ohci *ohci = ctx->ohci;
-       ctx->active = true;
 
        reg_write(ohci, COMMAND_PTR(ctx->regs),
                  le32_to_cpu(ctx->last->branch_address));
        reg_write(ohci, CONTROL_CLEAR(ctx->regs), ~0);
        reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN | extra);
+       ctx->running = true;
        flush_writes(ohci);
 }
 
@@ -1200,8 +1203,8 @@ static void context_stop(struct context *ctx)
        u32 reg;
        int i;
 
-       ctx->active = false;
        reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
+       ctx->running = false;
        flush_writes(ctx->ohci);
 
        for (i = 0; i < 10; i++) {
@@ -1232,7 +1235,6 @@ static int at_context_queue_packet(struct context *ctx,
        struct descriptor *d, *last;
        __le32 *header;
        int z, tcode;
-       u32 reg;
 
        d = context_get_descriptors(ctx, 4, &d_bus);
        if (d == NULL) {
@@ -1327,21 +1329,8 @@ static int at_context_queue_packet(struct context *ctx,
                                     DESCRIPTOR_IRQ_ALWAYS |
                                     DESCRIPTOR_BRANCH_ALWAYS);
 
-       /*
-        * If the controller and packet generations don't match, we need to
-        * bail out and try again.  If IntEvent.busReset is set, the AT context
-        * is halted, so appending to the context and trying to run it is
-        * futile.  Most controllers do the right thing and just flush the AT
-        * queue (per section 7.2.3.2 of the OHCI 1.1 specification), but
-        * some controllers (like a JMicron JMB381 PCI-e) misbehave and wind
-        * up stalling out.  So we just bail out in software and try again
-        * later, and everyone is happy.
-        * FIXME: Test of IntEvent.busReset may no longer be necessary since we
-        *        flush AT queues in bus_reset_tasklet.
-        * FIXME: Document how the locking works.
-        */
-       if (ohci->generation != packet->generation ||
-           reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+       /* FIXME: Document how the locking works. */
+       if (ohci->generation != packet->generation) {
                if (packet->payload_mapped)
                        dma_unmap_single(ohci->card.device, payload_bus,
                                         packet->payload_length, DMA_TO_DEVICE);
@@ -1351,9 +1340,7 @@ static int at_context_queue_packet(struct context *ctx,
 
        context_append(ctx, d, z, 4 - z);
 
-       /* If the context isn't already running, start it up. */
-       reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs));
-       if ((reg & CONTEXT_RUN) == 0)
+       if (!ctx->running)
                context_run(ctx, 0);
 
        return 0;
@@ -1593,6 +1580,47 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
 
 }
 
+static void detect_dead_context(struct fw_ohci *ohci,
+                               const char *name, unsigned int regs)
+{
+       u32 ctl;
+
+       ctl = reg_read(ohci, CONTROL_SET(regs));
+       if (ctl & CONTEXT_DEAD) {
+#ifdef CONFIG_FIREWIRE_OHCI_DEBUG
+               fw_error("DMA context %s has stopped, error code: %s\n",
+                        name, evts[ctl & 0x1f]);
+#else
+               fw_error("DMA context %s has stopped, error code: %#x\n",
+                        name, ctl & 0x1f);
+#endif
+       }
+}
+
+static void handle_dead_contexts(struct fw_ohci *ohci)
+{
+       unsigned int i;
+       char name[8];
+
+       detect_dead_context(ohci, "ATReq", OHCI1394_AsReqTrContextBase);
+       detect_dead_context(ohci, "ATRsp", OHCI1394_AsRspTrContextBase);
+       detect_dead_context(ohci, "ARReq", OHCI1394_AsReqRcvContextBase);
+       detect_dead_context(ohci, "ARRsp", OHCI1394_AsRspRcvContextBase);
+       for (i = 0; i < 32; ++i) {
+               if (!(ohci->it_context_support & (1 << i)))
+                       continue;
+               sprintf(name, "IT%u", i);
+               detect_dead_context(ohci, name, OHCI1394_IsoXmitContextBase(i));
+       }
+       for (i = 0; i < 32; ++i) {
+               if (!(ohci->ir_context_support & (1 << i)))
+                       continue;
+               sprintf(name, "IR%u", i);
+               detect_dead_context(ohci, name, OHCI1394_IsoRcvContextBase(i));
+       }
+       /* TODO: maybe try to flush and restart the dead contexts */
+}
+
 static u32 cycle_timer_ticks(u32 cycle_timer)
 {
        u32 ticks;
@@ -1907,6 +1935,9 @@ static irqreturn_t irq_handler(int irq, void *data)
                        fw_notify("isochronous cycle inconsistent\n");
        }
 
+       if (unlikely(event & OHCI1394_unrecoverableError))
+               handle_dead_contexts(ohci);
+
        if (event & OHCI1394_cycle64Seconds) {
                spin_lock(&ohci->lock);
                update_bus_time(ohci);
@@ -2144,7 +2175,9 @@ static int ohci_enable(struct fw_card *card,
                OHCI1394_selfIDComplete |
                OHCI1394_regAccessFail |
                OHCI1394_cycle64Seconds |
-               OHCI1394_cycleInconsistent | OHCI1394_cycleTooLong |
+               OHCI1394_cycleInconsistent |
+               OHCI1394_unrecoverableError |
+               OHCI1394_cycleTooLong |
                OHCI1394_masterIntEnable;
        if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS)
                irqs |= OHCI1394_busReset;
@@ -2166,7 +2199,6 @@ static int ohci_set_config_rom(struct fw_card *card,
 {
        struct fw_ohci *ohci;
        unsigned long flags;
-       int ret = -EBUSY;
        __be32 *next_config_rom;
        dma_addr_t uninitialized_var(next_config_rom_bus);
 
@@ -2207,22 +2239,37 @@ static int ohci_set_config_rom(struct fw_card *card,
 
        spin_lock_irqsave(&ohci->lock, flags);
 
+       /*
+        * If there is not an already pending config_rom update,
+        * push our new allocation into the ohci->next_config_rom
+        * and then mark the local variable as null so that we
+        * won't deallocate the new buffer.
+        *
+        * OTOH, if there is a pending config_rom update, just
+        * use that buffer with the new config_rom data, and
+        * let this routine free the unused DMA allocation.
+        */
+
        if (ohci->next_config_rom == NULL) {
                ohci->next_config_rom = next_config_rom;
                ohci->next_config_rom_bus = next_config_rom_bus;
+               next_config_rom = NULL;
+       }
 
-               copy_config_rom(ohci->next_config_rom, config_rom, length);
+       copy_config_rom(ohci->next_config_rom, config_rom, length);
 
-               ohci->next_header = config_rom[0];
-               ohci->next_config_rom[0] = 0;
+       ohci->next_header = config_rom[0];
+       ohci->next_config_rom[0] = 0;
 
-               reg_write(ohci, OHCI1394_ConfigROMmap,
-                         ohci->next_config_rom_bus);
-               ret = 0;
-       }
+       reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus);
 
        spin_unlock_irqrestore(&ohci->lock, flags);
 
+       /* If we didn't use the DMA allocation, delete it. */
+       if (next_config_rom != NULL)
+               dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+                                 next_config_rom, next_config_rom_bus);
+
        /*
         * Now initiate a bus reset to have the changes take
         * effect. We clean up the old config rom memory and DMA
@@ -2230,13 +2277,10 @@ static int ohci_set_config_rom(struct fw_card *card,
         * controller could need to access it before the bus reset
         * takes effect.
         */
-       if (ret == 0)
-               fw_schedule_bus_reset(&ohci->card, true, true);
-       else
-               dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
-                                 next_config_rom, next_config_rom_bus);
 
-       return ret;
+       fw_schedule_bus_reset(&ohci->card, true, true);
+
+       return 0;
 }
 
 static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
@@ -2660,6 +2704,10 @@ static int ohci_start_iso(struct fw_iso_context *base,
        u32 control = IR_CONTEXT_ISOCH_HEADER, match;
        int index;
 
+       /* the controller cannot start without any queued packets */
+       if (ctx->context.last->branch_address == 0)
+               return -ENODATA;
+
        switch (ctx->base.type) {
        case FW_ISO_CONTEXT_TRANSMIT:
                index = ctx - ohci->it_context_list;
@@ -2718,6 +2766,7 @@ static int ohci_stop_iso(struct fw_iso_context *base)
        }
        flush_writes(ohci);
        context_stop(&ctx->context);
+       tasklet_kill(&ctx->context.tasklet);
 
        return 0;
 }
@@ -2797,13 +2846,13 @@ static void ohci_resume_iso_dma(struct fw_ohci *ohci)
 
        for (i = 0 ; i < ohci->n_ir ; i++) {
                ctx = &ohci->ir_context_list[i];
-               if (ctx->context.active)
+               if (ctx->context.running)
                        ohci_start_iso(&ctx->base, 0, ctx->sync, ctx->tags);
        }
 
        for (i = 0 ; i < ohci->n_it ; i++) {
                ctx = &ohci->it_context_list[i];
-               if (ctx->context.active)
+               if (ctx->context.running)
                        ohci_start_iso(&ctx->base, 0, ctx->sync, ctx->tags);
        }
 }
@@ -3210,15 +3259,17 @@ static int __devinit pci_probe(struct pci_dev *dev,
 
        reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0);
        ohci->ir_context_channels = ~0ULL;
-       ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet);
+       ohci->ir_context_support = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet);
        reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0);
+       ohci->ir_context_mask = ohci->ir_context_support;
        ohci->n_ir = hweight32(ohci->ir_context_mask);
        size = sizeof(struct iso_context) * ohci->n_ir;
        ohci->ir_context_list = kzalloc(size, GFP_KERNEL);
 
        reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
-       ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
+       ohci->it_context_support = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
        reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
+       ohci->it_context_mask = ohci->it_context_support;
        ohci->n_it = hweight32(ohci->it_context_mask);
        size = sizeof(struct iso_context) * ohci->n_it;
        ohci->it_context_list = kzalloc(size, GFP_KERNEL);
@@ -3269,7 +3320,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
  fail_disable:
        pci_disable_device(dev);
  fail_free:
-       kfree(&ohci->card);
+       kfree(ohci);
        pmac_ohci_off(dev);
  fail:
        if (err == -ENOMEM)
@@ -3313,7 +3364,7 @@ static void pci_remove(struct pci_dev *dev)
        pci_iounmap(dev, ohci->registers);
        pci_release_region(dev, 0);
        pci_disable_device(dev);
-       kfree(&ohci->card);
+       kfree(ohci);
        pmac_ohci_off(dev);
 
        fw_notify("Removed fw-ohci device.\n");
@@ -3363,11 +3414,11 @@ static int pci_resume(struct pci_dev *dev)
        }
 
        err = ohci_enable(&ohci->card, NULL, 0);
-
        if (err)
                return err;
 
        ohci_resume_iso_dma(ohci);
+
        return 0;
 }
 #endif