firewire: ohci: fix isochronous DMA synchronization

Add the dma_sync_single_* calls necessary to ensure proper cache
synchronization for isochronous data buffers on non-coherent
architectures.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
This commit is contained in:
Clemens Ladisch 2011-10-15 23:12:23 +02:00 committed by Stefan Richter
parent 32eaeae177
commit a572e688cf
1 changed files with 73 additions and 0 deletions

View File

@ -126,6 +126,7 @@ struct context {
struct fw_ohci *ohci; struct fw_ohci *ohci;
u32 regs; u32 regs;
int total_allocation; int total_allocation;
u32 current_bus;
bool running; bool running;
bool flushing; bool flushing;
@ -1057,6 +1058,7 @@ static void context_tasklet(unsigned long data)
address = le32_to_cpu(last->branch_address); address = le32_to_cpu(last->branch_address);
z = address & 0xf; z = address & 0xf;
address &= ~0xf; address &= ~0xf;
ctx->current_bus = address;
/* If the branch address points to a buffer outside of the /* If the branch address points to a buffer outside of the
* current buffer, advance to the next buffer. */ * current buffer, advance to the next buffer. */
@ -2697,6 +2699,7 @@ static int handle_ir_packet_per_buffer(struct context *context,
struct iso_context *ctx = struct iso_context *ctx =
container_of(context, struct iso_context, context); container_of(context, struct iso_context, context);
struct descriptor *pd; struct descriptor *pd;
u32 buffer_dma;
__le32 *ir_header; __le32 *ir_header;
void *p; void *p;
@ -2707,6 +2710,16 @@ static int handle_ir_packet_per_buffer(struct context *context,
/* Descriptor(s) not done yet, stop iteration */ /* Descriptor(s) not done yet, stop iteration */
return 0; return 0;
while (!(d->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))) {
d++;
buffer_dma = le32_to_cpu(d->data_address);
dma_sync_single_range_for_cpu(context->ohci->card.device,
buffer_dma & PAGE_MASK,
buffer_dma & ~PAGE_MASK,
le16_to_cpu(d->req_count),
DMA_FROM_DEVICE);
}
p = last + 1; p = last + 1;
copy_iso_headers(ctx, p); copy_iso_headers(ctx, p);
@ -2729,11 +2742,19 @@ static int handle_ir_buffer_fill(struct context *context,
{ {
struct iso_context *ctx = struct iso_context *ctx =
container_of(context, struct iso_context, context); container_of(context, struct iso_context, context);
u32 buffer_dma;
if (!last->transfer_status) if (!last->transfer_status)
/* Descriptor(s) not done yet, stop iteration */ /* Descriptor(s) not done yet, stop iteration */
return 0; return 0;
buffer_dma = le32_to_cpu(last->data_address);
dma_sync_single_range_for_cpu(context->ohci->card.device,
buffer_dma & PAGE_MASK,
buffer_dma & ~PAGE_MASK,
le16_to_cpu(last->req_count),
DMA_FROM_DEVICE);
if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS)
ctx->base.callback.mc(&ctx->base, ctx->base.callback.mc(&ctx->base,
le32_to_cpu(last->data_address) + le32_to_cpu(last->data_address) +
@ -2744,6 +2765,43 @@ static int handle_ir_buffer_fill(struct context *context,
return 1; return 1;
} }
static inline void sync_it_packet_for_cpu(struct context *context,
struct descriptor *pd)
{
__le16 control;
u32 buffer_dma;
/* only packets beginning with OUTPUT_MORE* have data buffers */
if (pd->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
return;
/* skip over the OUTPUT_MORE_IMMEDIATE descriptor */
pd += 2;
/*
* If the packet has a header, the first OUTPUT_MORE/LAST descriptor's
* data buffer is in the context program's coherent page and must not
* be synced.
*/
if ((le32_to_cpu(pd->data_address) & PAGE_MASK) ==
(context->current_bus & PAGE_MASK)) {
if (pd->control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS))
return;
pd++;
}
do {
buffer_dma = le32_to_cpu(pd->data_address);
dma_sync_single_range_for_cpu(context->ohci->card.device,
buffer_dma & PAGE_MASK,
buffer_dma & ~PAGE_MASK,
le16_to_cpu(pd->req_count),
DMA_TO_DEVICE);
control = pd->control;
pd++;
} while (!(control & cpu_to_le16(DESCRIPTOR_BRANCH_ALWAYS)));
}
static int handle_it_packet(struct context *context, static int handle_it_packet(struct context *context,
struct descriptor *d, struct descriptor *d,
struct descriptor *last) struct descriptor *last)
@ -2760,6 +2818,8 @@ static int handle_it_packet(struct context *context,
/* Descriptor(s) not done yet, stop iteration */ /* Descriptor(s) not done yet, stop iteration */
return 0; return 0;
sync_it_packet_for_cpu(context, d);
i = ctx->header_length; i = ctx->header_length;
if (i + 4 < PAGE_SIZE) { if (i + 4 < PAGE_SIZE) {
/* Present this value as big-endian to match the receive code */ /* Present this value as big-endian to match the receive code */
@ -3129,6 +3189,10 @@ static int queue_iso_transmit(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]); page_bus = page_private(buffer->pages[page]);
pd[i].data_address = cpu_to_le32(page_bus + offset); pd[i].data_address = cpu_to_le32(page_bus + offset);
dma_sync_single_range_for_device(ctx->context.ohci->card.device,
page_bus, offset, length,
DMA_TO_DEVICE);
payload_index += length; payload_index += length;
} }
@ -3153,6 +3217,7 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
struct fw_iso_buffer *buffer, struct fw_iso_buffer *buffer,
unsigned long payload) unsigned long payload)
{ {
struct device *device = ctx->context.ohci->card.device;
struct descriptor *d, *pd; struct descriptor *d, *pd;
dma_addr_t d_bus, page_bus; dma_addr_t d_bus, page_bus;
u32 z, header_z, rest; u32 z, header_z, rest;
@ -3207,6 +3272,10 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]); page_bus = page_private(buffer->pages[page]);
pd->data_address = cpu_to_le32(page_bus + offset); pd->data_address = cpu_to_le32(page_bus + offset);
dma_sync_single_range_for_device(device, page_bus,
offset, length,
DMA_FROM_DEVICE);
offset = (offset + length) & ~PAGE_MASK; offset = (offset + length) & ~PAGE_MASK;
rest -= length; rest -= length;
if (offset == 0) if (offset == 0)
@ -3266,6 +3335,10 @@ static int queue_iso_buffer_fill(struct iso_context *ctx,
page_bus = page_private(buffer->pages[page]); page_bus = page_private(buffer->pages[page]);
d->data_address = cpu_to_le32(page_bus + offset); d->data_address = cpu_to_le32(page_bus + offset);
dma_sync_single_range_for_device(ctx->context.ohci->card.device,
page_bus, offset, length,
DMA_FROM_DEVICE);
rest -= length; rest -= length;
offset = 0; offset = 0;
page++; page++;