summary refs log tree commit diff
path: root/drivers/firewire
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-12-24 14:40:15 +0100
committerStefan Richter <stefanr@s5r6.in-berlin.de>2011-01-03 22:34:48 +0100
commit82b662dc41027527675740de15344d1b4e34958e (patch)
treee9e38e035e426c40b5f52f48da7928872fe61f5c /drivers/firewire
parentc16714704bb35165e5b85d927873dcc643772648 (diff)
downloadlinux-82b662dc41027527675740de15344d1b4e34958e.tar.gz
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>
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/ohci.c46
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)