|
@@ -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 |
|
|
@@ -1326,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);
|
|
@@ -1590,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;
|
|
@@ -1904,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);
|
|
@@ -2141,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;
|
|
@@ -2657,6 +2693,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;
|
|
@@ -2715,6 +2755,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;
|
|
|
}
|
|
@@ -3207,15 +3248,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);
|
|
@@ -3266,7 +3309,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)
|
|
@@ -3310,7 +3353,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");
|