]> git.karo-electronics.de Git - linux-beck.git/commitdiff
firewire: ohci: flush AT contexts after bus reset for OHCI 1.2
authorClemens Ladisch <clemens@ladisch.de>
Fri, 24 Dec 2010 13:40:15 +0000 (14:40 +0100)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Mon, 3 Jan 2011 21:34:48 +0000 (22:34 +0100)
The OHCI 1.2 (draft) specification, clause 7.2.3.3, allows and
recommends that, after a bus reset, the controller does not flush all
the packets in the AT queues.  Therefore, the driver has to do this
itself.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
drivers/firewire/ohci.c

index 5e5d5d3d43d19bd3163a0ce0e2bba3439815be15..3372cd187c258d766033b3feb9d672693c15cdec 100644 (file)
@@ -125,6 +125,7 @@ struct context {
        struct fw_ohci *ohci;
        u32 regs;
        int total_allocation;
+       bool flushing;
 
        /*
         * List of page-sized buffers for storing DMA descriptors.
@@ -1356,6 +1357,17 @@ static int at_context_queue_packet(struct context *ctx,
        return 0;
 }
 
+static void at_context_flush(struct context *ctx)
+{
+       tasklet_disable(&ctx->tasklet);
+
+       ctx->flushing = true;
+       context_tasklet((unsigned long)ctx);
+       ctx->flushing = false;
+
+       tasklet_enable(&ctx->tasklet);
+}
+
 static int handle_at_packet(struct context *context,
                            struct descriptor *d,
                            struct descriptor *last)
@@ -1365,7 +1377,7 @@ static int handle_at_packet(struct context *context,
        struct fw_ohci *ohci = context->ohci;
        int evt;
 
-       if (last->transfer_status == 0)
+       if (last->transfer_status == 0 && !context->flushing)
                /* This descriptor isn't done yet, stop iteration. */
                return 0;
 
@@ -1399,11 +1411,15 @@ static int handle_at_packet(struct context *context,
                break;
 
        case OHCI1394_evt_missing_ack:
-               /*
-                * Using a valid (current) generation count, but the
-                * node is not on the bus or not sending acks.
-                */
-               packet->ack = RCODE_NO_ACK;
+               if (context->flushing)
+                       packet->ack = RCODE_GENERATION;
+               else {
+                       /*
+                        * Using a valid (current) generation count, but the
+                        * node is not on the bus or not sending acks.
+                        */
+                       packet->ack = RCODE_NO_ACK;
+               }
                break;
 
        case ACK_COMPLETE + 0x10:
@@ -1416,6 +1432,13 @@ static int handle_at_packet(struct context *context,
                packet->ack = evt - 0x10;
                break;
 
+       case OHCI1394_evt_no_status:
+               if (context->flushing) {
+                       packet->ack = RCODE_GENERATION;
+                       break;
+               }
+               /* fall through */
+
        default:
                packet->ack = RCODE_SEND_ERROR;
                break;
@@ -1721,9 +1744,18 @@ static void bus_reset_tasklet(unsigned long data)
        /* FIXME: Document how the locking works. */
        spin_lock_irqsave(&ohci->lock, flags);
 
-       ohci->generation = generation;
+       ohci->generation = -1; /* prevent AT packet queueing */
        context_stop(&ohci->at_request_ctx);
        context_stop(&ohci->at_response_ctx);
+
+       spin_unlock_irqrestore(&ohci->lock, flags);
+
+       at_context_flush(&ohci->at_request_ctx);
+       at_context_flush(&ohci->at_response_ctx);
+
+       spin_lock_irqsave(&ohci->lock, flags);
+
+       ohci->generation = generation;
        reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
 
        if (ohci->quirks & QUIRK_RESET_PACKET)