diff options
Diffstat (limited to 'drivers/firewire/ohci.c')
| -rw-r--r-- | drivers/firewire/ohci.c | 85 |
1 files changed, 64 insertions, 21 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index bd3c61b6dd8d..f903d7b6f34a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c | |||
| @@ -208,9 +208,11 @@ struct fw_ohci { | |||
| 208 | struct context at_request_ctx; | 208 | struct context at_request_ctx; |
| 209 | struct context at_response_ctx; | 209 | struct context at_response_ctx; |
| 210 | 210 | ||
| 211 | u32 it_context_support; | ||
| 211 | u32 it_context_mask; /* unoccupied IT contexts */ | 212 | u32 it_context_mask; /* unoccupied IT contexts */ |
| 212 | struct iso_context *it_context_list; | 213 | struct iso_context *it_context_list; |
| 213 | u64 ir_context_channels; /* unoccupied channels */ | 214 | u64 ir_context_channels; /* unoccupied channels */ |
| 215 | u32 ir_context_support; | ||
| 214 | u32 ir_context_mask; /* unoccupied IR contexts */ | 216 | u32 ir_context_mask; /* unoccupied IR contexts */ |
| 215 | struct iso_context *ir_context_list; | 217 | struct iso_context *ir_context_list; |
| 216 | u64 mc_channels; /* channels in use by the multichannel IR context */ | 218 | u64 mc_channels; /* channels in use by the multichannel IR context */ |
| @@ -338,7 +340,7 @@ static void log_irqs(u32 evt) | |||
| 338 | !(evt & OHCI1394_busReset)) | 340 | !(evt & OHCI1394_busReset)) |
| 339 | return; | 341 | return; |
| 340 | 342 | ||
| 341 | fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, | 343 | fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, |
| 342 | evt & OHCI1394_selfIDComplete ? " selfID" : "", | 344 | evt & OHCI1394_selfIDComplete ? " selfID" : "", |
| 343 | evt & OHCI1394_RQPkt ? " AR_req" : "", | 345 | evt & OHCI1394_RQPkt ? " AR_req" : "", |
| 344 | evt & OHCI1394_RSPkt ? " AR_resp" : "", | 346 | evt & OHCI1394_RSPkt ? " AR_resp" : "", |
| @@ -351,6 +353,7 @@ static void log_irqs(u32 evt) | |||
| 351 | evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", | 353 | evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", |
| 352 | evt & OHCI1394_cycleInconsistent ? " cycleInconsistent" : "", | 354 | evt & OHCI1394_cycleInconsistent ? " cycleInconsistent" : "", |
| 353 | evt & OHCI1394_regAccessFail ? " regAccessFail" : "", | 355 | evt & OHCI1394_regAccessFail ? " regAccessFail" : "", |
| 356 | evt & OHCI1394_unrecoverableError ? " unrecoverableError" : "", | ||
| 354 | evt & OHCI1394_busReset ? " busReset" : "", | 357 | evt & OHCI1394_busReset ? " busReset" : "", |
| 355 | evt & ~(OHCI1394_selfIDComplete | OHCI1394_RQPkt | | 358 | evt & ~(OHCI1394_selfIDComplete | OHCI1394_RQPkt | |
| 356 | OHCI1394_RSPkt | OHCI1394_reqTxComplete | | 359 | OHCI1394_RSPkt | OHCI1394_reqTxComplete | |
| @@ -1326,21 +1329,8 @@ static int at_context_queue_packet(struct context *ctx, | |||
| 1326 | DESCRIPTOR_IRQ_ALWAYS | | 1329 | DESCRIPTOR_IRQ_ALWAYS | |
| 1327 | DESCRIPTOR_BRANCH_ALWAYS); | 1330 | DESCRIPTOR_BRANCH_ALWAYS); |
| 1328 | 1331 | ||
| 1329 | /* | 1332 | /* FIXME: Document how the locking works. */ |
| 1330 | * If the controller and packet generations don't match, we need to | 1333 | if (ohci->generation != packet->generation) { |
| 1331 | * bail out and try again. If IntEvent.busReset is set, the AT context | ||
| 1332 | * is halted, so appending to the context and trying to run it is | ||
| 1333 | * futile. Most controllers do the right thing and just flush the AT | ||
| 1334 | * queue (per section 7.2.3.2 of the OHCI 1.1 specification), but | ||
| 1335 | * some controllers (like a JMicron JMB381 PCI-e) misbehave and wind | ||
| 1336 | * up stalling out. So we just bail out in software and try again | ||
| 1337 | * later, and everyone is happy. | ||
| 1338 | * FIXME: Test of IntEvent.busReset may no longer be necessary since we | ||
| 1339 | * flush AT queues in bus_reset_tasklet. | ||
| 1340 | * FIXME: Document how the locking works. | ||
| 1341 | */ | ||
| 1342 | if (ohci->generation != packet->generation || | ||
| 1343 | reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) { | ||
| 1344 | if (packet->payload_mapped) | 1334 | if (packet->payload_mapped) |
| 1345 | dma_unmap_single(ohci->card.device, payload_bus, | 1335 | dma_unmap_single(ohci->card.device, payload_bus, |
| 1346 | packet->payload_length, DMA_TO_DEVICE); | 1336 | packet->payload_length, DMA_TO_DEVICE); |
| @@ -1590,6 +1580,47 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet) | |||
| 1590 | 1580 | ||
| 1591 | } | 1581 | } |
| 1592 | 1582 | ||
| 1583 | static void detect_dead_context(struct fw_ohci *ohci, | ||
| 1584 | const char *name, unsigned int regs) | ||
| 1585 | { | ||
| 1586 | u32 ctl; | ||
| 1587 | |||
| 1588 | ctl = reg_read(ohci, CONTROL_SET(regs)); | ||
| 1589 | if (ctl & CONTEXT_DEAD) { | ||
| 1590 | #ifdef CONFIG_FIREWIRE_OHCI_DEBUG | ||
| 1591 | fw_error("DMA context %s has stopped, error code: %s\n", | ||
| 1592 | name, evts[ctl & 0x1f]); | ||
| 1593 | #else | ||
| 1594 | fw_error("DMA context %s has stopped, error code: %#x\n", | ||
| 1595 | name, ctl & 0x1f); | ||
| 1596 | #endif | ||
| 1597 | } | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | static void handle_dead_contexts(struct fw_ohci *ohci) | ||
| 1601 | { | ||
| 1602 | unsigned int i; | ||
| 1603 | char name[8]; | ||
| 1604 | |||
| 1605 | detect_dead_context(ohci, "ATReq", OHCI1394_AsReqTrContextBase); | ||
| 1606 | detect_dead_context(ohci, "ATRsp", OHCI1394_AsRspTrContextBase); | ||
| 1607 | detect_dead_context(ohci, "ARReq", OHCI1394_AsReqRcvContextBase); | ||
| 1608 | detect_dead_context(ohci, "ARRsp", OHCI1394_AsRspRcvContextBase); | ||
| 1609 | for (i = 0; i < 32; ++i) { | ||
| 1610 | if (!(ohci->it_context_support & (1 << i))) | ||
| 1611 | continue; | ||
| 1612 | sprintf(name, "IT%u", i); | ||
| 1613 | detect_dead_context(ohci, name, OHCI1394_IsoXmitContextBase(i)); | ||
| 1614 | } | ||
| 1615 | for (i = 0; i < 32; ++i) { | ||
| 1616 | if (!(ohci->ir_context_support & (1 << i))) | ||
| 1617 | continue; | ||
| 1618 | sprintf(name, "IR%u", i); | ||
| 1619 | detect_dead_context(ohci, name, OHCI1394_IsoRcvContextBase(i)); | ||
| 1620 | } | ||
| 1621 | /* TODO: maybe try to flush and restart the dead contexts */ | ||
| 1622 | } | ||
| 1623 | |||
| 1593 | static u32 cycle_timer_ticks(u32 cycle_timer) | 1624 | static u32 cycle_timer_ticks(u32 cycle_timer) |
| 1594 | { | 1625 | { |
| 1595 | u32 ticks; | 1626 | u32 ticks; |
| @@ -1904,6 +1935,9 @@ static irqreturn_t irq_handler(int irq, void *data) | |||
| 1904 | fw_notify("isochronous cycle inconsistent\n"); | 1935 | fw_notify("isochronous cycle inconsistent\n"); |
| 1905 | } | 1936 | } |
| 1906 | 1937 | ||
| 1938 | if (unlikely(event & OHCI1394_unrecoverableError)) | ||
| 1939 | handle_dead_contexts(ohci); | ||
| 1940 | |||
| 1907 | if (event & OHCI1394_cycle64Seconds) { | 1941 | if (event & OHCI1394_cycle64Seconds) { |
| 1908 | spin_lock(&ohci->lock); | 1942 | spin_lock(&ohci->lock); |
| 1909 | update_bus_time(ohci); | 1943 | update_bus_time(ohci); |
| @@ -2141,7 +2175,9 @@ static int ohci_enable(struct fw_card *card, | |||
| 2141 | OHCI1394_selfIDComplete | | 2175 | OHCI1394_selfIDComplete | |
| 2142 | OHCI1394_regAccessFail | | 2176 | OHCI1394_regAccessFail | |
| 2143 | OHCI1394_cycle64Seconds | | 2177 | OHCI1394_cycle64Seconds | |
| 2144 | OHCI1394_cycleInconsistent | OHCI1394_cycleTooLong | | 2178 | OHCI1394_cycleInconsistent | |
| 2179 | OHCI1394_unrecoverableError | | ||
| 2180 | OHCI1394_cycleTooLong | | ||
| 2145 | OHCI1394_masterIntEnable; | 2181 | OHCI1394_masterIntEnable; |
| 2146 | if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) | 2182 | if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) |
| 2147 | irqs |= OHCI1394_busReset; | 2183 | irqs |= OHCI1394_busReset; |
| @@ -2657,6 +2693,10 @@ static int ohci_start_iso(struct fw_iso_context *base, | |||
| 2657 | u32 control = IR_CONTEXT_ISOCH_HEADER, match; | 2693 | u32 control = IR_CONTEXT_ISOCH_HEADER, match; |
| 2658 | int index; | 2694 | int index; |
| 2659 | 2695 | ||
| 2696 | /* the controller cannot start without any queued packets */ | ||
| 2697 | if (ctx->context.last->branch_address == 0) | ||
| 2698 | return -ENODATA; | ||
| 2699 | |||
| 2660 | switch (ctx->base.type) { | 2700 | switch (ctx->base.type) { |
| 2661 | case FW_ISO_CONTEXT_TRANSMIT: | 2701 | case FW_ISO_CONTEXT_TRANSMIT: |
| 2662 | index = ctx - ohci->it_context_list; | 2702 | index = ctx - ohci->it_context_list; |
| @@ -2715,6 +2755,7 @@ static int ohci_stop_iso(struct fw_iso_context *base) | |||
| 2715 | } | 2755 | } |
| 2716 | flush_writes(ohci); | 2756 | flush_writes(ohci); |
| 2717 | context_stop(&ctx->context); | 2757 | context_stop(&ctx->context); |
| 2758 | tasklet_kill(&ctx->context.tasklet); | ||
| 2718 | 2759 | ||
| 2719 | return 0; | 2760 | return 0; |
| 2720 | } | 2761 | } |
| @@ -3207,15 +3248,17 @@ static int __devinit pci_probe(struct pci_dev *dev, | |||
| 3207 | 3248 | ||
| 3208 | reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); | 3249 | reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); |
| 3209 | ohci->ir_context_channels = ~0ULL; | 3250 | ohci->ir_context_channels = ~0ULL; |
| 3210 | ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); | 3251 | ohci->ir_context_support = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); |
| 3211 | reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0); | 3252 | reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0); |
| 3253 | ohci->ir_context_mask = ohci->ir_context_support; | ||
| 3212 | ohci->n_ir = hweight32(ohci->ir_context_mask); | 3254 | ohci->n_ir = hweight32(ohci->ir_context_mask); |
| 3213 | size = sizeof(struct iso_context) * ohci->n_ir; | 3255 | size = sizeof(struct iso_context) * ohci->n_ir; |
| 3214 | ohci->ir_context_list = kzalloc(size, GFP_KERNEL); | 3256 | ohci->ir_context_list = kzalloc(size, GFP_KERNEL); |
| 3215 | 3257 | ||
| 3216 | reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0); | 3258 | reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0); |
| 3217 | ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet); | 3259 | ohci->it_context_support = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet); |
| 3218 | reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0); | 3260 | reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0); |
| 3261 | ohci->it_context_mask = ohci->it_context_support; | ||
| 3219 | ohci->n_it = hweight32(ohci->it_context_mask); | 3262 | ohci->n_it = hweight32(ohci->it_context_mask); |
| 3220 | size = sizeof(struct iso_context) * ohci->n_it; | 3263 | size = sizeof(struct iso_context) * ohci->n_it; |
| 3221 | ohci->it_context_list = kzalloc(size, GFP_KERNEL); | 3264 | ohci->it_context_list = kzalloc(size, GFP_KERNEL); |
| @@ -3266,7 +3309,7 @@ static int __devinit pci_probe(struct pci_dev *dev, | |||
| 3266 | fail_disable: | 3309 | fail_disable: |
| 3267 | pci_disable_device(dev); | 3310 | pci_disable_device(dev); |
| 3268 | fail_free: | 3311 | fail_free: |
| 3269 | kfree(&ohci->card); | 3312 | kfree(ohci); |
| 3270 | pmac_ohci_off(dev); | 3313 | pmac_ohci_off(dev); |
| 3271 | fail: | 3314 | fail: |
| 3272 | if (err == -ENOMEM) | 3315 | if (err == -ENOMEM) |
| @@ -3310,7 +3353,7 @@ static void pci_remove(struct pci_dev *dev) | |||
| 3310 | pci_iounmap(dev, ohci->registers); | 3353 | pci_iounmap(dev, ohci->registers); |
| 3311 | pci_release_region(dev, 0); | 3354 | pci_release_region(dev, 0); |
| 3312 | pci_disable_device(dev); | 3355 | pci_disable_device(dev); |
| 3313 | kfree(&ohci->card); | 3356 | kfree(ohci); |
| 3314 | pmac_ohci_off(dev); | 3357 | pmac_ohci_off(dev); |
| 3315 | 3358 | ||
| 3316 | fw_notify("Removed fw-ohci device.\n"); | 3359 | fw_notify("Removed fw-ohci device.\n"); |
