diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2010-12-24 14:40:15 +0100 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-01-03 22:34:48 +0100 |
commit | 82b662dc41027527675740de15344d1b4e34958e (patch) | |
tree | e9e38e035e426c40b5f52f48da7928872fe61f5c | |
parent | c16714704bb35165e5b85d927873dcc643772648 (diff) | |
download | blackbird-op-linux-82b662dc41027527675740de15344d1b4e34958e.tar.gz blackbird-op-linux-82b662dc41027527675740de15344d1b4e34958e.zip |
firewire: ohci: flush AT contexts after bus reset for OHCI 1.2
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>
-rw-r--r-- | drivers/firewire/ohci.c | 46 |
1 files changed, 39 insertions, 7 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 5e5d5d3d43d1..3372cd187c25 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -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) |